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;
       }
 

Reply via email to