You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by me...@apache.org on 2017/04/27 08:17:44 UTC

[1/2] mesos git commit: Added support for authorization of Hierachical roles.

Repository: mesos
Updated Branches:
  refs/heads/master 2cde52884 -> d55148afb


Added support for authorization of Hierachical roles.

Adds mechanisms to support authorization of hierarchical roles,
that is, it allows operators to write ACLs of the form role/%
which will enforce the rule for any nested role, e.g. role/a,
role/b and such.

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


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

Branch: refs/heads/master
Commit: d46d33079805379515de0c300d7e6f4286adeb77
Parents: 2cde528
Author: Alexander Rojas <al...@mesosphere.io>
Authored: Thu Apr 27 00:02:06 2017 -0700
Committer: Adam B <ad...@mesosphere.io>
Committed: Thu Apr 27 00:03:26 2017 -0700

----------------------------------------------------------------------
 src/authorizer/local/authorizer.cpp | 537 ++++++++++++++++++++++---------
 1 file changed, 384 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d46d3307/src/authorizer/local/authorizer.cpp
----------------------------------------------------------------------
diff --git a/src/authorizer/local/authorizer.cpp b/src/authorizer/local/authorizer.cpp
index 3eb59d3..9082fb1 100644
--- a/src/authorizer/local/authorizer.cpp
+++ b/src/authorizer/local/authorizer.cpp
@@ -199,8 +199,6 @@ public:
       aclObject.set_type(mesos::ACL::Entity::ANY);
     } else {
       switch (action_) {
-        // All actions using `object.value` for authorization.
-        case authorization::VIEW_ROLE:
         case authorization::GET_ENDPOINT_WITH_PATH:
           // Check object has the required types set.
           CHECK_NOTNULL(object->value);
@@ -209,25 +207,6 @@ public:
           aclObject.set_type(mesos::ACL::Entity::SOME);
 
           break;
-        case authorization::REGISTER_FRAMEWORK:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->framework_info) {
-            foreach (
-                const string& role,
-                protobuf::framework::getRoles(*object->framework_info)) {
-              aclObject.add_values(role);
-            }
-          } else if (object->value) {
-            // We also update the deprecated `value` field to support custom
-            // authorizers not yet modified to examine `framework_info`.
-            //
-            // TODO(bbannier): Clean up use of `value` here, see MESOS-7091.
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
         case authorization::TEARDOWN_FRAMEWORK:
           aclObject.set_type(mesos::ACL::Entity::SOME);
           if (object->framework_info) {
@@ -239,18 +218,6 @@ public:
           }
 
           break;
-        case authorization::CREATE_VOLUME:
-        case authorization::RESERVE_RESOURCES:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->resource) {
-            aclObject.add_values(object->resource->role());
-          } else if (object->value) {
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
         case authorization::DESTROY_VOLUME:
           aclObject.set_type(mesos::ACL::Entity::SOME);
           if (object->resource) {
@@ -274,28 +241,6 @@ public:
           }
 
           break;
-        case authorization::GET_QUOTA:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->quota_info) {
-            aclObject.add_values(object->quota_info->role());
-          } else if (object->value) {
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
-        case authorization::UPDATE_WEIGHT:
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-          if (object->weight_info) {
-            aclObject.add_values(object->weight_info->role());
-          } else if (object->value) {
-            aclObject.add_values(*(object->value));
-          } else {
-            aclObject.set_type(mesos::ACL::Entity::ANY);
-          }
-
-          break;
         case authorization::RUN_TASK:
           aclObject.set_type(mesos::ACL::Entity::SOME);
           if (object->task_info && object->task_info->has_command() &&
@@ -354,14 +299,6 @@ public:
           }
 
           break;
-        case authorization::UPDATE_QUOTA:
-          // Check object has the required types set.
-          CHECK_NOTNULL(object->quota_info);
-
-          aclObject.add_values(object->quota_info->role());
-          aclObject.set_type(mesos::ACL::Entity::SOME);
-
-          break;
         case authorization::VIEW_FRAMEWORK:
           // Check object has the required types set.
           CHECK_NOTNULL(object->framework_info);
@@ -455,11 +392,19 @@ public:
           aclObject.set_type(mesos::ACL::Entity::ANY);
 
           break;
+        case authorization::CREATE_VOLUME:
+        case authorization::GET_QUOTA:
+        case authorization::RESERVE_RESOURCES:
+        case authorization::UPDATE_QUOTA:
+        case authorization::UPDATE_WEIGHT:
+        case authorization::VIEW_ROLE:
+        case authorization::REGISTER_FRAMEWORK:
+          return Error("Authorization for action " + stringify(action_) +
+                       " requires a specialized approver object.");
         case authorization::UNKNOWN:
           LOG(WARNING) << "Authorization for action '" << action_
                        << "' is not defined and therefore not authorized";
           return false;
-          break;
       }
     }
 
