Repository: mesos
Updated Branches:
  refs/heads/master d503fbbfc -> 95b9aa0e8


Added `ObjectApprover` interface to `Authorizer`.

With the goal to provide more efficient authorization for multiple,
potentially large objects, we extend the authorizer interface to support
`ObjectApprover`. Retrieving an `ObjectApprover` for a given action is
an asynchronous operation but following authorization of multiple
objects can be done synchronously without copying.

NOTE: This implies that `Authorizer` module writers need to ensure that
an `ObjectApprover` will behave nicely (e.g., it must not block).

Review: https://reviews.apache.org/r/47558/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/4c83d7f7
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/4c83d7f7
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/4c83d7f7

Branch: refs/heads/master
Commit: 4c83d7f7adc5f5146426eff70b67c317a583c15d
Parents: d503fbb
Author: Joerg Schad <jo...@mesosphere.io>
Authored: Mon May 30 23:25:21 2016 -0700
Committer: Michael Park <mp...@apache.org>
Committed: Mon May 30 23:25:21 2016 -0700

----------------------------------------------------------------------
 include/mesos/authorizer/authorizer.hpp |  62 +++
 src/authorizer/local/authorizer.cpp     | 570 ++++++++++++++++++---------
 src/authorizer/local/authorizer.hpp     |   4 +
 src/master/http.cpp                     |  88 +++++
 src/tests/mesos.cpp                     |  14 +
 src/tests/mesos.hpp                     |   5 +
 6 files changed, 565 insertions(+), 178 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/4c83d7f7/include/mesos/authorizer/authorizer.hpp
----------------------------------------------------------------------
diff --git a/include/mesos/authorizer/authorizer.hpp 
b/include/mesos/authorizer/authorizer.hpp
index ed5f9e7..ea49681 100644
--- a/include/mesos/authorizer/authorizer.hpp
+++ b/include/mesos/authorizer/authorizer.hpp
@@ -33,6 +33,52 @@ namespace mesos {
 class ACLs;
 
 /**
+ * This interface represents a function object returned by the
+ * authorizer which can be used locally (and synchronously) to
+ * check whether a specific object is authorized.
+ */
+class ObjectApprover
+{
+public:
+  // This object has a 1:1 relationship with `authorization::Object`.
+  // We need to ensure that the fields in this object are in sync
+  // with the fields in `authorization::Object`.
+  struct Object
+  {
+    Object()
+      : value(NULL),
+        framework_info(NULL),
+        task(NULL),
+        task_info(NULL),
+        executor_info(NULL) {}
+
+    Object(const authorization::Object& object)
+      : value(object.has_value() ? &object.value() : NULL),
+        framework_info(
+            object.has_framework_info() ? &object.framework_info() : NULL),
+        task(object.has_task() ? &object.task() : NULL),
+        task_info(object.has_task_info() ? &object.task_info() : NULL),
+        executor_info(
+            object.has_executor_info() ? &object.executor_info() : NULL) {}
+
+    const std::string* value;
+    const FrameworkInfo* framework_info;
+    const Task* task;
+    const TaskInfo* task_info;
+    const ExecutorInfo* executor_info;
+  };
+
+  /**
+   * NOTE: As this function can be used synchronously by actors
+   * it is essential that it does not block!
+   */
+  virtual Try<bool> approved(const Object& object) const noexcept = 0;
+
+  virtual ~ObjectApprover() = default;
+};
+
+
+/**
  * This interface is used to enable an identity service or any other
  * back end to check authorization policies for a set of predefined
  * actions.
@@ -94,6 +140,22 @@ public:
   virtual process::Future<bool> authorized(
       const authorization::Request& request) = 0;
 
+  /**
+   * Creates an `ObjectApprover` which can synchronously check authorization on
+   * an object.
+   *
+   * @param subject `authorization::Subject` subject for which the
+   *     `ObjectApprover` should be created.
+   *
+   * @param action `authorization::Action` action for which the
+   *     `ObjectApprover` should be created.
+   *
+   * @return An `ObjectApprover` for the given `subject` and `action`.
+   */
+  virtual process::Future<process::Owned<ObjectApprover>> getObjectApprover(
+      const authorization::Subject& subject,
+      const authorization::Action& action) = 0;
+
 protected:
   Authorizer() {}
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/4c83d7f7/src/authorizer/local/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.cpp 
b/src/authorizer/local/authorizer.cpp
index 547bbdd..be2c691 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -41,6 +41,7 @@
 using process::dispatch;
 using process::Failure;
 using process::Future;
+using process::Owned;
 
 using std::string;
 using std::vector;
@@ -54,6 +55,266 @@ struct GenericACL
   ACL::Entity objects;
 };
 
