Updated master handlers to use the 'Principal' type. This patch updates the HTTP endpoint handlers in the master process to accept the `Principal` type instead of an `Option<string>& principal`.
Review: https://reviews.apache.org/r/56813/ Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/da47646e Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/da47646e Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/da47646e Branch: refs/heads/master Commit: da47646e22d5294ce48de57424bfa9b6562a6896 Parents: 8da4d6f Author: Greg Mann <[email protected]> Authored: Mon Mar 6 12:39:45 2017 -0800 Committer: Vinod Kone <[email protected]> Committed: Mon Mar 6 12:39:45 2017 -0800 ---------------------------------------------------------------------- src/master/http.cpp | 363 ++++++++++++++++++++++++------------ src/master/master.cpp | 155 +++++++-------- src/master/master.hpp | 192 +++++++++++-------- src/master/quota_handler.cpp | 56 +++--- src/master/registrar.cpp | 6 +- src/master/weights_handler.cpp | 34 ++-- 6 files changed, 497 insertions(+), 309 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/da47646e/src/master/http.cpp ---------------------------------------------------------------------- diff --git a/src/master/http.cpp b/src/master/http.cpp index b0b73e3..713ee0f 100644 --- a/src/master/http.cpp +++ b/src/master/http.cpp @@ -112,6 +112,8 @@ using process::http::TemporaryRedirect; using process::http::UnsupportedMediaType; using process::http::URL; +using process::http::authentication::Principal; + using std::copy_if; using std::list; using std::map; @@ -124,6 +126,8 @@ using std::vector; namespace mesos { +using mesos::authorization::createSubject; + static void json( JSON::StringWriter* writer, const FrameworkInfo::Capability& capability) @@ -469,8 +473,17 @@ string Master::Http::API_HELP() Future<Response> Master::Http::api( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // TODO(vinod): Add metrics for rejected requests. // TODO(vinod): Add support for rate limiting. @@ -652,7 +665,7 @@ Future<Response> Master::Http::api( Future<Response> Master::Http::subscribe( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::SUBSCRIBE, call.type()); @@ -662,11 +675,7 @@ Future<Response> Master::Http::subscribe( Future<Owned<ObjectApprover>> tasksApprover; Future<Owned<ObjectApprover>> executorsApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -741,8 +750,17 @@ string Master::Http::SCHEDULER_HELP() Future<Response> Master::Http::scheduler( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // TODO(vinod): Add metrics for rejected requests. // TODO(vinod): Add support for rate limiting. @@ -833,20 +851,24 @@ Future<Response> Master::Http::scheduler( const FrameworkInfo& frameworkInfo = call.subscribe().framework_info(); + // We allow an authenticated framework to not specify a principal in + // `FrameworkInfo`, but in that case we log a WARNING here. We also set + // `FrameworkInfo.principal` to the value of the authenticated principal + // and use it for authorization later. + // + // NOTE: Common validation code, called previously, verifies that the + // authenticated principal is the same as `FrameworkInfo.principal`, + // if present. if (principal.isSome() && !frameworkInfo.has_principal()) { - // We allow an authenticated framework to not specify a principal - // in `FrameworkInfo` but we'd prefer to log a WARNING here. We also - // set `FrameworkInfo.principal` to the value of authenticated principal - // and use it for authorization later when it happens. - if (!frameworkInfo.has_principal()) { - LOG(WARNING) - << "Setting 'principal' in FrameworkInfo to '" << principal.get() - << "' because the framework authenticated with that principal but " - << "did not set it in FrameworkInfo"; - - call.mutable_subscribe()->mutable_framework_info()->set_principal( - principal.get()); - } + CHECK_SOME(principal->value); + + LOG(WARNING) + << "Setting 'principal' in FrameworkInfo to '" << principal->value.get() + << "' because the framework authenticated with that principal but " + << "did not set it in FrameworkInfo"; + + call.mutable_subscribe()->mutable_framework_info()->set_principal( + principal->value.get()); } Pipe pipe; @@ -876,7 +898,7 @@ Future<Response> Master::Http::scheduler( if (principal.isSome() && principal != framework->info.principal()) { return BadRequest( - "Authenticated principal '" + principal.get() + "' does not " + "Authenticated principal '" + stringify(principal.get()) + "' does not " "match principal '" + framework->info.principal() + "' set in " "`FrameworkInfo`"); } @@ -1012,8 +1034,17 @@ string Master::Http::CREATE_VOLUMES_HELP() Future<Response> Master::Http::createVolumes( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -1083,7 +1114,7 @@ Future<Response> Master::Http::createVolumes( Future<Response> Master::Http::_createVolumes( const SlaveID& slaveId, const RepeatedPtrField<Resource>& volumes, - const Option<string>& principal) const + const Option<Principal>& principal) const { Slave* slave = master->slaves.registered.get(slaveId); if (slave == nullptr) { @@ -1118,9 +1149,18 @@ Future<Response> Master::Http::_createVolumes( Future<Response> Master::Http::createVolumes( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType /*contentType*/) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + CHECK_EQ(mesos::master::Call::CREATE_VOLUMES, call.type()); CHECK(call.has_create_volumes()); @@ -1161,8 +1201,17 @@ string Master::Http::DESTROY_VOLUMES_HELP() Future<Response> Master::Http::destroyVolumes( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -1232,7 +1281,7 @@ Future<Response> Master::Http::destroyVolumes( Future<Response> Master::Http::_destroyVolumes( const SlaveID& slaveId, const RepeatedPtrField<Resource>& volumes, - const Option<string>& principal) const + const Option<Principal>& principal) const { Slave* slave = master->slaves.registered.get(slaveId); if (slave == nullptr) { @@ -1267,9 +1316,18 @@ Future<Response> Master::Http::_destroyVolumes( Future<Response> Master::Http::destroyVolumes( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType /*contentType*/) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + CHECK_EQ(mesos::master::Call::DESTROY_VOLUMES, call.type()); CHECK(call.has_destroy_volumes()); @@ -1299,8 +1357,17 @@ string Master::Http::FRAMEWORKS_HELP() Future<Response> Master::Http::frameworks( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -1313,11 +1380,7 @@ Future<Response> Master::Http::frameworks( Future<Owned<ObjectApprover>> executorsApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -1469,7 +1532,7 @@ mesos::master::Response::GetFrameworks::Framework model( Future<Response> Master::Http::getFrameworks( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_FRAMEWORKS, call.type()); @@ -1478,15 +1541,10 @@ Future<Response> Master::Http::getFrameworks( Future<Owned<ObjectApprover>> frameworksApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); - } else { frameworksApprover = Owned<ObjectApprover>(new AcceptingObjectApprover()); } @@ -1536,7 +1594,7 @@ mesos::master::Response::GetFrameworks Master::Http::_getFrameworks( Future<Response> Master::Http::getExecutors( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_EXECUTORS, call.type()); @@ -1545,11 +1603,7 @@ Future<Response> Master::Http::getExecutors( Future<Owned<ObjectApprover>> frameworksApprover; Future<Owned<ObjectApprover>> executorsApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -1667,7 +1721,7 @@ mesos::master::Response::GetExecutors Master::Http::_getExecutors( Future<Response> Master::Http::getState( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_STATE, call.type()); @@ -1677,11 +1731,7 @@ Future<Response> Master::Http::getState( Future<Owned<ObjectApprover>> tasksApprover; Future<Owned<ObjectApprover>> executorsApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -1784,8 +1834,17 @@ string Master::Http::FLAGS_HELP() Future<Response> Master::Http::flags( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // TODO(nfnt): Remove check for enabled // authorization as part of MESOS-5346. if (request.method != "GET" && master->authorizer.isSome()) { @@ -1812,7 +1871,7 @@ Future<Response> Master::Http::flags( Future<Try<JSON::Object, Master::Http::FlagsError>> Master::Http::_flags( - const Option<string>& principal) const + const Option<Principal>& principal) const { if (master->authorizer.isNone()) { return __flags(); @@ -1821,8 +1880,9 @@ Future<Try<JSON::Object, Master::Http::FlagsError>> Master::Http::_flags( authorization::Request authRequest; authRequest.set_action(authorization::VIEW_FLAGS); - if (principal.isSome()) { - authRequest.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + authRequest.mutable_subject()->CopyFrom(subject.get()); } return master->authorizer.get()->authorized(authRequest) @@ -1859,7 +1919,7 @@ JSON::Object Master::Http::__flags() const Future<Response> Master::Http::getFlags( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_FLAGS, call.type()); @@ -1904,7 +1964,7 @@ Future<Response> Master::Http::health(const Request& request) const Future<Response> Master::Http::getHealth( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_HEALTH, call.type()); @@ -1920,7 +1980,7 @@ Future<Response> Master::Http::getHealth( Future<Response> Master::Http::getVersion( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_VERSION, call.type()); @@ -1933,7 +1993,7 @@ Future<Response> Master::Http::getVersion( Future<Response> Master::Http::getMetrics( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_METRICS, call.type()); @@ -1965,7 +2025,7 @@ Future<Response> Master::Http::getMetrics( Future<Response> Master::Http::getLoggingLevel( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_LOGGING_LEVEL, call.type()); @@ -1981,7 +2041,7 @@ Future<Response> Master::Http::getLoggingLevel( Future<Response> Master::Http::setLoggingLevel( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType /*contentType*/) const { CHECK_EQ(mesos::master::Call::SET_LOGGING_LEVEL, call.type()); @@ -2000,7 +2060,7 @@ Future<Response> Master::Http::setLoggingLevel( Future<Response> Master::Http::getMaster( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_MASTER, call.type()); @@ -2125,8 +2185,17 @@ string Master::Http::RESERVE_HELP() Future<Response> Master::Http::reserve( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -2197,7 +2266,7 @@ Future<Response> Master::Http::reserve( Future<Response> Master::Http::_reserve( const SlaveID& slaveId, const Resources& resources, - const Option<string>& principal) const + const Option<Principal>& principal) const { Slave* slave = master->slaves.registered.get(slaveId); if (slave == nullptr) { @@ -2233,7 +2302,7 @@ Future<Response> Master::Http::_reserve( Future<Response> Master::Http::reserveResources( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::RESERVE_RESOURCES, call.type()); @@ -2265,7 +2334,7 @@ string Master::Http::SLAVES_HELP() Future<Response> Master::Http::slaves( const Request& request, - const Option<string>& /*principal*/) const + const Option<Principal>&) const { // When current master is not the leader, redirect to the leading master. if (!master->elected()) { @@ -2341,7 +2410,7 @@ Future<Response> Master::Http::slaves( Future<process::http::Response> Master::Http::getAgents( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_AGENTS, call.type()); @@ -2404,8 +2473,17 @@ string Master::Http::QUOTA_HELP() Future<Response> Master::Http::quota( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -2455,8 +2533,17 @@ string Master::Http::WEIGHTS_HELP() Future<Response> Master::Http::weights( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -2571,8 +2658,17 @@ string Master::Http::STATE_HELP() Future<Response> Master::Http::state( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -2585,11 +2681,7 @@ Future<Response> Master::Http::state( Future<Owned<ObjectApprover>> flagsApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -2815,7 +2907,7 @@ Future<Response> Master::Http::state( Future<Response> Master::Http::readFile( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::READ_FILE, call.type()); @@ -3065,8 +3157,17 @@ string Master::Http::STATESUMMARY_HELP() Future<Response> Master::Http::stateSummary( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -3075,11 +3176,7 @@ Future<Response> Master::Http::stateSummary( Future<Owned<ObjectApprover>> frameworksApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -3279,21 +3376,16 @@ string Master::Http::ROLES_HELP() Future<vector<string>> Master::Http::_roles( - const Option<string>& principal) const + const Option<Principal>& principal) const { // Retrieve `ObjectApprover`s for authorizing roles. Future<Owned<ObjectApprover>> rolesApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); rolesApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_ROLE); - } else { rolesApprover = Owned<ObjectApprover>(new AcceptingObjectApprover()); } @@ -3346,8 +3438,17 @@ Future<vector<string>> Master::Http::_roles( Future<Response> Master::Http::roles( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -3386,7 +3487,7 @@ Future<Response> Master::Http::roles( Future<Response> Master::Http::listFiles( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::LIST_FILES, call.type()); @@ -3438,7 +3539,7 @@ Future<Response> Master::Http::listFiles( // convert back into a `Resource` object. Future<Response> Master::Http::getRoles( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_ROLES, call.type()); @@ -3508,8 +3609,17 @@ string Master::Http::TEARDOWN_HELP() Future<Response> Master::Http::teardown( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -3553,8 +3663,9 @@ Future<Response> Master::Http::teardown( authorization::Request teardown; teardown.set_action(authorization::TEARDOWN_FRAMEWORK); - if (principal.isSome()) { - teardown.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + teardown.mutable_subject()->CopyFrom(subject.get()); } if (framework->info.has_principal()) { @@ -3666,8 +3777,17 @@ string Master::Http::TASKS_HELP() Future<Response> Master::Http::tasks( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -3687,11 +3807,7 @@ Future<Response> Master::Http::tasks( Future<Owned<ObjectApprover>> frameworksApprover; Future<Owned<ObjectApprover>> tasksApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -3793,7 +3909,7 @@ Future<Response> Master::Http::tasks( Future<Response> Master::Http::getTasks( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_TASKS, call.type()); @@ -3802,11 +3918,7 @@ Future<Response> Master::Http::getTasks( Future<Owned<ObjectApprover>> frameworksApprover; Future<Owned<ObjectApprover>> tasksApprover; if (master->authorizer.isSome()) { - Option<authorization::Subject> subject; - if (principal.isSome()) { - subject = authorization::Subject(); - subject->set_value(principal.get()); - } + Option<authorization::Subject> subject = createSubject(principal); frameworksApprover = master->authorizer.get()->getObjectApprover( subject, authorization::VIEW_FRAMEWORK); @@ -3969,7 +4081,7 @@ string Master::Http::MAINTENANCE_SCHEDULE_HELP() // /master/maintenance/schedule endpoint handler. Future<Response> Master::Http::maintenanceSchedule( const Request& request, - const Option<string>& /*principal*/) const + const Option<Principal>&) const { // When current master is not the leader, redirect to the leading master. if (!master->elected()) { @@ -4108,7 +4220,7 @@ Future<Response> Master::Http::_updateMaintenanceSchedule( Future<Response> Master::Http::getMaintenanceSchedule( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_MAINTENANCE_SCHEDULE, call.type()); @@ -4125,7 +4237,7 @@ Future<Response> Master::Http::getMaintenanceSchedule( Future<Response> Master::Http::updateMaintenanceSchedule( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType /*contentType*/) const { CHECK_EQ(mesos::master::Call::UPDATE_MAINTENANCE_SCHEDULE, call.type()); @@ -4160,7 +4272,7 @@ string Master::Http::MACHINE_DOWN_HELP() // /master/machine/down endpoint handler. Future<Response> Master::Http::machineDown( const Request& request, - const Option<string>& /*principal*/) const + const Option<Principal>&) const { // When current master is not the leader, redirect to the leading master. if (!master->elected()) { @@ -4262,7 +4374,7 @@ Future<Response> Master::Http::_startMaintenance( Future<Response> Master::Http::startMaintenance( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType /*contentType*/) const { CHECK_EQ(mesos::master::Call::START_MAINTENANCE, call.type()); @@ -4296,7 +4408,7 @@ string Master::Http::MACHINE_UP_HELP() // /master/machine/up endpoint handler. Future<Response> Master::Http::machineUp( const Request& request, - const Option<string>& /*principal*/) const + const Option<Principal>&) const { // When current master is not the leader, redirect to the leading master. if (!master->elected()) { @@ -4397,7 +4509,7 @@ Future<Response> Master::Http::_stopMaintenance( Future<Response> Master::Http::stopMaintenance( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType /*contentType*/) const { CHECK_EQ(mesos::master::Call::STOP_MAINTENANCE, call.type()); @@ -4434,7 +4546,7 @@ string Master::Http::MAINTENANCE_STATUS_HELP() // /master/maintenance/status endpoint handler. Future<Response> Master::Http::maintenanceStatus( const Request& request, - const Option<string>& /*principal*/) const + const Option<Principal>&) const { // When current master is not the leader, redirect to the leading master. if (!master->elected()) { @@ -4511,7 +4623,7 @@ Future<mesos::maintenance::ClusterStatus> Future<Response> Master::Http::getMaintenanceStatus( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_MAINTENANCE_STATUS, call.type()); @@ -4560,8 +4672,17 @@ string Master::Http::UNRESERVE_HELP() Future<Response> Master::Http::unreserve( const Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { + // TODO(greggomann): Remove this check once the `Principal` type is used in + // `ReservationInfo`, `DiskInfo`, and within the master's `principals` map. + // See MESOS-7202. + if (principal.isSome() && principal->value.isNone()) { + return Forbidden( + "The request's authenticated principal contains claims, but no value " + "string. The master currently requires that principals have a value"); + } + // When current master is not the leader, redirect to the leading master. if (!master->elected()) { return redirect(request); @@ -4632,7 +4753,7 @@ Future<Response> Master::Http::unreserve( Future<Response> Master::Http::_unreserve( const SlaveID& slaveId, const Resources& resources, - const Option<string>& principal) const + const Option<Principal>& principal) const { Slave* slave = master->slaves.registered.get(slaveId); if (slave == nullptr) { @@ -4724,7 +4845,7 @@ Future<Response> Master::Http::_operation( Future<Response> Master::Http::unreserveResources( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::UNRESERVE_RESOURCES, call.type()); http://git-wip-us.apache.org/repos/asf/mesos/blob/da47646e/src/master/master.cpp ---------------------------------------------------------------------- diff --git a/src/master/master.cpp b/src/master/master.cpp index a15c6d8..3fa5438 100644 --- a/src/master/master.cpp +++ b/src/master/master.cpp @@ -118,6 +118,8 @@ using process::UPID; using process::http::Pipe; +using process::http::authentication::Principal; + using process::metrics::Counter; namespace mesos { @@ -126,6 +128,8 @@ namespace master { using mesos::allocator::Allocator; +using mesos::authorization::createSubject; + using mesos::master::contender::MasterContender; using mesos::master::detector::MasterDetector; @@ -856,7 +860,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::API_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.api(request, principal); }); @@ -864,7 +868,7 @@ void Master::initialize() DEFAULT_HTTP_FRAMEWORK_AUTHENTICATION_REALM, Http::SCHEDULER_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.scheduler(request, principal); }); @@ -872,7 +876,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::CREATE_VOLUMES_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.createVolumes(request, principal); }); @@ -880,7 +884,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::DESTROY_VOLUMES_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.destroyVolumes(request, principal); }); @@ -888,7 +892,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::FRAMEWORKS_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.frameworks(request, principal); }); @@ -896,7 +900,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::FLAGS_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.flags(request, principal); }); @@ -914,7 +918,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::RESERVE_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.reserve(request, principal); }); @@ -924,7 +928,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::ROLES_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.roles(request, principal); }); @@ -932,7 +936,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::ROLES_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.roles(request, principal); }); @@ -940,7 +944,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::TEARDOWN_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.teardown(request, principal); }); @@ -948,7 +952,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::SLAVES_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.slaves(request, principal); }); @@ -958,7 +962,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::STATE_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.state(request, principal); }); @@ -966,7 +970,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::STATE_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.state(request, principal); }); @@ -974,7 +978,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::STATESUMMARY_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.stateSummary(request, principal); }); @@ -984,7 +988,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::TASKS_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.tasks(request, principal); }); @@ -992,7 +996,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::TASKS_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.tasks(request, principal); }); @@ -1000,7 +1004,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::MAINTENANCE_SCHEDULE_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.maintenanceSchedule(request, principal); }); @@ -1008,7 +1012,7 @@ void Master::initialize() READONLY_HTTP_AUTHENTICATION_REALM, Http::MAINTENANCE_STATUS_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.maintenanceStatus(request, principal); }); @@ -1016,7 +1020,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::MACHINE_DOWN_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.machineDown(request, principal); }); @@ -1024,7 +1028,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::MACHINE_UP_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.machineUp(request, principal); }); @@ -1032,7 +1036,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::UNRESERVE_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.unreserve(request, principal); }); @@ -1040,7 +1044,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::QUOTA_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.quota(request, principal); }); @@ -1048,7 +1052,7 @@ void Master::initialize() READWRITE_HTTP_AUTHENTICATION_REALM, Http::WEIGHTS_HELP(), [this](const process::http::Request& request, - const Option<string>& principal) { + const Option<Principal>& principal) { Http::log(request); return http.weights(request, principal); }); @@ -1063,7 +1067,7 @@ void Master::initialize() const PID<Master> masterPid = self(); - auto authorize = [masterPid](const Option<string>& principal) { + auto authorize = [masterPid](const Option<Principal>& principal) { return dispatch(masterPid, &Master::authorizeLogAccess, principal); }; @@ -1405,7 +1409,7 @@ void Master::_exited(Framework* framework) } -Future<bool> Master::authorizeLogAccess(const Option<string>& principal) +Future<bool> Master::authorizeLogAccess(const Option<Principal>& principal) { if (authorizer.isNone()) { return true; @@ -1414,8 +1418,9 @@ Future<bool> Master::authorizeLogAccess(const Option<string>& principal) authorization::Request request; request.set_action(authorization::ACCESS_MESOS_LOG); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } return authorizer.get()->authorized(request); @@ -1558,6 +1563,8 @@ void Master::visit(const ExitedEvent& event) } +// TODO(greggomann): Change this to accept an `Option<Principal>` +// when MESOS-7202 is resolved. void Master::throttled( const MessageEvent& event, const Option<string>& principal) @@ -1600,6 +1607,8 @@ void Master::_visit(const MessageEvent& event) } +// TODO(greggomann): Change this to accept an `Option<Principal>` +// when MESOS-7202 is resolved. void Master::exceededCapacity( const MessageEvent& event, const Option<string>& principal, @@ -3411,7 +3420,7 @@ Future<bool> Master::authorizeTask( Future<bool> Master::authorizeReserveResources( const Offer::Operation::Reserve& reserve, - const Option<string>& principal) + const Option<Principal>& principal) { if (authorizer.isNone()) { return true; // Authorization is disabled. @@ -3420,11 +3429,12 @@ Future<bool> Master::authorizeReserveResources( authorization::Request request; request.set_action(authorization::RESERVE_RESOURCES); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } - // The operation will be authorized if the principal is allowed to make + // The operation will be authorized if the entity is allowed to make // reservations for all roles included in `reserve.resources`. // Add an element to `request.roles` for each unique role in the resources. hashset<string> roles; @@ -3440,7 +3450,7 @@ Future<bool> Master::authorizeReserveResources( } LOG(INFO) << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") + << (principal.isSome() ? stringify(principal.get()) : "ANY") << "' to reserve resources '" << reserve.resources() << "'"; // NOTE: Empty authorizations are not valid and are checked by a validator. @@ -3468,7 +3478,7 @@ Future<bool> Master::authorizeReserveResources( Future<bool> Master::authorizeUnreserveResources( const Offer::Operation::Unreserve& unreserve, - const Option<string>& principal) + const Option<Principal>& principal) { if (authorizer.isNone()) { return true; // Authorization is disabled. @@ -3477,8 +3487,9 @@ Future<bool> Master::authorizeUnreserveResources( authorization::Request request; request.set_action(authorization::UNRESERVE_RESOURCES); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } list<Future<bool>> authorizations; @@ -3498,10 +3509,9 @@ Future<bool> Master::authorizeUnreserveResources( } } - LOG(INFO) - << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") - << "' to unreserve resources '" << unreserve.resources() << "'"; + LOG(INFO) << "Authorizing principal '" + << (principal.isSome() ? stringify(principal.get()) : "ANY") + << "' to unreserve resources '" << unreserve.resources() << "'"; if (authorizations.empty()) { return authorizer.get()->authorized(request); @@ -3523,7 +3533,7 @@ Future<bool> Master::authorizeUnreserveResources( Future<bool> Master::authorizeCreateVolume( const Offer::Operation::Create& create, - const Option<string>& principal) + const Option<Principal>& principal) { if (authorizer.isNone()) { return true; // Authorization is disabled. @@ -3532,11 +3542,12 @@ Future<bool> Master::authorizeCreateVolume( authorization::Request request; request.set_action(authorization::CREATE_VOLUME); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } - // The operation will be authorized if the principal is allowed to create + // The operation will be authorized if the entity is allowed to create // volumes for all roles included in `create.volumes`. // Add an element to `request.roles` for each unique role in the volumes. hashset<string> roles; @@ -3551,10 +3562,9 @@ Future<bool> Master::authorizeCreateVolume( } } - LOG(INFO) - << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") - << "' to create volumes"; + LOG(INFO) << "Authorizing principal '" + << (principal.isSome() ? stringify(principal.get()) : "ANY") + << "' to create volumes '" << create.volumes() << "'"; if (authorizations.empty()) { return authorizer.get()->authorized(request); @@ -3576,7 +3586,7 @@ Future<bool> Master::authorizeCreateVolume( Future<bool> Master::authorizeDestroyVolume( const Offer::Operation::Destroy& destroy, - const Option<string>& principal) + const Option<Principal>& principal) { if (authorizer.isNone()) { return true; // Authorization is disabled. @@ -3585,8 +3595,9 @@ Future<bool> Master::authorizeDestroyVolume( authorization::Request request; request.set_action(authorization::DESTROY_VOLUME); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } list<Future<bool>> authorizations; @@ -3603,11 +3614,9 @@ Future<bool> Master::authorizeDestroyVolume( } } - LOG(INFO) - << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") - << "' to destroy volumes '" - << stringify(destroy.volumes()) << "'"; + LOG(INFO) << "Authorizing principal '" + << (principal.isSome() ? stringify(principal.get()) : "ANY") + << "' to destroy volumes '" << destroy.volumes() << "'"; if (authorizations.empty()) { return authorizer.get()->authorized(request); @@ -3876,9 +3885,9 @@ void Master::accept( // The RESERVE operation allows a principal to reserve resources. case Offer::Operation::RESERVE: { - Option<string> principal = framework->info.has_principal() - ? framework->info.principal() - : Option<string>::none(); + Option<Principal> principal = framework->info.has_principal() + ? Principal(framework->info.principal()) + : Option<Principal>::none(); futures.push_back( authorizeReserveResources( @@ -3889,9 +3898,9 @@ void Master::accept( // The UNRESERVE operation allows a principal to unreserve resources. case Offer::Operation::UNRESERVE: { - Option<string> principal = framework->info.has_principal() - ? framework->info.principal() - : Option<string>::none(); + Option<Principal> principal = framework->info.has_principal() + ? Principal(framework->info.principal()) + : Option<Principal>::none(); futures.push_back( authorizeUnreserveResources( @@ -3902,9 +3911,9 @@ void Master::accept( // The CREATE operation allows the creation of a persistent volume. case Offer::Operation::CREATE: { - Option<string> principal = framework->info.has_principal() - ? framework->info.principal() - : Option<string>::none(); + Option<Principal> principal = framework->info.has_principal() + ? Principal(framework->info.principal()) + : Option<Principal>::none(); futures.push_back( authorizeCreateVolume( @@ -3915,9 +3924,9 @@ void Master::accept( // The DESTROY operation allows the destruction of a persistent volume. case Offer::Operation::DESTROY: { - Option<string> principal = framework->info.has_principal() - ? framework->info.principal() - : Option<string>::none(); + Option<Principal> principal = framework->info.has_principal() + ? Principal(framework->info.principal()) + : Option<Principal>::none(); futures.push_back( authorizeDestroyVolume( @@ -4100,9 +4109,9 @@ void Master::_accept( continue; } - Option<string> principal = framework->info.has_principal() - ? framework->info.principal() - : Option<string>::none(); + Option<Principal> principal = framework->info.has_principal() + ? Principal(framework->info.principal()) + : Option<Principal>::none(); // Make sure this reserve operation is valid. Option<Error> error = validation::operation::validate( @@ -4213,9 +4222,9 @@ void Master::_accept( continue; } - Option<string> principal = framework->info.has_principal() ? - framework->info.principal() : - Option<string>::none(); + Option<Principal> principal = framework->info.has_principal() + ? Principal(framework->info.principal()) + : Option<Principal>::none(); // Make sure this create operation is valid. Option<Error> error = validation::operation::validate( http://git-wip-us.apache.org/repos/asf/mesos/blob/da47646e/src/master/master.hpp ---------------------------------------------------------------------- diff --git a/src/master/master.hpp b/src/master/master.hpp index 81320e0..47c5e61 100644 --- a/src/master/master.hpp +++ b/src/master/master.hpp @@ -680,7 +680,7 @@ protected: */ process::Future<bool> authorizeReserveResources( const Offer::Operation::Reserve& reserve, - const Option<std::string>& principal); + const Option<process::http::authentication::Principal>& principal); /** * Authorizes an `UNRESERVE` offer operation. @@ -701,7 +701,7 @@ protected: */ process::Future<bool> authorizeUnreserveResources( const Offer::Operation::Unreserve& unreserve, - const Option<std::string>& principal); + const Option<process::http::authentication::Principal>& principal); /** * Authorizes a `CREATE` offer operation. @@ -722,7 +722,7 @@ protected: */ process::Future<bool> authorizeCreateVolume( const Offer::Operation::Create& create, - const Option<std::string>& principal); + const Option<process::http::authentication::Principal>& principal); /** * Authorizes a `DESTROY` offer operation. @@ -743,7 +743,7 @@ protected: */ process::Future<bool> authorizeDestroyVolume( const Offer::Operation::Destroy& destroy, - const Option<std::string>& principal); + const Option<process::http::authentication::Principal>& principal); // Add the task and its executor (if not already running) to the // framework and slave. Returns the resources consumed as a result, @@ -931,7 +931,7 @@ private: const process::Future<bool>& registrarResult); process::Future<bool> authorizeLogAccess( - const Option<std::string>& principal); + const Option<process::http::authentication::Principal>& principal); /** * Returns whether the given role is on the whitelist. @@ -976,28 +976,33 @@ private: // Returns a list of set quotas. process::Future<process::http::Response> status( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> status( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> set( const mesos::master::Call& call, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> set( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> remove( const mesos::master::Call& call, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> remove( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; private: // Heuristically tries to determine whether a quota request could @@ -1045,7 +1050,7 @@ private: void rescindOffers(const mesos::quota::QuotaInfo& request) const; process::Future<bool> authorizeGetQuota( - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, const mesos::quota::QuotaInfo& quotaInfo) const; // TODO(mpark): The following functions `authorizeSetQuota` and @@ -1053,19 +1058,21 @@ private: // the end of deprecation cycle which started with 1.0. process::Future<bool> authorizeSetQuota( - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, const mesos::quota::QuotaInfo& quotaInfo) const; process::Future<bool> authorizeRemoveQuota( - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, const mesos::quota::QuotaInfo& quotaInfo) const; process::Future<mesos::quota::QuotaStatus> _status( - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> _set( const mesos::quota::QuotaRequest& quotaRequest, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> __set( const mesos::quota::QuotaInfo& quotaInfo, @@ -1073,7 +1080,8 @@ private: process::Future<process::http::Response> _remove( const std::string& role, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> __remove( const std::string& role) const; @@ -1101,29 +1109,31 @@ private: process::Future<process::http::Response> get( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> get( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> update( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> update( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; private: process::Future<bool> authorizeGetWeight( - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, const WeightInfo& weight) const; process::Future<bool> authorizeUpdateWeights( - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, const std::vector<std::string>& roles) const; process::Future<std::vector<WeightInfo>> _filterWeights( @@ -1131,10 +1141,11 @@ private: const std::list<bool>& roleAuthorizations) const; process::Future<std::vector<WeightInfo>> _getWeights( - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response>_updateWeights( - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, const google::protobuf::RepeatedPtrField<WeightInfo>& weightInfos) const; @@ -1164,32 +1175,38 @@ private: // /api/v1 process::Future<process::http::Response> api( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /api/v1/scheduler process::Future<process::http::Response> scheduler( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/create-volumes process::Future<process::http::Response> createVolumes( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/destroy-volumes process::Future<process::http::Response> destroyVolumes( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/flags process::Future<process::http::Response> flags( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/frameworks process::Future<process::http::Response> frameworks( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/health process::Future<process::http::Response> health( @@ -1202,72 +1219,86 @@ private: // /master/reserve process::Future<process::http::Response> reserve( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/roles process::Future<process::http::Response> roles( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/teardown process::Future<process::http::Response> teardown( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/slaves process::Future<process::http::Response> slaves( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/state process::Future<process::http::Response> state( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/state-summary process::Future<process::http::Response> stateSummary( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/tasks process::Future<process::http::Response> tasks( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/maintenance/schedule process::Future<process::http::Response> maintenanceSchedule( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/maintenance/status process::Future<process::http::Response> maintenanceStatus( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/machine/down process::Future<process::http::Response> machineDown( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/machine/up process::Future<process::http::Response> machineUp( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/unreserve process::Future<process::http::Response> unreserve( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/quota process::Future<process::http::Response> quota( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // /master/weights process::Future<process::http::Response> weights( const process::http::Request& request, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; static std::string API_HELP(); static std::string SCHEDULER_HELP(); @@ -1298,13 +1329,15 @@ private: class FlagsError; // Forward declaration. process::Future<Try<JSON::Object, FlagsError>> _flags( - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<std::vector<const Task*>> _tasks( const size_t limit, const size_t offset, const std::string& order, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> _teardown( const FrameworkID& id) const; @@ -1326,22 +1359,26 @@ private: process::Future<process::http::Response> _reserve( const SlaveID& slaveId, const Resources& resources, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> _unreserve( const SlaveID& slaveId, const Resources& resources, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> _createVolumes( const SlaveID& slaveId, const google::protobuf::RepeatedPtrField<Resource>& volumes, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; process::Future<process::http::Response> _destroyVolumes( const SlaveID& slaveId, const google::protobuf::RepeatedPtrField<Resource>& volumes, - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; /** * Continuation for operations: /reserve, /unreserve, @@ -1368,90 +1405,91 @@ private: const Offer::Operation& operation) const; process::Future<std::vector<std::string>> _roles( - const Option<std::string>& principal) const; + const Option<process::http::authentication::Principal>& + principal) const; // Master API handlers. process::Future<process::http::Response> getAgents( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; mesos::master::Response::GetAgents _getAgents() const; process::Future<process::http::Response> getFlags( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getHealth( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getVersion( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getRoles( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getMetrics( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getLoggingLevel( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> setLoggingLevel( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> listFiles( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getMaster( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> updateMaintenanceSchedule( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getMaintenanceSchedule( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getMaintenanceStatus( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> startMaintenance( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> stopMaintenance( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getTasks( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; mesos::master::Response::GetTasks _getTasks( @@ -1460,27 +1498,27 @@ private: process::Future<process::http::Response> createVolumes( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> destroyVolumes( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> reserveResources( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> unreserveResources( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> getFrameworks( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; mesos::master::Response::GetFrameworks _getFrameworks( @@ -1488,7 +1526,7 @@ private: process::Future<process::http::Response> getExecutors( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; mesos::master::Response::GetExecutors _getExecutors( @@ -1497,7 +1535,7 @@ private: process::Future<process::http::Response> getState( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; mesos::master::Response::GetState _getState( @@ -1507,12 +1545,12 @@ private: process::Future<process::http::Response> subscribe( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; process::Future<process::http::Response> readFile( const mesos::master::Call& call, - const Option<std::string>& principal, + const Option<process::http::authentication::Principal>& principal, ContentType contentType) const; Master* master; http://git-wip-us.apache.org/repos/asf/mesos/blob/da47646e/src/master/quota_handler.cpp ---------------------------------------------------------------------- diff --git a/src/master/quota_handler.cpp b/src/master/quota_handler.cpp index 3ad28e4..5212ed7 100644 --- a/src/master/quota_handler.cpp +++ b/src/master/quota_handler.cpp @@ -50,6 +50,8 @@ using http::Conflict; using http::Forbidden; using http::OK; +using mesos::authorization::createSubject; + using mesos::quota::QuotaInfo; using mesos::quota::QuotaRequest; using mesos::quota::QuotaStatus; @@ -57,6 +59,8 @@ using mesos::quota::QuotaStatus; using process::Future; using process::Owned; +using process::http::authentication::Principal; + using std::list; using std::string; using std::vector; @@ -213,7 +217,7 @@ void Master::QuotaHandler::rescindOffers(const QuotaInfo& request) const Future<http::Response> Master::QuotaHandler::status( const mesos::master::Call& call, - const Option<string>& principal, + const Option<Principal>& principal, ContentType contentType) const { CHECK_EQ(mesos::master::Call::GET_QUOTA, call.type()); @@ -232,7 +236,7 @@ Future<http::Response> Master::QuotaHandler::status( Future<http::Response> Master::QuotaHandler::status( const http::Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { VLOG(1) << "Handling quota status request"; @@ -247,7 +251,7 @@ Future<http::Response> Master::QuotaHandler::status( Future<QuotaStatus> Master::QuotaHandler::_status( - const Option<string>& principal) const + const Option<Principal>& principal) const { // Quotas can be updated during preparation of the response. // Copy current view of the collection to avoid conflicts. @@ -296,7 +300,7 @@ Future<QuotaStatus> Master::QuotaHandler::_status( Future<http::Response> Master::QuotaHandler::set( const mesos::master::Call& call, - const Option<string>& principal) const + const Option<Principal>& principal) const { CHECK_EQ(mesos::master::Call::SET_QUOTA, call.type()); CHECK(call.has_set_quota()); @@ -307,7 +311,7 @@ Future<http::Response> Master::QuotaHandler::set( Future<http::Response> Master::QuotaHandler::set( const http::Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { VLOG(1) << "Setting quota from request: '" << request.body << "'"; @@ -338,7 +342,7 @@ Future<http::Response> Master::QuotaHandler::set( Future<http::Response> Master::QuotaHandler::_set( const QuotaRequest& quotaRequest, - const Option<string>& principal) const + const Option<Principal>& principal) const { Try<QuotaInfo> create = quota::createQuotaInfo(quotaRequest); if (create.isError()) { @@ -376,7 +380,12 @@ Future<http::Response> Master::QuotaHandler::_set( const bool forced = quotaRequest.force(); if (principal.isSome()) { - quotaInfo.set_principal(principal.get()); + // We assume that `principal->value.isSome()` is true. The master's HTTP + // handlers enforce this constraint, and V0 authenticators will only return + // principals of that form. + CHECK_SOME(principal->value); + + quotaInfo.set_principal(principal->value.get()); } return authorizeSetQuota(principal, quotaInfo) @@ -439,7 +448,7 @@ Future<http::Response> Master::QuotaHandler::__set( Future<http::Response> Master::QuotaHandler::remove( const mesos::master::Call& call, - const Option<string>& principal) const + const Option<Principal>& principal) const { CHECK_EQ(mesos::master::Call::REMOVE_QUOTA, call.type()); CHECK(call.has_remove_quota()); @@ -450,7 +459,7 @@ Future<http::Response> Master::QuotaHandler::remove( Future<http::Response> Master::QuotaHandler::remove( const http::Request& request, - const Option<string>& principal) const + const Option<Principal>& principal) const { VLOG(1) << "Removing quota for request path: '" << request.url.path << "'"; @@ -497,7 +506,7 @@ Future<http::Response> Master::QuotaHandler::remove( Future<http::Response> Master::QuotaHandler::_remove( const string& role, - const Option<string>& principal) const + const Option<Principal>& principal) const { return authorizeRemoveQuota(principal, master->quotas[role].info) .then(defer(master->self(), [=](bool authorized) -> Future<http::Response> { @@ -531,7 +540,7 @@ Future<http::Response> Master::QuotaHandler::__remove(const string& role) const Future<bool> Master::QuotaHandler::authorizeGetQuota( - const Option<string>& principal, + const Option<Principal>& principal, const QuotaInfo& quotaInfo) const { if (master->authorizer.isNone()) { @@ -539,14 +548,15 @@ Future<bool> Master::QuotaHandler::authorizeGetQuota( } LOG(INFO) << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") + << (principal.isSome() ? stringify(principal.get()) : "ANY") << "' to get quota for role '" << quotaInfo.role() << "'"; authorization::Request request; request.set_action(authorization::GET_QUOTA); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } // TODO(alexr): The `value` field is set for backwards compatibility @@ -561,7 +571,7 @@ Future<bool> Master::QuotaHandler::authorizeGetQuota( // TODO(zhitao): Remove this function at the end of the // deprecation cycle which started with 1.0. Future<bool> Master::QuotaHandler::authorizeSetQuota( - const Option<string>& principal, + const Option<Principal>& principal, const QuotaInfo& quotaInfo) const { if (master->authorizer.isNone()) { @@ -569,14 +579,15 @@ Future<bool> Master::QuotaHandler::authorizeSetQuota( } LOG(INFO) << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") + << (principal.isSome() ? stringify(principal.get()) : "ANY") << "' to set quota for role '" << quotaInfo.role() << "'"; authorization::Request request; request.set_action(authorization::UPDATE_QUOTA); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } request.mutable_object()->set_value("SetQuota"); @@ -589,7 +600,7 @@ Future<bool> Master::QuotaHandler::authorizeSetQuota( // TODO(zhitao): Remove this function at the end of the // deprecation cycle which started with 1.0. Future<bool> Master::QuotaHandler::authorizeRemoveQuota( - const Option<string>& principal, + const Option<Principal>& principal, const QuotaInfo& quotaInfo) const { if (master->authorizer.isNone()) { @@ -597,14 +608,15 @@ Future<bool> Master::QuotaHandler::authorizeRemoveQuota( } LOG(INFO) << "Authorizing principal '" - << (principal.isSome() ? principal.get() : "ANY") + << (principal.isSome() ? stringify(principal.get()) : "ANY") << "' to remove quota for role '" << quotaInfo.role() << "'"; authorization::Request request; request.set_action(authorization::UPDATE_QUOTA); - if (principal.isSome()) { - request.mutable_subject()->set_value(principal.get()); + Option<authorization::Subject> subject = createSubject(principal); + if (subject.isSome()) { + request.mutable_subject()->CopyFrom(subject.get()); } request.mutable_object()->set_value("RemoveQuota"); http://git-wip-us.apache.org/repos/asf/mesos/blob/da47646e/src/master/registrar.cpp ---------------------------------------------------------------------- diff --git a/src/master/registrar.cpp b/src/master/registrar.cpp index d7134ee..0029cc7 100644 --- a/src/master/registrar.cpp +++ b/src/master/registrar.cpp @@ -65,6 +65,8 @@ using process::TLDR; using process::http::OK; +using process::http::authentication::Principal; + using process::metrics::Gauge; using process::metrics::Timer; @@ -120,7 +122,7 @@ private: // /registrar(N)/registry Future<Response> registry( const Request& request, - const Option<string>& /* principal */); + const Option<Principal>&); static string registryHelp(); // The 'Recover' operation adds the latest MasterInfo. @@ -256,7 +258,7 @@ void fail(deque<Owned<Operation>>* operations, const string& message) Future<Response> RegistrarProcess::registry( const Request& request, - const Option<string>& /* principal */) + const Option<Principal>&) { JSON::Object result;