@@ -575,6 +520,207 @@ public:
 };
 
 
+class LocalHierarchicalRoleApprover : public ObjectApprover
+{
+public:
+  LocalHierarchicalRoleApprover(
+      const vector<GenericACL>& acls,
+      const Option<authorization::Subject>& subject,
+      const authorization::Action& action,
+      bool permissive)
+    : acls_(acls), subject_(subject), action_(action), permissive_(permissive)
+  {
+    if (subject_.isSome()) {
+      entitySubject_.set_type(ACL::Entity::SOME);
+      entitySubject_.add_values(subject_->value());
+    } else {
+      entitySubject_.set_type(ACL::Entity::ANY);
+    }
+  }
+
+  virtual Try<bool> approved(const Option<ObjectApprover::Object>& object) const
+      noexcept override
+  {
+    ACL::Entity entityObject;
+
+    if (object.isNone()) {
+      entityObject.set_type(ACL::Entity::ANY);
+    } else {
+      switch (action_) {
+        case authorization::CREATE_VOLUME:
+        case authorization::RESERVE_RESOURCES: {
+          entityObject.set_type(ACL::Entity::SOME);
+          if (object->resource) {
+            entityObject.add_values(object->resource->role());
+          } else if (object->value) {
+            entityObject.add_values(*(object->value));
+          } else {
+            entityObject.set_type(ACL::Entity::ANY);
+          }
+          break;
+        }
+        case authorization::UPDATE_WEIGHT: {
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+          if (object->weight_info) {
+            entityObject.add_values(object->weight_info->role());
+          } else if (object->value) {
+            entityObject.add_values(*(object->value));
+          } else {
+            entityObject.set_type(mesos::ACL::Entity::ANY);
+          }
+
+          break;
+        }
+        case authorization::VIEW_ROLE: {
+          // Check object has the required types set.
+          CHECK_NOTNULL(object->value);
+
+          entityObject.add_values(*(object->value));
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+
+          break;
+        }
+        case authorization::GET_QUOTA: {
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+          if (object->quota_info) {
+            entityObject.add_values(object->quota_info->role());
+          } else if (object->value) {
+            entityObject.add_values(*(object->value));
+          } else {
+            entityObject.set_type(mesos::ACL::Entity::ANY);
+          }
+
+          break;
+        }
+        case authorization::UPDATE_QUOTA: {
+          // Check object has the required types set.
+          CHECK_NOTNULL(object->quota_info);
+
+          entityObject.add_values(object->quota_info->role());
+          entityObject.set_type(mesos::ACL::Entity::SOME);
+
+          break;
+        }
+        case authorization::REGISTER_FRAMEWORK: {
+          vector<ACL::Entity> objects;
+          if (object->framework_info) {
+            foreach (
+                const string& role,
+                protobuf::framework::getRoles(*(object->framework_info))) {
+              objects.emplace_back();
+              objects.back().set_type(mesos::ACL::Entity::SOME);
+              objects.back().add_values(role);
+            }
+          } else if (object->value) {
+            // We also update the deprecated `value` field to support custom
+            // authorizers not yet modified to examine `framework_info`.
+            //
+            // TODO(bbannier): Clean up use of `value` here, see MESOS-7091.
+            objects.emplace_back();
+            objects.back().set_type(mesos::ACL::Entity::SOME);
+            objects.back().add_values(*(object->value));
+          } else {
+            objects.emplace_back();
+            objects.back().set_type(mesos::ACL::Entity::ANY);
+          }
+
+          // The framework needs to be allowed to register under
+          // all the roles it requests.
+          foreach (const ACL::Entity& entity, objects) {
+            if (!approved(acls_, entitySubject_, entity)) {
+              return false;
+            }
+          }
+
+          return objects.empty() ? permissive_ : true;
+        }
+        case authorization::ACCESS_MESOS_LOG:
+        case authorization::ACCESS_SANDBOX:
+        case authorization::ATTACH_CONTAINER_INPUT:
+        case authorization::ATTACH_CONTAINER_OUTPUT:
+        case authorization::DESTROY_VOLUME:
+        case authorization::GET_ENDPOINT_WITH_PATH:
+        case authorization::KILL_NESTED_CONTAINER:
+        case authorization::LAUNCH_NESTED_CONTAINER:
+        case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
+        case authorization::REMOVE_NESTED_CONTAINER:
+        case authorization::RUN_TASK:
+        case authorization::SET_LOG_LEVEL:
+        case authorization::TEARDOWN_FRAMEWORK:
+        case authorization::UNRESERVE_RESOURCES:
+        case authorization::VIEW_CONTAINER:
+        case authorization::VIEW_EXECUTOR:
+        case authorization::VIEW_FLAGS:
+        case authorization::VIEW_FRAMEWORK:
+        case authorization::VIEW_TASK:
+        case authorization::WAIT_NESTED_CONTAINER:
+        case authorization::UNKNOWN:
+          UNREACHABLE();
+      }
+    }
+
+    CHECK(
+        entityObject.type() == ACL::Entity::ANY ||
+        entityObject.values_size() == 1);
+
+    return approved(acls_, entitySubject_, entityObject);
+  }
+
+private:
+  bool approved(
+      const vector<GenericACL>& acls,
+      const ACL::Entity& subject,
+      const ACL::Entity& object) const
+  {
+    // This entity is used for recursive hierarchies where we already
+    // validated that the object role is a nested hierarchy of the
+    // acl role.
+    ACL::Entity aclAny;
+    aclAny.set_type(ACL::Entity::ANY);
+
+    foreach (const GenericACL& acl, acls) {
+      if (!isRecursiveACL(acl)) {
+        // If `acl` is not recursive, treat it as a normal acl.
+        if (matches(subject, acl.subjects) && matches(object, acl.objects)) {
+          return allows(subject, acl.subjects) && allows(object, acl.objects);
+        }
+      } else if (object.type() == ACL::Entity::SOME &&
+          isNestedHierarchy(acl.objects.values(0), object.values(0))) {
+        // Partial validation was done when verifying that the object is
+        // a nested hierarchy.
+        if (matches(subject, acl.subjects) && matches(object, aclAny)) {
+          return allows(subject, acl.subjects) && allows(object, aclAny);
+        }
+      }
+    }
+
+    return permissive_;
+  }
+
+  static bool isRecursiveACL(const GenericACL& acl)
+  {
+    return acl.objects.values_size() == 1 &&
+           strings::endsWith(acl.objects.values(0), "/%");
+  }
+
+  // Returns true if child is a nested hierarchy of parent, i.e. child has more
+  // levels of nesting and all the levels of parent are a prefix of the levels
+  // of child.
+  static bool isNestedHierarchy(const string& parent, const string& child)
+  {
+    // Requires that parent ends with `/%`.
+    CHECK(strings::endsWith(parent, "/%"));
+    return strings::startsWith(child, parent.substr(0, parent.size() - 1));
+  }
+
+  vector<GenericACL> acls_;
+  Option<authorization::Subject> subject_;
+  authorization::Action action_;
+  bool permissive_;
+  ACL::Entity entitySubject_;
+};
+
+
 class LocalAuthorizerProcess : public ProtobufProcess<LocalAuthorizerProcess>
 {
 public:
@@ -630,9 +776,127 @@ public:
       });
   }
 