+// Match matrix:
+//
+//                  -----------ACL----------
+//
+//                    SOME    NONE    ANY
+//          -------|-------|-------|-------
+//  |        SOME  | Yes/No|  Yes  |   Yes
+//  |       -------|-------|-------|-------
+// Request   NONE  |  No   |  Yes  |   No
+//  |       -------|-------|-------|-------
+//  |        ANY   |  No   |  Yes  |   Yes
+//          -------|-------|-------|-------
+static bool matches(const ACL::Entity& request, const ACL::Entity& acl)
+{
+  // NONE only matches with NONE.
+  if (request.type() == ACL::Entity::NONE) {
+    return acl.type() == ACL::Entity::NONE;
+  }
+
+  // ANY matches with ANY or NONE.
+  if (request.type() == ACL::Entity::ANY) {
+    return acl.type() == ACL::Entity::ANY || acl.type() == ACL::Entity::NONE;
+  }
+
+  if (request.type() == ACL::Entity::SOME) {
+    // SOME matches with ANY or NONE.
+    if (acl.type() == ACL::Entity::ANY || acl.type() == ACL::Entity::NONE) {
+      return true;
+    }
+
+    // SOME is allowed if the request values are a subset of ACL
+    // values.
+    foreach (const string& value, request.values()) {
+      bool found = false;
+      foreach (const string& value_, acl.values()) {
+        if (value == value_) {
+          found = true;
+          break;
+        }
+      }
+
+      if (!found) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  return false;
+}
+
+// Allow matrix:
+//
+//                 -----------ACL----------
+//
+//                    SOME    NONE    ANY
+//          -------|-------|-------|-------
+//  |        SOME  | Yes/No|  No   |   Yes
+//  |       -------|-------|-------|-------
+// Request   NONE  |  No   |  Yes  |   No
+//  |       -------|-------|-------|-------
+//  |        ANY   |  No   |  No   |   Yes
+//          -------|-------|-------|-------
+static bool allows(const ACL::Entity& request, const ACL::Entity& acl)
+{
+  // NONE is only allowed by NONE.
+  if (request.type() == ACL::Entity::NONE) {
+    return acl.type() == ACL::Entity::NONE;
+  }
+
+  // ANY is only allowed by ANY.
+  if (request.type() == ACL::Entity::ANY) {
+    return acl.type() == ACL::Entity::ANY;
+  }
+
+  if (request.type() == ACL::Entity::SOME) {
+    // SOME is allowed by ANY.
+    if (acl.type() == ACL::Entity::ANY) {
+      return true;
+    }
+
+    // SOME is not allowed by NONE.
+    if (acl.type() == ACL::Entity::NONE) {
+      return false;
+    }
+
+    // SOME is allowed if the request values are a subset of ACL
+    // values.
+    foreach (const string& value, request.values()) {
+      bool found = false;
+      foreach (const string& value_, acl.values()) {
+        if (value == value_) {
+          found = true;
+          break;
+        }
+      }
+
+      if (!found) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  return false;
+}
+
+
+class LocalAuthorizerObjectApprover : public ObjectApprover
+{
+public:
+  LocalAuthorizerObjectApprover(
+      const vector<GenericACL>& acls,
+      const authorization::Subject& subject,
+      const authorization::Action& action,
+      const bool permissive)
+    : acls_(acls),
+      subject_(subject),
+      action_(action),
+      permissive_(permissive) {}
+
+  virtual Try<bool> approved(
+      const ObjectApprover::Object& object) const noexcept override {
+    // Construct subject.
+    ACL::Entity aclSubject;
+    if (subject_.has_value()) {
+      aclSubject.add_values(subject_.value());
+      aclSubject.set_type(mesos::ACL::Entity::SOME);
+    } else {
+      aclSubject.set_type(mesos::ACL::Entity::ANY);
+    }
+
+    // Construct object.
+    ACL::Entity aclObject;
+    switch (action_) {
+      // All actions using `object.value` for authorization.
+      // Missing `object.value` implies 'ANY'.
+      case authorization::REGISTER_FRAMEWORK_WITH_ROLE:
+      case authorization::TEARDOWN_FRAMEWORK_WITH_PRINCIPAL:
+      case authorization::RUN_TASK_WITH_USER:
+      case authorization::RESERVE_RESOURCES_WITH_ROLE:
+      case authorization::UNRESERVE_RESOURCES_WITH_PRINCIPAL:
+      case authorization::CREATE_VOLUME_WITH_ROLE:
+      case authorization::DESTROY_VOLUME_WITH_PRINCIPAL:
+      case authorization::GET_QUOTA_WITH_ROLE:
+      case authorization::UPDATE_QUOTA_WITH_ROLE:
+      case authorization::SET_QUOTA_WITH_ROLE:
+      case authorization::DESTROY_QUOTA_WITH_PRINCIPAL:
+      case authorization::UPDATE_WEIGHTS_WITH_ROLE:
+      case authorization::GET_ENDPOINT_WITH_PATH: {
+        // Construct object.
+        if (object.value != NULL) {
+          aclObject.add_values(*(object.value));
+          aclObject.set_type(mesos::ACL::Entity::SOME);
+        } else {
+          aclObject.set_type(mesos::ACL::Entity::ANY);
+        }
+
+        break;
+      }
+      case authorization::ACCESS_MESOS_LOG: {
+        aclObject.set_type(mesos::ACL::Entity::ANY);
+
+        break;
+      }
+      case authorization::ACCESS_SANDBOX: {
+        // Check object has the required types set.
+        CHECK_NOTNULL(object.executor_info);
+        CHECK_NOTNULL(object.framework_info);
+
+        if (object.executor_info->command().has_user()) {
+          aclObject.add_values(object.executor_info->command().user());
+          aclObject.set_type(mesos::ACL::Entity::SOME);
+        } else {
+          aclObject.add_values(object.framework_info->user());
+          aclObject.set_type(mesos::ACL::Entity::SOME);
+        }
+
+        break;
+      }
+      case authorization::VIEW_FRAMEWORK: {
+        // Check object has the required types set.
+        CHECK_NOTNULL(object.framework_info);
+
+        aclObject.add_values(object.framework_info->user());
+        aclObject.set_type(mesos::ACL::Entity::SOME);
+
+        break;
+      }
+      case authorization::VIEW_TASK: {
+        CHECK(object.task != NULL || object.task_info != NULL);
+        CHECK_NOTNULL(object.framework_info);
+
+        // First we consider either whether `Task` or `TaskInfo`
+        // have `user` set. As fallback we use `FrameworkInfo.user`.
+        Option<string> taskUser = None();
+        if (object.task != NULL && object.task->has_user()) {
+          taskUser = object.task->user();
+        } else if (object.task_info != NULL) {
+          // Within TaskInfo the user can be either set in `command`
+          // or `executor.command`.
+          if (object.task_info->has_command() &&
+              object.task_info->command().has_user()) {
+            taskUser = object.task_info->command().user();
+          } else if (object.task_info->has_executor() &&
+                     object.task_info->executor().command().has_user()) {
+            taskUser = object.task_info->executor().command().user();
+          }
+        }
+
+        // In case there is no `user` set on task level we fallback
+        // to the `FrameworkInfo.user`.
+        if (taskUser.isNone()) {
+          taskUser = object.framework_info->user();
+        }
+        aclObject.add_values(taskUser.get());
+        aclObject.set_type(mesos::ACL::Entity::SOME);
+
+        break;
+      }
+      case authorization::VIEW_EXECUTOR: {
+        CHECK_NOTNULL(object.executor_info);
+        CHECK_NOTNULL(object.framework_info);
+
+        if (object.executor_info->command().has_user()) {
+          aclObject.add_values(object.executor_info->command().user());
+          aclObject.set_type(mesos::ACL::Entity::SOME);
+        } else {
+          aclObject.add_values(object.framework_info->user());
+          aclObject.set_type(mesos::ACL::Entity::SOME);
+        }
+
+        break;
+      }
+      case authorization::UNKNOWN:
+        LOG(WARNING) << "Authorization for action '" << action_
+                     << "' is not defined and therefore not authorized";
+        return false;
+        break;
+    }
+
+    // Authorize subject/object.
+    foreach (const GenericACL& acl, acls_) {
+      if (matches(aclSubject, acl.subjects) &&
+          matches(aclObject, acl.objects)) {
+        return allows(aclSubject, acl.subjects) &&
+               allows(aclObject, acl.objects);
+      }
+    }
+
+    return permissive_; // None of the ACLs match.
+  }
+
+private:
+  const vector<GenericACL> acls_;
+  const authorization::Subject subject_;
+  const authorization::Action action_;
+  const bool permissive_;
+};
+
 
 class LocalAuthorizerProcess : public ProtobufProcess<LocalAuthorizerProcess>
 {
@@ -98,9 +359,56 @@ public:
 
   Future<bool> authorized(const authorization::Request& request)
   {
+    return getObjectApprover(request.subject(), request.action())
+      .then([=](const Owned<ObjectApprover>& objectApprover) -> Future<bool> {
+        ObjectApprover::Object object(request.object());
+        Try<bool> result = objectApprover->approved(object);
+        if (result.isError()) {
+          return Failure(result.error());
+        }
+        return result.get();
+      });
+  }
+
+  Future<Owned<ObjectApprover>> getObjectApprover(
+      const authorization::Subject& subject,
+      const authorization::Action& action)
+  {
+    // Implementation of the ObjectApprover interface denying all objects.
+    class RejectingObjectApprover : public ObjectApprover
+    {
+    public:
+      virtual Try<bool> approved(
+          const ObjectApprover::Object& object) const noexcept override
+      {
+        return false;
+      }
+    };
+
+    // Generate GenericACLs.
+    Result<vector<GenericACL>> genericACLs = createGenericACLs(action, acls);
+    if (genericACLs.isError()) {
+      return Failure(genericACLs.error());
+    }
+
+    if (genericACLs.isNone()) {
+      // If we could not create acls, we deny all objects.
+      return Owned<ObjectApprover>(new RejectingObjectApprover());
+    }
+
+    return Owned<ObjectApprover>(
+        new LocalAuthorizerObjectApprover(
+            genericACLs.get(), subject, action, acls.permissive()));
+  }
+
+private:
+  static Result<vector<GenericACL>> createGenericACLs(
+      const authorization::Action& action,
+      const ACLs& acls)
+  {
     vector<GenericACL> acls_;
 
-    switch (request.action()) {
+    switch (action) {
       case authorization::REGISTER_FRAMEWORK_WITH_ROLE:
         foreach (
             const ACL::RegisterFramework& acl, acls.register_frameworks()) {
@@ -111,7 +419,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::TEARDOWN_FRAMEWORK_WITH_PRINCIPAL:
         foreach (
@@ -123,7 +431,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::RUN_TASK_WITH_USER:
         foreach (const ACL::RunTask& acl, acls.run_tasks()) {
@@ -134,7 +442,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::RESERVE_RESOURCES_WITH_ROLE:
         foreach (const ACL::ReserveResources& acl, acls.reserve_resources()) {
@@ -145,7 +453,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::UNRESERVE_RESOURCES_WITH_PRINCIPAL:
         foreach (
@@ -157,7 +465,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::CREATE_VOLUME_WITH_ROLE:
         foreach (const ACL::CreateVolume& acl, acls.create_volumes()) {
@@ -168,7 +476,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::DESTROY_VOLUME_WITH_PRINCIPAL:
         foreach (const ACL::DestroyVolume& acl, acls.destroy_volumes()) {
@@ -179,7 +487,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::GET_QUOTA_WITH_ROLE:
         foreach (const ACL::GetQuota& acl, acls.get_quotas()) {
@@ -190,7 +498,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::UPDATE_QUOTA_WITH_ROLE:
         // Deprecation case: If `update_quotas` is empty but
@@ -202,7 +510,15 @@ public:
         // of the deprecation cycle which started with 0.29.
         if (acls.set_quotas_size() > 0 || acls.remove_quotas_size() > 0) {
           CHECK(acls.update_quotas_size() == 0);
-          return true;
+
+          // TODO(joerg): This is a hack to support the quota behavior.
+          // Remove this once we remove the `SET_QUOTA_WITH_ROLE` and
+          // `REMOVE_QUOTA_WITH_PRINCIPAL` actions.
+          GenericACL acl_;
+          acl_.subjects.set_type(ACL::Entity::ANY);
+          acl_.objects.set_type(ACL::Entity::ANY);
+
+          acls_.push_back(acl_);
         }
 
         foreach (const ACL::UpdateQuota& acl, acls.update_quotas()) {
@@ -213,7 +529,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
 
       // TODO(zhitao): Remove the following two cases at the end
@@ -221,7 +537,15 @@ public:
       case authorization::SET_QUOTA_WITH_ROLE:
         if (acls.update_quotas_size() > 0) {
           CHECK(acls.set_quotas_size() == 0);
-          return true;
+
+          // TODO(joerg): This is a hack to support the quota behavior.
+          // Remove this once we remove the `SET_QUOTA_WITH_ROLE` and
+          // `REMOVE_QUOTA_WITH_PRINCIPAL` actions.
+          GenericACL acl_;
+          acl_.subjects.set_type(ACL::Entity::ANY);
+          acl_.objects.set_type(ACL::Entity::ANY);
+
+          acls_.push_back(acl_);
         }
 
         foreach (const ACL::SetQuota& acl, acls.set_quotas()) {
@@ -232,12 +556,20 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::DESTROY_QUOTA_WITH_PRINCIPAL:
         if (acls.update_quotas_size() > 0) {
           CHECK(acls.remove_quotas_size() == 0);
-          return true;
+
+          // TODO(joerg): This is a hack to support the quota behavior.
+          // Remove this once we remove the `SET_QUOTA_WITH_ROLE` and
+          // `REMOVE_QUOTA_WITH_PRINCIPAL` actions.
+          GenericACL acl_;
+          acl_.subjects.set_type(ACL::Entity::ANY);
+          acl_.objects.set_type(ACL::Entity::ANY);
+
+          acls_.push_back(acl_);
         }
 
         foreach (const ACL::RemoveQuota& acl, acls.remove_quotas()) {
@@ -248,9 +580,8 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
-
       case authorization::UPDATE_WEIGHTS_WITH_ROLE:
         foreach (const ACL::UpdateWeight& acl, acls.update_weights()) {
           GenericACL acl_;
@@ -260,7 +591,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::GET_ENDPOINT_WITH_PATH:
         foreach (const ACL::GetEndpoint& acl, acls.get_endpoints()) {
@@ -271,7 +602,7 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::ACCESS_MESOS_LOG:
         foreach (const ACL::AccessMesosLog& acl, acls.access_mesos_logs()) {
@@ -282,27 +613,9 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(request, acls_);
+        return acls_;
         break;
       case authorization::ACCESS_SANDBOX: {
-        authorization::Request realRequest;
-        realRequest.set_action(authorization::ACCESS_SANDBOX);
-
-        if (request.subject().has_value()) {
-          realRequest.mutable_subject()->set_value(request.subject().value());
-        }
-
-        authorization::Object* object = realRequest.mutable_object();
-
-        if (request.object().has_executor_info() &&
-            request.object().executor_info().has_command() &&
-            request.object().executor_info().command().has_user()) {
-          object->set_value(request.object().executor_info().command().user());
-        } else if (request.object().has_framework_info() &&
-                   request.object().framework_info().has_user()) {
-          object->set_value(request.object().framework_info().user());
-        }
-
         foreach (const ACL::AccessSandbox& acl, acls.access_sandboxes()) {
           GenericACL acl_;
           acl_.subjects = acl.principals();
@@ -311,159 +624,48 @@ public:
           acls_.push_back(acl_);
         }
 
-        return authorized(realRequest, acls_);
+        return acls_;
         break;
       }
-      // TODO(joerg84): Add logic for the `VIEW_*` actions.
       case authorization::VIEW_FRAMEWORK:
-      case authorization::VIEW_TASK:
-      case authorization::VIEW_EXECUTOR:
-      case authorization::UNKNOWN:
-        LOG(WARNING) << "Authorization request for action '" << 
request.action()
-                     << "' is not defined and therefore not authorized";
-        return false;
-        break;
-    }
-    UNREACHABLE();
-  }
-
-private:
-  Future<bool> authorized(
-      const authorization::Request& request,
-      const vector<GenericACL>& acls)
-  {
-    ACL::Entity subject;
-    if (request.subject().has_value()) {
-      subject.add_values(request.subject().value());
-      subject.set_type(mesos::ACL::Entity::SOME);
-    } else {
-      subject.set_type(mesos::ACL::Entity::ANY);
-    }
-
-    ACL::Entity object;
-    if (request.object().has_value()) {
-      object.add_values(request.object().value());
-      object.set_type(mesos::ACL::Entity::SOME);
-    } else {
-      object.set_type(mesos::ACL::Entity::ANY);
-    }
-
-    foreach (const GenericACL& acl, acls) {
-      if (matches(subject, acl.subjects) &&
-          matches(object, acl.objects)) {
-        return allows(subject, acl.subjects) &&
-            allows(object, acl.objects);
-      }
-    }
-
-    return this->acls.permissive(); // None of the ACLs match.
-  }
-
-  // Match matrix:
-  //
-  //                  -----------ACL----------
-  //
-  //                    SOME    NONE    ANY
-  //          -------|-------|-------|-------
-  //  |        SOME  | Yes/No|  Yes  |   Yes
-  //  |       -------|-------|-------|-------
-  // Request   NONE  |  No   |  Yes  |   No
-  //  |       -------|-------|-------|-------
-  //  |        ANY   |  No   |  Yes  |   Yes
-  //          -------|-------|-------|-------
-  bool matches(const ACL::Entity& request, const ACL::Entity& acl)
-  {
-    // NONE only matches with NONE.
-    if (request.type() == ACL::Entity::NONE) {
-      return acl.type() == ACL::Entity::NONE;
-    }
-
-    // ANY matches with ANY or NONE.
-    if (request.type() == ACL::Entity::ANY) {
-      return acl.type() == ACL::Entity::ANY || acl.type() == ACL::Entity::NONE;
-    }
-
-    if (request.type() == ACL::Entity::SOME) {
-      // SOME matches with ANY or NONE.
-      if (acl.type() == ACL::Entity::ANY || acl.type() == ACL::Entity::NONE) {
-        return true;
-      }
-
-      // SOME is allowed if the request values are a subset of ACL
-      // values.
-      foreach (const string& value, request.values()) {
-        bool found = false;
-        foreach (const string& value_, acl.values()) {
-          if (value == value_) {
-            found = true;
-            break;
-          }
-        }
+        foreach (const ACL::ViewFramework& acl, acls.view_frameworks()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.users();
 
-        if (!found) {
-          return false;
+          acls_.push_back(acl_);
         }
-      }
-      return true;
-    }
-
-    return false;
-  }
-
-  // Allow matrix:
-  //
-  //                 -----------ACL----------
-  //
-  //                    SOME    NONE    ANY
-  //          -------|-------|-------|-------
-  //  |        SOME  | Yes/No|  No   |   Yes
-  //  |       -------|-------|-------|-------
-  // Request   NONE  |  No   |  Yes  |   No
-  //  |       -------|-------|-------|-------
-  //  |        ANY   |  No   |  No   |   Yes
-  //          -------|-------|-------|-------
-  bool allows(const ACL::Entity& request, const ACL::Entity& acl)
-  {
-    // NONE is only allowed by NONE.
-    if (request.type() == ACL::Entity::NONE) {
-      return acl.type() == ACL::Entity::NONE;
-    }
 
-    // ANY is only allowed by ANY.
-    if (request.type() == ACL::Entity::ANY) {
-      return acl.type() == ACL::Entity::ANY;
-    }
+        return acls_;
+        break;
+      case authorization::VIEW_TASK:
+        foreach (const ACL::ViewTask& acl, acls.view_tasks()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.users();
 
-    if (request.type() == ACL::Entity::SOME) {
-      // SOME is allowed by ANY.
-      if (acl.type() == ACL::Entity::ANY) {
-        return true;
-      }
+          acls_.push_back(acl_);
+        }
 
-      // SOME is not allowed by NONE.
-      if (acl.type() == ACL::Entity::NONE) {
-        return false;
-      }
+        return acls_;
+        break;
+      case authorization::VIEW_EXECUTOR:
+        foreach (const ACL::ViewExecutor& acl, acls.view_executors()) {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.users();
 
-      // SOME is allowed if the request values are a subset of ACL
-      // values.
-      foreach (const string& value, request.values()) {
-        bool found = false;
-        foreach (const string& value_, acl.values()) {
-          if (value == value_) {
-            found = true;
-            break;
-          }
+          acls_.push_back(acl_);
         }
 
-        if (!found) {
-          return false;
-        }
-      }
-      return true;
+        return acls_;
+        break;
+      case authorization::UNKNOWN:
+        // Cannot generate acls for an unknown action.
+        return None();
+        break;
     }
-
-    return false;
+    UNREACHABLE();
   }
 
   ACLs acls;
@@ -554,5 +756,17 @@ process::Future<bool> LocalAuthorizer::authorized(
       request);
 }
 
+
+Future<Owned<ObjectApprover>> LocalAuthorizer::getObjectApprover(
+      const authorization::Subject& subject,
+      const authorization::Action& action)
+{
+  return dispatch(
+      process,
+      &LocalAuthorizerProcess::getObjectApprover,
+      subject,
+      action);
+}
+
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/4c83d7f7/src/authorizer/local/authorizer.hpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.hpp 
b/src/authorizer/local/authorizer.hpp
index 6138845..9242889 100644
--- a/src/authorizer/local/authorizer.hpp
+++ b/src/authorizer/local/authorizer.hpp
@@ -65,6 +65,10 @@ public:
   virtual process::Future<bool> authorized(
       const authorization::Request& request);
 
+  virtual process::Future<process::Owned<ObjectApprover>> getObjectApprover(
+      const authorization::Subject& subject,
+      const authorization::Action& action);
+
 private:
   LocalAuthorizer(const ACLs& acls);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/4c83d7f7/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index c8d2f46..0d65c15 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -161,6 +161,94 @@ struct Full : Representation<T>
 };
 
 
+static bool approveViewFrameworkInfo(
+    const Owned<ObjectApprover>& frameworksApprover,
+    const FrameworkInfo& frameworkInfo)
+{
+  ObjectApprover::Object object;
+  object.framework_info = &frameworkInfo;
+
+  Try<bool> approved = frameworksApprover->approved(object);
+  if (approved.isError()) {
+    LOG(WARNING) << "Error during FrameworkInfo authorization: "
+                 << approved.error();
+    // TODO(joerg84): Consider exposing these errors to the caller.
+    return false;
+  }
+  return approved.get();
+}
+
+
+static bool approveViewTaskInfo(
+    const Owned<ObjectApprover>& tasksApprover,
+    const TaskInfo& taskInfo,
+    const FrameworkInfo& frameworkInfo)
+{
+  ObjectApprover::Object object;
+  object.task_info = &taskInfo;
+  object.framework_info = &frameworkInfo;
+
+  Try<bool> approved = tasksApprover->approved(object);
+  if (approved.isError()) {
+    LOG(WARNING) << "Error during TaskInfo authorization: " << 
approved.error();
+    // TODO(joerg84): Consider exposing these errors to the caller.
+    return false;
+  }
+  return approved.get();
+}
+
+
+static bool approveViewTask(
+    const Owned<ObjectApprover>& tasksApprover,
+    const Task& task,
+    const FrameworkInfo& frameworkInfo)
+{
+  ObjectApprover::Object object;
+  object.task = &task;
+  object.framework_info = &frameworkInfo;
+
+  Try<bool> approved = tasksApprover->approved(object);
+  if (approved.isError()) {
+    LOG(WARNING) << "Error during Task authorization: " << approved.error();
+     // TODO(joerg84): Consider exposing these errors to the caller.
+    return false;
+  }
+  return approved.get();
+}
+
+
+static bool approveViewExecutorInfo(
+    const Owned<ObjectApprover>& executorsApprover,
+    const ExecutorInfo& executorInfo,
+    const FrameworkInfo& frameworkInfo)
+{
+  ObjectApprover::Object object;
+  object.executor_info = &executorInfo;
+  object.framework_info = &frameworkInfo;
+
+  Try<bool> approved = executorsApprover->approved(object);
+  if (approved.isError()) {
+    LOG(WARNING) << "Error during ExecutorInfo authorization: "
+             << approved.error();
+    // TODO(joerg84): Consider exposing these errors to the caller.
+    return false;
+  }
+  return approved.get();
+}
+
+
+// Implementation of the ObjectApprover interface authorizing all objects.
+class ObjectApproverAll : public ObjectApprover
+{
+public:
+  virtual Try<bool> approved(
+      const ObjectApprover::Object& object) const noexcept override
+  {
+    return true;
+  }
+};
+
+
 static void json(JSON::ObjectWriter* writer, const Summary<Slave>& summary)
 {
   const Slave& slave = summary;

http://git-wip-us.apache.org/repos/asf/mesos/blob/4c83d7f7/src/tests/mesos.cpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.cpp b/src/tests/mesos.cpp
index 629135f..6c64bd5 100644
--- a/src/tests/mesos.cpp
+++ b/src/tests/mesos.cpp
@@ -670,11 +670,25 @@ 
MockDockerContainerizerProcess::~MockDockerContainerizerProcess() {}
 
 MockAuthorizer::MockAuthorizer()
 {
+  // Implementation of the ObjectApprover interface authorizing all objects.
+  class ObjectApproverAll : public ObjectApprover
+  {
+  public:
+    virtual Try<bool> approved(
+        const ObjectApprover::Object& object) const noexcept override
+    {
+      return true;
+    }
+  };
+
   // NOTE: We use 'EXPECT_CALL' and 'WillRepeatedly' here instead of
   // 'ON_CALL' and 'WillByDefault'. See 'TestContainerizer::SetUp()'
   // for more details.
   EXPECT_CALL(*this, authorized(_))
     .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(*this, getObjectApprover(_, _))
+    .WillRepeatedly(Return(Owned<ObjectApprover>(new ObjectApproverAll())));
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/4c83d7f7/src/tests/mesos.hpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index 041cd42..83c5d41 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -1701,6 +1701,11 @@ public:
 
   MOCK_METHOD1(
       authorized, process::Future<bool>(const authorization::Request& 
request));
+
+  MOCK_METHOD2(
+      getObjectApprover, process::Future<process::Owned<ObjectApprover>>(
+          const authorization::Subject& subject,
+          const authorization::Action& action));
 };
 
 

Reply via email to