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