+  template <typename SomeACL>
+  static vector<GenericACL> createHierarchicalRoleACLs(
+      SomeACL&& someACL)
+  {
+    vector<GenericACL> acls;
+
+    // This may split an ACL into several. This does not change semantics
+    // since the split rules appear consecutively and if an object
+    // matches any of the split ACLs in any order, it will yield the same
+    // results.
+    foreach (auto&& acl, someACL) {
+      switch (acl.roles().type()) {
+        case ACL::Entity::SOME: {
+          ACL::Entity roles;
+          foreach (const string& value, acl.roles().values()) {
+            if (strings::endsWith(value, "/%")) {
+              // Recursive ACLs only have one value in their object list.
+              GenericACL acl_;
+              acl_.subjects = acl.principals();
+              acl_.objects.add_values(value);
+              acls.push_back(acl_);
+            } else {
+              roles.add_values(value);
+            }
+          }
+
+          if (!roles.values().empty()) {
+            GenericACL acl_;
+            acl_.subjects = acl.principals();
+            acl_.objects = roles;
+            acls.push_back(acl_);
+          }
+          break;
+        }
+        case ACL::Entity::ANY:
+        case ACL::Entity::NONE: {
+          GenericACL acl_;
+          acl_.subjects = acl.principals();
+          acl_.objects = acl.roles();
+          acls.push_back(acl_);
+          break;
+        }
+      }
+    }
+
+    return acls;
+  }
+
+  Future<Owned<ObjectApprover>> getHierarchicalRoleApprover(
+      const Option<authorization::Subject>& subject,
+      const authorization::Action& action) const
+  {
+    vector<GenericACL> hierarchicalRoleACLs;
+    switch (action) {
+      case authorization::CREATE_VOLUME: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.create_volumes());
+        break;
+      }
+      case authorization::RESERVE_RESOURCES: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.reserve_resources());
+        break;
+      }
+      case authorization::UPDATE_WEIGHT: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.update_weights());
+        break;
+      }
+      case authorization::VIEW_ROLE: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.view_roles());
+        break;
+      }
+      case authorization::GET_QUOTA: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.get_quotas());
+        break;
+      }
+      case authorization::REGISTER_FRAMEWORK: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.register_frameworks());
+        break;
+      }
+      case authorization::UPDATE_QUOTA: {
+        hierarchicalRoleACLs =
+            createHierarchicalRoleACLs(acls.update_quotas());
+        break;
+      }
+      case authorization::ACCESS_MESOS_LOG:
+      case authorization::ACCESS_SANDBOX:
+      case authorization::ATTACH_CONTAINER_INPUT:
+      case authorization::ATTACH_CONTAINER_OUTPUT:
+      case authorization::DESTROY_VOLUME:
+      case authorization::GET_ENDPOINT_WITH_PATH:
+      case authorization::KILL_NESTED_CONTAINER:
+      case authorization::LAUNCH_NESTED_CONTAINER:
+      case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
+      case authorization::RUN_TASK:
+      case authorization::SET_LOG_LEVEL:
+      case authorization::TEARDOWN_FRAMEWORK:
+      case authorization::UNKNOWN:
+      case authorization::UNRESERVE_RESOURCES:
+      case authorization::VIEW_CONTAINER:
+      case authorization::VIEW_EXECUTOR:
+      case authorization::VIEW_FLAGS:
+      case authorization::VIEW_FRAMEWORK:
+      case authorization::VIEW_TASK:
+      case authorization::WAIT_NESTED_CONTAINER:
+      case authorization::REMOVE_NESTED_CONTAINER:
+        UNREACHABLE();
+    }
+
+    return Owned<ObjectApprover>(
+        new LocalHierarchicalRoleApprover(
+            hierarchicalRoleACLs, subject, action, acls.permissive()));
+  }
+
   Future<Owned<ObjectApprover>> getNestedContainerObjectApprover(
       const Option<authorization::Subject>& subject,
-      const authorization::Action& action)
+      const authorization::Action& action) const
   {
     CHECK(action == authorization::LAUNCH_NESTED_CONTAINER ||
           action == authorization::LAUNCH_NESTED_CONTAINER_SESSION);
@@ -747,24 +1011,56 @@ public:
       return Owned<ObjectApprover>(new RejectingObjectApprover());
     }
 
