Fix infinite redirect when webui isn't installed
In the begining, bmcweb had its own webui checked in as source. Largely
conceived of clay, and built by someone that doesn't understand UI
development (me), it was eventually superceeded by phosphor-webui. When
we did that, we created a bug where bmcweb was expecting a UI to always
be installed, and when it wasn't resolved into an infinite recursive
redirect as it tried to find the login page.
This patchset fixes that, by adding a connection between the
authorization class, and the webassets class, for bmcweb to detect at
runtime whether or not the UI is installed, and change behavior in that
case.
Along the way, we got a circular #include, so some includes needed to be
rearranged slightly.
This patchset will change no behavior when the UI is installed. Login
failures will continue to redirect to /, to hit the login page.
If the UI is not installed, and there is no / route, BMCWEB will return
the plaintext UNAUTHORIZED if you attempt to open the webui from the
browser without having a webui installed and without having credentials.
Tested:
Launched in a build without webui-vue, and observed "UNAUTHORIZED" when
I connected through chrome.
Also launched in a build with webui-vue installed with:
IMAGE_INSTALL_append = "webui-vue"
And loaded the webui in chrome, and logged in successfully.
Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Iac9b83ba9e80d434479685b082d547847cdfe309
diff --git a/http/http_response.hpp b/http/http_response.hpp
index cd00ec8..7965704 100644
--- a/http/http_response.hpp
+++ b/http/http_response.hpp
@@ -1,9 +1,9 @@
#pragma once
-#include "http_request.hpp"
#include "logging.hpp"
#include "nlohmann/json.hpp"
#include <boost/beast/http/message.hpp>
+#include <boost/beast/http/string_body.hpp>
#include <string>
diff --git a/include/authorization.hpp b/include/authorization.hpp
index c078ede..b1d1097 100644
--- a/include/authorization.hpp
+++ b/include/authorization.hpp
@@ -6,6 +6,7 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/container/flat_set.hpp>
#include <common.hpp>
+#include <forward_unauthorized.hpp>
#include <http_request.hpp>
#include <http_response.hpp>
#include <http_utility.hpp>
@@ -299,26 +300,7 @@
if (req.session == nullptr)
{
BMCWEB_LOG_WARNING << "[AuthMiddleware] authorization failed";
-
- // If it's a browser connecting, don't send the HTTP authenticate
- // header, to avoid possible CSRF attacks with basic auth
- if (http_helpers::requestPrefersHtml(req))
- {
- res.result(boost::beast::http::status::temporary_redirect);
- res.addHeader("Location",
- "/#/login?next=" + http_helpers::urlEncode(req.url));
- }
- else
- {
- res.result(boost::beast::http::status::unauthorized);
- // only send the WWW-authenticate header if this isn't a xhr
- // from the browser. most scripts,
- if (req.getHeaderValue("User-Agent").empty())
- {
- res.addHeader("WWW-Authenticate", "Basic");
- }
- }
-
+ forward_unauthorized::sendUnauthorized(req, res);
res.end();
return;
}
diff --git a/include/forward_unauthorized.hpp b/include/forward_unauthorized.hpp
new file mode 100644
index 0000000..02e1123
--- /dev/null
+++ b/include/forward_unauthorized.hpp
@@ -0,0 +1,44 @@
+#pragma once
+#include <http_request.hpp>
+#include <http_response.hpp>
+#include <http_utility.hpp>
+
+namespace forward_unauthorized
+{
+
+bool hasWebuiRoute = false;
+
+inline void sendUnauthorized(const crow::Request& req, crow::Response& res)
+{
+ // If it's a browser connecting, don't send the HTTP authenticate
+ // header, to avoid possible CSRF attacks with basic auth
+ if (http_helpers::requestPrefersHtml(req))
+ {
+ // If we have a webui installed, redirect to that login page
+ if (hasWebuiRoute)
+ {
+ res.result(boost::beast::http::status::temporary_redirect);
+ res.addHeader("Location",
+ "/#/login?next=" + http_helpers::urlEncode(req.url));
+ }
+ else
+ {
+ // If we don't have a webui installed, just return a lame
+ // unauthorized body
+ res.result(boost::beast::http::status::unauthorized);
+ res.body() = "Unauthorized";
+ }
+ }
+ else
+ {
+ res.result(boost::beast::http::status::unauthorized);
+ // only send the WWW-authenticate header if this isn't a xhr
+ // from the browser. Most scripts, tend to not set a user-agent header.
+ // So key off that to know whether or not we need to suggest basic auth
+ if (req.getHeaderValue("User-Agent").empty())
+ {
+ res.addHeader("WWW-Authenticate", "Basic");
+ }
+ }
+}
+} // namespace forward_unauthorized
diff --git a/include/webassets.hpp b/include/webassets.hpp
index 070442e..f92214f 100644
--- a/include/webassets.hpp
+++ b/include/webassets.hpp
@@ -142,6 +142,11 @@
<< extension;
}
+ if (webpath == "/")
+ {
+ forward_unauthorized::hasWebuiRoute = true;
+ }
+
app.routeDynamic(webpath)(
[absolutePath, contentType, contentEncoding](
const crow::Request&,