Repository: mesos Updated Branches: refs/heads/master 5ac6e1563 -> 072638d25
Implemented the 'Principal' type in libprocess. This patch adds a new struct, `Principal`, to libprocess to represent an authenticated entity in the system. The new type contains a string `value` and a map containing arbitrary key-value pairs. Review: https://reviews.apache.org/r/56623/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/81cbc395 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/81cbc395 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/81cbc395 Branch: refs/heads/master Commit: 81cbc395295fe9922473b12b1c966d01fe298341 Parents: 5ac6e15 Author: Greg Mann <[email protected]> Authored: Mon Mar 6 12:38:52 2017 -0800 Committer: Vinod Kone <[email protected]> Committed: Mon Mar 6 12:38:52 2017 -0800 ---------------------------------------------------------------------- .../include/process/authenticator.hpp | 45 +++++++++++++++++++- 3rdparty/libprocess/include/process/http.hpp | 4 +- 3rdparty/libprocess/include/process/process.hpp | 25 +++++++---- 3rdparty/libprocess/src/authenticator.cpp | 24 ++++++++++- .../libprocess/src/authenticator_manager.cpp | 15 ++++++- 3rdparty/libprocess/src/process.cpp | 4 +- 6 files changed, 102 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/81cbc395/3rdparty/libprocess/include/process/authenticator.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/authenticator.hpp b/3rdparty/libprocess/include/process/authenticator.hpp index e5489c6..00660f4 100644 --- a/3rdparty/libprocess/include/process/authenticator.hpp +++ b/3rdparty/libprocess/include/process/authenticator.hpp @@ -13,6 +13,7 @@ #ifndef __PROCESS_AUTHENTICATOR_HPP__ #define __PROCESS_AUTHENTICATOR_HPP__ +#include <iosfwd> #include <string> #include <process/future.hpp> @@ -28,6 +29,48 @@ namespace authentication { class BasicAuthenticatorProcess; /** + * Contains information associated with an authenticated principal. + * + * At least one of the following two members should be set: + * `value` : Optional string which is used to identify this principal. + * `claims`: Map containing key-value pairs associated with this principal. + */ +struct Principal +{ + Principal() = delete; + + Principal(const Option<std::string>& _value) + : value(_value) {} + + Principal( + const Option<std::string>& _value, + const std::map<std::string, std::string>& _claims) + : value(_value), claims(_claims) {} + + bool operator==(const Principal& that) const + { + return this->value == that.value && this->claims == that.claims; + } + + bool operator==(const std::string& that) const + { + return this->value == that; + } + + bool operator!=(const std::string& that) const + { + return !(*this == that); + } + + Option<std::string> value; + std::map<std::string, std::string> claims; +}; + + +std::ostream& operator<<(std::ostream& stream, const Principal& principal); + + +/** * Represents the result of authenticating a request. * An `AuthenticationResult` can represent one of the * following states: @@ -44,7 +87,7 @@ class BasicAuthenticatorProcess; */ struct AuthenticationResult { - Option<std::string> principal; + Option<Principal> principal; Option<Unauthorized> unauthorized; Option<Forbidden> forbidden; }; http://git-wip-us.apache.org/repos/asf/mesos/blob/81cbc395/3rdparty/libprocess/include/process/http.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/http.hpp b/3rdparty/libprocess/include/process/http.hpp index eb2c87d..9b89d31 100644 --- a/3rdparty/libprocess/include/process/http.hpp +++ b/3rdparty/libprocess/include/process/http.hpp @@ -64,6 +64,8 @@ namespace authentication { class Authenticator; +struct Principal; + /** * Sets (or overwrites) the authenticator for the realm. * @@ -97,7 +99,7 @@ namespace authorization { typedef hashmap<std::string, lambda::function<process::Future<bool>( const Request, - const Option<std::string> principal)>> + const Option<authentication::Principal>)>> AuthorizationCallbacks; http://git-wip-us.apache.org/repos/asf/mesos/blob/81cbc395/3rdparty/libprocess/include/process/process.hpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/include/process/process.hpp b/3rdparty/libprocess/include/process/process.hpp index ceedbe8..401510c 100644 --- a/3rdparty/libprocess/include/process/process.hpp +++ b/3rdparty/libprocess/include/process/process.hpp @@ -20,6 +20,7 @@ #include <vector> #include <process/address.hpp> +#include <process/authenticator.hpp> #include <process/clock.hpp> #include <process/event.hpp> #include <process/filter.hpp> @@ -296,22 +297,28 @@ protected: /** * Any function which takes a `process::http::Request` and an - * `Option<std::string>` principal and returns a - * `process::http::Response`. + * `Option<Principal>` and returns a `process::http::Response`. + * This type is meant to be used for the endpoint handlers of + * authenticated HTTP endpoints. + * + * If the handler is called and the principal is set, + * this implies two things: + * 1) The realm that the handler's endpoint is installed into + * requires authentication. + * 2) The HTTP request has been successfully authenticated. * - * If the authentication principal string is set, the realm - * requires authentication and authentication succeeded. If - * it is not set, the realm does not require authentication. + * If the principal is not set, then the endpoint's + * realm does not require authentication. * * The default visit implementation for HTTP events invokes * installed HTTP handlers. * * @see process::ProcessBase::route */ - // TODO(arojas): Consider introducing an `authentication::Principal` type. typedef lambda::function<Future<http::Response>( - const http::Request&, const Option<std::string>&)> - AuthenticatedHttpRequestHandler; + const http::Request&, + const Option<http::authentication::Principal>&)> + AuthenticatedHttpRequestHandler; // TODO(arojas): Consider introducing an `authentication::Realm` type. void route( @@ -331,7 +338,7 @@ protected: const Option<std::string>& help, Future<http::Response> (T::*method)( const http::Request&, - const Option<std::string>&), + const Option<http::authentication::Principal>&), const RouteOptions& options = RouteOptions()) { // Note that we use dynamic_cast here so a process can use http://git-wip-us.apache.org/repos/asf/mesos/blob/81cbc395/3rdparty/libprocess/src/authenticator.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/authenticator.cpp b/3rdparty/libprocess/src/authenticator.cpp index cfedb6f..8d1756f 100644 --- a/3rdparty/libprocess/src/authenticator.cpp +++ b/3rdparty/libprocess/src/authenticator.cpp @@ -35,6 +35,28 @@ using std::string; using std::vector; +static void json(JSON::ObjectWriter* writer, const Principal& principal) +{ + if (principal.value.isSome()) { + writer->field("value", principal.value.get()); + } + if (!principal.claims.empty()) { + writer->field("claims", principal.claims); + } +} + + +std::ostream& operator<<(std::ostream& stream, const Principal& principal) +{ + // When only the `value` is set, simply output a string. + if (principal.value.isSome() && principal.claims.empty()) { + return stream << principal.value.get(); + } + + return stream << jsonify(principal); +}; + + class BasicAuthenticatorProcess : public Process<BasicAuthenticatorProcess> { public: @@ -93,7 +115,7 @@ Future<AuthenticationResult> BasicAuthenticatorProcess::authenticate( } AuthenticationResult authenticated; - authenticated.principal = credential[0]; + authenticated.principal = Principal(credential[0]); return authenticated; } http://git-wip-us.apache.org/repos/asf/mesos/blob/81cbc395/3rdparty/libprocess/src/authenticator_manager.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/authenticator_manager.cpp b/3rdparty/libprocess/src/authenticator_manager.cpp index a22acd0..5cbed53 100644 --- a/3rdparty/libprocess/src/authenticator_manager.cpp +++ b/3rdparty/libprocess/src/authenticator_manager.cpp @@ -97,8 +97,19 @@ Future<Option<AuthenticationResult>> AuthenticatorManagerProcess::authenticate( (authentication.forbidden.isSome() ? 1 : 0); if (count != 1) { - return Failure("Expecting one of 'principal', 'unauthorized'," - " or 'forbidden' to be set"); + 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; http://git-wip-us.apache.org/repos/asf/mesos/blob/81cbc395/3rdparty/libprocess/src/process.cpp ---------------------------------------------------------------------- diff --git a/3rdparty/libprocess/src/process.cpp b/3rdparty/libprocess/src/process.cpp index 3ad485f..9eb7fe3 100644 --- a/3rdparty/libprocess/src/process.cpp +++ b/3rdparty/libprocess/src/process.cpp @@ -126,6 +126,7 @@ using process::http::Response; using process::http::ServiceUnavailable; using process::http::authentication::Authenticator; +using process::http::authentication::Principal; using process::http::authentication::AuthenticationResult; using process::http::authentication::AuthenticatorManager; @@ -3831,7 +3832,7 @@ Future<Response> ProcessBase::_visit( .then(defer(self(), [this, endpoint, request, name]( const Option<AuthenticationResult>& authentication) -> Future<Response> { - Option<string> principal = None(); + Option<Principal> principal = None(); // If authentication failed, we do not continue with authorization. if (authentication.isSome()) { @@ -3843,6 +3844,7 @@ Future<Response> ProcessBase::_visit( return authentication->forbidden.get(); } + CHECK_SOME(authentication->principal); principal = authentication->principal; }