-    if (action == authorization::LAUNCH_NESTED_CONTAINER ||
-        action == authorization::LAUNCH_NESTED_CONTAINER_SESSION) {
-      return getNestedContainerObjectApprover(subject, action);
-    }
-
-    Result<vector<GenericACL>> genericACLs = createGenericACLs(action, acls);
-    if (genericACLs.isError()) {
-      return Failure(genericACLs.error());
-    }
+    switch (action) {
+      case authorization::LAUNCH_NESTED_CONTAINER:
+      case authorization::LAUNCH_NESTED_CONTAINER_SESSION: {
+        return getNestedContainerObjectApprover(subject, action);
+      }
+      case authorization::CREATE_VOLUME:
+      case authorization::RESERVE_RESOURCES:
+      case authorization::UPDATE_WEIGHT:
+      case authorization::VIEW_ROLE:
+      case authorization::GET_QUOTA:
+      case authorization::REGISTER_FRAMEWORK:
+      case authorization::UPDATE_QUOTA: {
+        return getHierarchicalRoleApprover(subject, action);
+      }
+      case authorization::ACCESS_MESOS_LOG:
+      case authorization::ACCESS_SANDBOX:
+      case authorization::ATTACH_CONTAINER_INPUT:
+      case authorization::ATTACH_CONTAINER_OUTPUT:
+      case authorization::DESTROY_VOLUME:
+      case authorization::GET_ENDPOINT_WITH_PATH:
+      case authorization::KILL_NESTED_CONTAINER:
+      case authorization::RUN_TASK:
+      case authorization::SET_LOG_LEVEL:
+      case authorization::TEARDOWN_FRAMEWORK:
+      case authorization::UNRESERVE_RESOURCES:
+      case authorization::VIEW_CONTAINER:
+      case authorization::VIEW_EXECUTOR:
+      case authorization::VIEW_FLAGS:
+      case authorization::VIEW_FRAMEWORK:
+      case authorization::VIEW_TASK:
+      case authorization::WAIT_NESTED_CONTAINER:
+      case authorization::REMOVE_NESTED_CONTAINER:
+      case authorization::UNKNOWN: {
+        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());
+        }
 
-    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()));
+      }
     }
 
-    return Owned<ObjectApprover>(
-        new LocalAuthorizerObjectApprover(
-            genericACLs.get(), subject, action, acls.permissive()));
+    UNREACHABLE();
   }
 
 private:
