blob: 5cbed53e7085f227d90679e1b56ad803d9b74a47 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
#include "authenticator_manager.hpp"
#include <string>
#include <process/authenticator.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/http.hpp>
#include <process/id.hpp>
#include <process/owned.hpp>
#include <process/process.hpp>
#include <stout/hashmap.hpp>
#include <stout/nothing.hpp>
#include <stout/path.hpp>
using std::string;
namespace process {
namespace http {
namespace authentication {
class AuthenticatorManagerProcess : public Process<AuthenticatorManagerProcess>
{
public:
AuthenticatorManagerProcess();
Future<Nothing> setAuthenticator(
const string& realm,
Owned<Authenticator> authenticator);
Future<Nothing> unsetAuthenticator(
const string& realm);
Future<Option<AuthenticationResult>> authenticate(
const Request& request,
const string& realm);
private:
hashmap<string, Owned<Authenticator>> authenticators_;
};
AuthenticatorManagerProcess::AuthenticatorManagerProcess()
: ProcessBase(ID::generate("__authentication_router__")) {}
Future<Nothing> AuthenticatorManagerProcess::setAuthenticator(
const string& realm,
Owned<Authenticator> authenticator)
{
CHECK_NOTNULL(authenticator.get());
authenticators_[realm] = authenticator;
return Nothing();
}
Future<Nothing> AuthenticatorManagerProcess::unsetAuthenticator(
const string& realm)
{
authenticators_.erase(realm);
return Nothing();
}
Future<Option<AuthenticationResult>> AuthenticatorManagerProcess::authenticate(
const Request& request,
const string& realm)
{
if (!authenticators_.contains(realm)) {
VLOG(2) << "Request for '" << request.url.path << "' requires"
<< " authentication in realm '" << realm << "'"
<< " but no authenticator found";
return None();
}
return authenticators_[realm]->authenticate(request)
.then([](const AuthenticationResult& authentication)
-> Future<Option<AuthenticationResult>> {
// Validate that exactly 1 member is set!
size_t count =
(authentication.principal.isSome() ? 1 : 0) +
(authentication.unauthorized.isSome() ? 1 : 0) +
(authentication.forbidden.isSome() ? 1 : 0);
if (count != 1) {
return Failure(
"HTTP authenticators must return only one of an authenticated "
"principal, an Unauthorized response, or a Forbidden response");
}
if (authentication.principal.isSome()) {
// Validate that at least one of `value` and `claims` is set.
if (authentication.principal->value.isNone() &&
authentication.principal->claims.empty()) {
return Failure(
"In the principal returned by an HTTP authenticator, at least one"
" of 'value' and 'claims' must be set");
}
}
return authentication;
});
}
AuthenticatorManager::AuthenticatorManager()
: process(new AuthenticatorManagerProcess())
{
spawn(process.get());
}
AuthenticatorManager::~AuthenticatorManager()
{
terminate(process.get());
wait(process.get());
}
Future<Nothing> AuthenticatorManager::setAuthenticator(
const string& realm,
Owned<Authenticator> authenticator)
{
return dispatch(
process.get(),
&AuthenticatorManagerProcess::setAuthenticator,
realm,
authenticator);
}
Future<Nothing> AuthenticatorManager::unsetAuthenticator(
const string& realm)
{
return dispatch(
process.get(),
&AuthenticatorManagerProcess::unsetAuthenticator,
realm);
}
Future<Option<AuthenticationResult>> AuthenticatorManager::authenticate(
const Request& request,
const string& realm)
{
return dispatch(
process.get(),
&AuthenticatorManagerProcess::authenticate,
request,
realm);
}
} // namespace authentication {
} // namespace http {
} // namespace process {