@@ -775,17 +1071,6 @@ private:
     vector<GenericACL> acls_;
 
     switch (action) {
-      case authorization::REGISTER_FRAMEWORK:
-        foreach (
-            const ACL::RegisterFramework& acl, acls.register_frameworks()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::TEARDOWN_FRAMEWORK:
         foreach (
             const ACL::TeardownFramework& acl, acls.teardown_frameworks()) {
@@ -807,16 +1092,6 @@ private:
         }
 
         return acls_;
-      case authorization::RESERVE_RESOURCES:
-        foreach (const ACL::ReserveResources& acl, acls.reserve_resources()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::UNRESERVE_RESOURCES:
         foreach (
             const ACL::UnreserveResources& acl, acls.unreserve_resources()) {
@@ -828,16 +1103,6 @@ private:
         }
 
         return acls_;
-      case authorization::CREATE_VOLUME:
-        foreach (const ACL::CreateVolume& acl, acls.create_volumes()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::DESTROY_VOLUME:
         foreach (const ACL::DestroyVolume& acl, acls.destroy_volumes()) {
           GenericACL acl_;
@@ -848,47 +1113,6 @@ private:
         }
 
         return acls_;
-      case authorization::GET_QUOTA:
-        foreach (const ACL::GetQuota& acl, acls.get_quotas()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
-      case authorization::UPDATE_QUOTA: {
-        foreach (const ACL::UpdateQuota& acl, acls.update_quotas()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
-      }
-      case authorization::VIEW_ROLE:
-        foreach (const ACL::ViewRole& acl, acls.view_roles()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
-      case authorization::UPDATE_WEIGHT:
-        foreach (const ACL::UpdateWeight& acl, acls.update_weights()) {
-          GenericACL acl_;
-          acl_.subjects = acl.principals();
-          acl_.objects = acl.roles();
-
-          acls_.push_back(acl_);
-        }
-
-        return acls_;
       case authorization::GET_ENDPOINT_WITH_PATH:
         foreach (const ACL::GetEndpoint& acl, acls.get_endpoints()) {
           GenericACL acl_;
@@ -1034,9 +1258,16 @@ private:
         }
 
         return acls_;
+      case authorization::REGISTER_FRAMEWORK:
+      case authorization::CREATE_VOLUME:
+      case authorization::RESERVE_RESOURCES:
+      case authorization::UPDATE_WEIGHT:
+      case authorization::VIEW_ROLE:
+      case authorization::GET_QUOTA:
+      case authorization::UPDATE_QUOTA:
       case authorization::LAUNCH_NESTED_CONTAINER_SESSION:
       case authorization::LAUNCH_NESTED_CONTAINER:
-        return Error("Extracting ACLs for launching nested containers requires "
+        return Error("Extracting ACLs for " + stringify(action) + " requires "
                      "a specialized function");
       case authorization::UNKNOWN:
         // Cannot generate acls for an unknown action.


[2/2] mesos git commit: Added test for authorization of hierarchical roles.

Posted by me...@apache.org.
Added test for authorization of hierarchical roles.

Adds tests for each of the actions which support hierarchical roles.

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


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

Branch: refs/heads/master
Commit: d55148afbe4c86dc97907abffcbf01dec7e8467a
Parents: d46d330
Author: Alexander Rojas <al...@mesosphere.io>
Authored: Thu Apr 27 00:04:57 2017 -0700
Committer: Adam B <ad...@mesosphere.io>
Committed: Thu Apr 27 00:05:39 2017 -0700

----------------------------------------------------------------------
 src/tests/authorization_tests.cpp | 943 ++++++++++++++++++++++++++++++++-
 1 file changed, 925 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d55148af/src/tests/authorization_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/authorization_tests.cpp b/src/tests/authorization_tests.cpp
index 937a726..b59623f 100644
--- a/src/tests/authorization_tests.cpp
+++ b/src/tests/authorization_tests.cpp
@@ -885,6 +885,135 @@ TYPED_TEST(AuthorizationTest, PrincipalNotOfferedAnyRoleRestrictive)
 }
 
 
+// Checks that the behavior of the authorizer is correct when using
+// hierarchical roles while registering frameworks.
+TYPED_TEST(AuthorizationTest, RegisterFrameworkHierarchical)
+{
+  ACLs acls;
+
+  {
+    // "elizabeth-ii" principal can register frameworks with role
+    // `king` and its nested ones.
+    mesos::ACL::RegisterFramework* acl = acls.add_register_frameworks();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // "charles" principal can register frameworks with just roles
+    // nested under `king`.
+    mesos::ACL::RegisterFramework* acl = acls.add_register_frameworks();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // "j-welby" principal register frameworks with just role 'king'.
+    mesos::ACL::RegisterFramework* acl = acls.add_register_frameworks();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // No other principal can register frameworks in any role.
+    mesos::ACL::RegisterFramework* acl = acls.add_register_frameworks();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  // Create an `Authorizer` with the ACLs.
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  // `elizabeth-ii` has full permissions for the `king` role as well as all
+  // its nested roles. She should be able to register frameworks in the next
+  // three blocks.
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_framework_info()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()
+        ->mutable_framework_info()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()
+        ->mutable_framework_info()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `charles` doesn't have permissions for the `king` role, so the first
+  // test should fail. However he has permissions for `king`'s nested roles
+  // so the next two tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_framework_info()->set_role("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_framework_info()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()
+        ->mutable_framework_info()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `j-welby` only has permissions for the role `king` itself, but not
+  // for its nested roles, therefore only the first of the following three
+  // tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_framework_info()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()
+        ->mutable_framework_info()->set_role("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::REGISTER_FRAMEWORK);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()
+        ->mutable_framework_info()->set_role("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+}
+
 // Tests the authorization of ACLs used for dynamic reservation of resources.
 TYPED_TEST(AuthorizationTest, Reserve)
 {
@@ -906,6 +1035,31 @@ TYPED_TEST(AuthorizationTest, Reserve)
   }
 
   {
+    // Principal "elizabeth-ii" can reserve for the "king" role and its
+    // nested ones.
+    mesos::ACL::ReserveResources* acl = acls.add_reserve_resources();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // Principal "charles" can reserve for any role below the "king/" role.
+    // Not in "king" itself.
+    mesos::ACL::ReserveResources* acl = acls.add_reserve_resources();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // Principal "j-welby" can reserve only for the "king" role but not
+    // in any nested one.
+    mesos::ACL::ReserveResources* acl = acls.add_reserve_resources();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
     // No other principals can reserve resources.
     mesos::ACL::ReserveResources* acl = acls.add_reserve_resources();
     acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
@@ -1025,6 +1179,87 @@ TYPED_TEST(AuthorizationTest, Reserve)
     request.mutable_object()->mutable_resource()->set_role("ads");
     AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
   }
+
+  // `elizabeth-ii` has full permissions for the `king` role as well as all
+  // its nested roles. She should be able to reserve resources in the next
+  // three blocks.
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_resource()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_resource()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_resource()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `charles` doesn't have permissions for the `king` role, so the first
+  // test should fail. However he has permissions for `king`'s nested roles
+  // so the next two tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_resource()->set_role("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_resource()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_resource()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `j-welby` only has permissions for the role `king` itself, but not
+  // for its nested roles, therefore only the first of the following three
+  // tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_resource()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_resource()->set_role("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::RESERVE_RESOURCES);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_resource()->set_role("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
 }
 
 
@@ -1230,6 +1465,38 @@ TYPED_TEST(AuthorizationTest, CreateVolume)
   }
 
   {
+    // Principal "elizabeth-ii" can create volumes for the "king" role and its
+    // nested ones.
+    mesos::ACL::CreateVolume* acl = acls.add_create_volumes();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // Principal "charles" can create volumes for any role below the "king/"
+    // role. Not in "king" itself.
+    mesos::ACL::CreateVolume* acl = acls.add_create_volumes();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // Principal "j-welby" can create volumes only for the "king" role but
+    // not in any nested one.
+    mesos::ACL::CreateVolume* acl = acls.add_create_volumes();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // No other principals can create volumes.
+    mesos::ACL::CreateVolume* acl = acls.add_create_volumes();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  {
     // No other principals can create volumes.
     mesos::ACL::CreateVolume* acl = acls.add_create_volumes();
     acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
@@ -1325,6 +1592,87 @@ TYPED_TEST(AuthorizationTest, CreateVolume)
     request.mutable_object()->mutable_resource()->set_role("panda");
     AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
   }
+
+  // `elizabeth-ii` has full permissions for the `king` role as well as all
+  // its nested roles. She should be able to create volumes in the next
+  // three blocks.
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_resource()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_resource()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_resource()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `charles` doesn't have permissions for the `king` role, so the first
+  // test should fail. However he has permissions for `king`'s nested roles
+  // so the next two tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_resource()->set_role("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_resource()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_resource()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `j-welby` only has permissions for the role `king` itself, but not
+  // for its nested roles, therefore only the first of the following three
+  // tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_resource()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_resource()->set_role("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::CREATE_VOLUME);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_resource()->set_role("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
 }
 
 
@@ -1515,6 +1863,31 @@ TYPED_TEST(AuthorizationTest, UpdateQuota)
   }
 
   {
+    // Principal "elizabeth-ii" can update quotas for the "king" role and its
+    // nested ones.
+    mesos::ACL::UpdateQuota* acl = acls.add_update_quotas();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // Principal "charles" can update quotas for any role below the "king/"
+    // role. Not in "king" itself.
+    mesos::ACL::UpdateQuota* acl = acls.add_update_quotas();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // Principal "j-welby" can update quotas only for the "king" role but
+    // not in any nested one.
+    mesos::ACL::UpdateQuota* acl = acls.add_update_quotas();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
     // No other principal can update quotas.
     mesos::ACL::UpdateQuota* acl = acls.add_update_quotas();
     acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
@@ -1571,32 +1944,116 @@ TYPED_TEST(AuthorizationTest, UpdateQuota)
     request.mutable_object()->mutable_quota_info()->set_role("prod");
     AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
   }
-}
-
-
-// This tests the authorization of requests to ViewFramework.
-TYPED_TEST(AuthorizationTest, ViewFramework)
-{
-  // Setup ACLs.
-  ACLs acls;
 
+  // `elizabeth-ii` has full permissions for the `king` role as well as all
+  // its nested roles. She should be able to update quotas in the next
+  // three blocks.
   {
-    // "foo" principal can view no frameworks.
-    mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
-    acl->mutable_principals()->add_values("foo");
-    acl->mutable_users()->set_type(mesos::ACL::Entity::NONE);
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_quota_info()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
   }
 
   {
-    // "bar" principal can see frameworks running under user "bar".
-    mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
-    acl->mutable_principals()->add_values("bar");
-    acl->mutable_users()->add_values("bar");
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->mutable_quota_info()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
   }
 
   {
-    // "ops" principal can see all frameworks.
-    mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()
+        ->mutable_quota_info()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `charles` doesn't have permissions for the `king` role, so the first
+  // test should fail. However he has permissions for `king`'s nested roles
+  // so the next two tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_quota_info()->set_role("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->mutable_quota_info()->set_role("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()
+        ->mutable_quota_info()->set_role("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `j-welby` only has permissions for the role `king` itself, but not
+  // for its nested roles, therefore only the first of the following three
+  // tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_quota_info()->set_role("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->mutable_quota_info()->set_role("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_QUOTA);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()
+        ->mutable_quota_info()->set_role("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+}
+
+
+// This tests the authorization of requests to ViewFramework.
+TYPED_TEST(AuthorizationTest, ViewFramework)
+{
+  // Setup ACLs.
+  ACLs acls;
+
+  {
+    // "foo" principal can view no frameworks.
+    mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_users()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  {
+    // "bar" principal can see frameworks running under user "bar".
+    mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
+    acl->mutable_principals()->add_values("bar");
+    acl->mutable_users()->add_values("bar");
+  }
+
+  {
+    // "ops" principal can see all frameworks.
+    mesos::ACL::ViewFramework* acl = acls.add_view_frameworks();
     acl->mutable_principals()->add_values("ops");
     acl->mutable_users()->set_type(mesos::ACL::Entity::ANY);
   }
@@ -3941,6 +4398,31 @@ TYPED_TEST(AuthorizationTest, ViewRole)
   }
 
   {
+    // "elizabeth-ii" principal can view info about the `king` role and
+    // all its nested ones.
+    mesos::ACL::ViewRole* acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // "charles" can only view info about `king`'s nested roles, but not
+    // the role `king` itself.
+    mesos::ACL::ViewRole* acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // "j-welby" can only view info about the `king` role itself
+    // but not its nested roles.
+    mesos::ACL::ViewRole* acl = acls.add_view_roles();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
     // No other principal can view any role.
     mesos::ACL::ViewRole* acl = acls.add_view_roles();
     acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
@@ -3980,6 +4462,431 @@ TYPED_TEST(AuthorizationTest, ViewRole)
 
     AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
   }
+
+  // `elizabeth-ii` has full permissions for the `king` role as well as all
+  // its nested roles. She should be able to view roles in the next
+  // three blocks.
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `charles` doesn't have permissions for the `king` role, so the first
+  // test should fail. However he has permissions for `king`'s nested roles
+  // so the next two tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // `j-welby` only has permissions for the role `king` itself, but not
+  // for its nested roles, therefore only the first of the following three
+  // tests should succeed.
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  {
+    authorization::Request request;
+    request.set_action(authorization::VIEW_ROLE);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+}
+
+
+// This tests the authorization of requests to UpdateWeight.
+TYPED_TEST(AuthorizationTest, UpdateWeight)
+{
+  // Setup ACLs.
+  ACLs acls;
+
+  {
+    // "foo" principal can update weights of `ANY` role.
+    mesos::ACL::UpdateWeight* acl = acls.add_update_weights();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+  }
+
+  {
+    // "bar" principal can update the weight of role `bar`.
+    mesos::ACL::UpdateWeight* acl = acls.add_update_weights();
+    acl->mutable_principals()->add_values("bar");
+    acl->mutable_roles()->add_values("bar");
+  }
+
+  {
+    // "elizabeth-ii" principal can update weights of role `king` and
+    // its nested ones.
+    mesos::ACL::UpdateWeight* acl = acls.add_update_weights();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // "charles" principal can update weights of just roles nested
+    // under `king`.
+    mesos::ACL::UpdateWeight* acl = acls.add_update_weights();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // "j-welby" principal can update the weight of just role 'king'.
+    mesos::ACL::UpdateWeight* acl = acls.add_update_weights();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // No other principal can update any weights.
+    mesos::ACL::UpdateWeight* acl = acls.add_update_weights();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  // Create an `Authorizer` with the ACLs.
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  // Check that principal "foo" can update weights of any role.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("foo");
+
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "bar" cannot update the weight of role `foo`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("bar");
+    request.mutable_object()->set_value("foo");
+
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "bar" can update the weight of role `bar`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("bar");
+    request.mutable_object()->set_value("bar");
+
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "elizabeth-ii" can update the weight of role `king`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "elizabeth-ii" can update the weight of role
+  // `king/prince`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "elizabeth-ii" can update the weight of role
+  // `king/prince/duke`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "charles" cannot update the weight of role `king`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "charles" can update the weight of role `king/prince`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "charles" can update the weight of role
+  // `king/prince/duke`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "j-welby" can update the weight of role `king`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "j-welby" cannot update the weight of role
+  // `king/prince`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "j-welby" cannot update the weight of role
+  // `king/prince/duke`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::UPDATE_WEIGHT);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+}
+
+
+// This tests the authorization of requests to GetQuota.
+TYPED_TEST(AuthorizationTest, GetQuota)
+{
+  // Setup ACLs.
+  ACLs acls;
+
+  {
+    // "foo" principal can get quotas of `ANY` role.
+    mesos::ACL::GetQuota* acl = acls.add_get_quotas();
+    acl->mutable_principals()->add_values("foo");
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::ANY);
+  }
+
+  {
+    // "bar" principal can get the quota of role `bar`.
+    mesos::ACL::GetQuota* acl = acls.add_get_quotas();
+    acl->mutable_principals()->add_values("bar");
+    acl->mutable_roles()->add_values("bar");
+  }
+
+  {
+    // "elizabeth-ii" principal can view quotas of role `king` and its
+    // nested ones.
+    mesos::ACL::GetQuota* acl = acls.add_get_quotas();
+    acl->mutable_principals()->add_values("elizabeth-ii");
+    acl->mutable_roles()->add_values("king/%");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // "charles" principal can view quotas of just roles nested under `king`.
+    mesos::ACL::GetQuota* acl = acls.add_get_quotas();
+    acl->mutable_principals()->add_values("charles");
+    acl->mutable_roles()->add_values("king/%");
+  }
+
+  {
+    // "j-welby" principal can view the quotas of just role 'king'.
+    mesos::ACL::GetQuota* acl = acls.add_get_quotas();
+    acl->mutable_principals()->add_values("j-welby");
+    acl->mutable_roles()->add_values("king");
+  }
+
+  {
+    // No other principal can view any quota.
+    mesos::ACL::GetQuota* acl = acls.add_get_quotas();
+    acl->mutable_principals()->set_type(mesos::ACL::Entity::ANY);
+    acl->mutable_roles()->set_type(mesos::ACL::Entity::NONE);
+  }
+
+  // Create an `Authorizer` with the ACLs.
+  Try<Authorizer*> create = TypeParam::create(parameterize(acls));
+  ASSERT_SOME(create);
+  Owned<Authorizer> authorizer(create.get());
+
+  // Check that principal "foo" can view quotas of any role.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("foo");
+
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "bar" cannot view quotas of role `foo`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("bar");
+    request.mutable_object()->set_value("foo");
+
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "bar" can view the quotas of role `bar`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("bar");
+    request.mutable_object()->set_value("bar");
+
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "elizabeth-ii" can view the quotas of role `king`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "elizabeth-ii" can view the quotas of role
+  // `king/prince`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "elizabeth-ii" can view the quota of role
+  // `king/prince/duke`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("elizabeth-ii");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "charles" cannot view the quota of role `king`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "charles" can view the quota of role `king/prince`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "charles" can view the quota of role
+  // `king/prince/duke`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("charles");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "j-welby" can view the quota of role `king`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king");
+    AWAIT_EXPECT_TRUE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "j-welby" cannot view the quota of role
+  // `king/prince`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king/prince");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
+
+  // Check that principal "j-welby" cannot view the quota of role
+  // `king/prince/duke`.
+  {
+    authorization::Request request;
+    request.set_action(authorization::GET_QUOTA);
+    request.mutable_subject()->set_value("j-welby");
+    request.mutable_object()->set_value("king/prince/duke");
+    AWAIT_EXPECT_FALSE(authorizer.get()->authorized(request));
+  }
 }
 
 } // namespace tests {