This is an automated email from the ASF dual-hosted git repository.
bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git
The following commit(s) were added to refs/heads/master by this push:
new b4c705f Moved creating authorization Object out of
Master::authorize*Volume.
b4c705f is described below
commit b4c705fb884b23e7a016b873aca6369a26eaa32a
Author: Andrei Sekretenko <[email protected]>
AuthorDate: Tue Jan 7 12:56:53 2020 -0500
Moved creating authorization Object out of Master::authorize*Volume.
Review: https://reviews.apache.org/r/71863/
---
src/master/authorization.cpp | 58 +++++++++++++++-
src/master/authorization.hpp | 7 ++
src/master/http.cpp | 6 +-
src/master/master.cpp | 153 +++++++++----------------------------------
src/master/master.hpp | 48 ++------------
5 files changed, 105 insertions(+), 167 deletions(-)
diff --git a/src/master/authorization.cpp b/src/master/authorization.cpp
index 6f1231f..96f6a1c 100644
--- a/src/master/authorization.cpp
+++ b/src/master/authorization.cpp
@@ -21,7 +21,7 @@
using std::ostream;
using std::string;
-
+using std::vector;
namespace mesos {
namespace authorization {
@@ -97,6 +97,62 @@ string getReservationRole(const Resource& resource)
}
+vector<ActionObject> ActionObject::createVolume(
+ const Offer::Operation::Create& create)
+{
+ vector<ActionObject> result;
+
+ // The operation will be authorized if the entity is allowed
+ // to create volumes for all roles included in `create.volumes`.
+
+ // Add an object for each unique role in the volumes.
+ hashset<string> roles;
+
+ foreach (const Resource& volume, create.volumes()) {
+ string role = getReservationRole(volume);
+
+ if (!roles.contains(role)) {
+ roles.insert(role);
+ result.push_back(fromResourceWithLegacyValue(
+ authorization::CREATE_VOLUME, volume, role));
+ }
+ }
+
+ if (result.empty()) {
+ result.push_back(ActionObject(authorization::CREATE_VOLUME, None()));
+ }
+
+ return result;
+}
+
+
+vector<ActionObject> ActionObject::destroyVolume(
+ const Offer::Operation::Destroy& destroy)
+{
+ vector<ActionObject> result;
+
+ // NOTE: As this code can be called for an invalid operation, we create
+ // action-object pairs only for resources that are persistent volumes and
+ // skip all the others, relying on the caller to perform validation after
+ // authorization (see MESOS-10083).
+ foreach (const Resource& volume, destroy.volumes()) {
+ // TODO(asekretenko): Replace with CHECK after MESOS-10083 is fixed.
+ if (volume.has_disk() && volume.disk().has_persistence()) {
+ result.push_back(fromResourceWithLegacyValue(
+ authorization::DESTROY_VOLUME,
+ volume,
+ volume.disk().persistence().principal()));
+ }
+ }
+
+ if (result.empty()) {
+ result.push_back(ActionObject(authorization::DESTROY_VOLUME, None()));
+ }
+
+ return result;
+}
+
+
ActionObject ActionObject::growVolume(const Offer::Operation::GrowVolume& grow)
{
return fromResourceWithLegacyValue(
diff --git a/src/master/authorization.hpp b/src/master/authorization.hpp
index 0949a0b..d2b96ef 100644
--- a/src/master/authorization.hpp
+++ b/src/master/authorization.hpp
@@ -19,6 +19,7 @@
#include <ostream>
#include <string>
+#include <vector>
#include <stout/option.hpp>
@@ -54,6 +55,12 @@ public:
// resources are in post-reservation-refinement format.
// Otherwise, they may crash the program.
+ static std::vector<ActionObject> createVolume(
+ const Offer::Operation::Create& create);
+
+ static std::vector<ActionObject> destroyVolume(
+ const Offer::Operation::Destroy& destroy);
+
static ActionObject growVolume(
const Offer::Operation::GrowVolume& grow);
diff --git a/src/master/http.cpp b/src/master/http.cpp
index 94084dd..93ec096 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -1010,7 +1010,8 @@ Future<Response> Master::Http::_createVolumes(
error->message);
}
- return master->authorizeCreateVolume(operation.create(), principal)
+ return master->authorize(
+ principal, ActionObject::createVolume(operation.create()))
.then(defer(master->self(), [=](bool authorized) -> Future<Response> {
if (!authorized) {
return Forbidden();
@@ -1178,7 +1179,8 @@ Future<Response> Master::Http::_destroyVolumes(
return BadRequest("Invalid DESTROY operation: " + error->message);
}
- return master->authorizeDestroyVolume(operation.destroy(), principal)
+ return master->authorize(
+ principal, ActionObject::destroyVolume(operation.destroy()))
.then(defer(master->self(), [=](bool authorized) -> Future<Response> {
if (!authorized) {
return Forbidden();
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 6ac5476..d3c2889 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -3727,6 +3727,33 @@ Future<bool> Master::authorize(
}
+Future<bool> Master::authorize(
+ const Option<Principal>& principal,
+ vector<ActionObject>&& actionObjects)
+{
+ // NOTE: In some cases (example: RESERVE with empty resources or with source
+ // identical to target) there is no need to authorize any action-object pair
+ // (and no meaningful ActionObject can be composed anyway). Here, we treat
+ // authorization as PASSED in these cases and expect these cases to be
+ // handled by validation afterwards.
+ //
+ // Some of these cases are invalid; ideally, they should be filtered by
+ // validation before being fed into ActionObject-composing code (see
+ // MESOS-10083).
+ if (actionObjects.empty()) {
+ return true;
+ }
+
+ vector<Future<bool>> authorizations;
+ authorizations.reserve(actionObjects.size());
+ for (ActionObject& actionObject: actionObjects) {
+ authorizations.push_back(authorize(principal, std::move(actionObject)));
+ }
+
+ return authorization::collectAuthorizations(authorizations);
+}
+
+
Future<bool> Master::authorizeReserveResources(
const Offer::Operation::Reserve& reserve,
const Option<Principal>& principal)
@@ -3906,114 +3933,6 @@ Future<bool> Master::authorizeUnreserveResources(
}
-Future<bool> Master::authorizeCreateVolume(
- const Offer::Operation::Create& create,
- const Option<Principal>& principal)
-{
- if (authorizer.isNone()) {
- return true; // Authorization is disabled.
- }
-
- authorization::Request request;
- request.set_action(authorization::CREATE_VOLUME);
-
- Option<authorization::Subject> subject = createSubject(principal);
- if (subject.isSome()) {
- request.mutable_subject()->CopyFrom(subject.get());
- }
-
- // 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;
- vector<Future<bool>> authorizations;
- foreach (const Resource& volume, create.volumes()) {
- // NOTE: We rely on the master to ensure that the resource is in the
- // post-reservation-refinement format. If there is a stack of reservations,
- // we perform authorization for the role of the most refined reservation,
- // since we only support "pushing" one reservation at a time. That is, all
- // of the previous reservations must have already been authorized.
- //
- // NOTE: Since authorization happens __before__ validation, we must check
- // here that this resource has a reservation. If not, the error will be
- // caught during validation, but we still authorize the resource with the
- // default role '*' for backward compatibility.
- CHECK(!volume.has_role()) << volume;
- CHECK(!volume.has_reservation()) << volume;
-
- const string role =
- Resources::isReserved(volume) ? Resources::reservationRole(volume) : "*";
-
- if (!roles.contains(role)) {
- roles.insert(role);
-
- request.mutable_object()->mutable_resource()->CopyFrom(volume);
-
- // We also set the deprecated `object.value` field to support legacy
- // authorizers that have not been upgraded to look at `object.resource`.
- request.mutable_object()->set_value(role);
-
- authorizations.push_back(authorizer.get()->authorized(request));
- }
- }
-
- LOG(INFO) << "Authorizing principal '"
- << (principal.isSome() ? stringify(principal.get()) : "ANY")
- << "' to create volumes '" << create.volumes() << "'";
-
- if (authorizations.empty()) {
- return authorizer.get()->authorized(request);
- }
-
- return authorization::collectAuthorizations(authorizations);
-}
-
-
-Future<bool> Master::authorizeDestroyVolume(
- const Offer::Operation::Destroy& destroy,
- const Option<Principal>& principal)
-{
- if (authorizer.isNone()) {
- return true; // Authorization is disabled.
- }
-
- authorization::Request request;
- request.set_action(authorization::DESTROY_VOLUME);
-
- Option<authorization::Subject> subject = createSubject(principal);
- if (subject.isSome()) {
- request.mutable_subject()->CopyFrom(subject.get());
- }
-
- vector<Future<bool>> authorizations;
- foreach (const Resource& volume, destroy.volumes()) {
- // NOTE: Since authorization happens __before__ validation, we must check
- // here that this resource is a persistent volume. If not, the error will
be
- // caught during validation.
- if (volume.has_disk() && volume.disk().has_persistence()) {
- request.mutable_object()->mutable_resource()->CopyFrom(volume);
-
- // We also set the deprecated `object.value` field to support legacy
- // authorizers that have not been upgraded to look at `object.resource`.
- request.mutable_object()->set_value(
- volume.disk().persistence().principal());
-
- authorizations.push_back(authorizer.get()->authorized(request));
- }
- }
-
- LOG(INFO) << "Authorizing principal '"
- << (principal.isSome() ? stringify(principal.get()) : "ANY")
- << "' to destroy volumes '" << destroy.volumes() << "'";
-
- if (authorizations.empty()) {
- return authorizer.get()->authorized(request);
- }
-
- return authorization::collectAuthorizations(authorizations);
-}
-
-
Future<bool> Master::authorizeSlave(
const SlaveInfo& slaveInfo,
const Option<Principal>& principal)
@@ -4677,26 +4596,16 @@ void Master::accept(
// The CREATE operation allows the creation of a persistent volume.
case Offer::Operation::CREATE: {
- Option<Principal> principal = framework->info.has_principal()
- ? Principal(framework->info.principal())
- : Option<Principal>::none();
-
- futures.push_back(
- authorizeCreateVolume(
- operation.create(), principal));
+ futures.push_back(authorize(
+ principal, ActionObject::createVolume(operation.create())));
break;
}
// The DESTROY operation allows the destruction of a persistent volume.
case Offer::Operation::DESTROY: {
- Option<Principal> principal = framework->info.has_principal()
- ? Principal(framework->info.principal())
- : Option<Principal>::none();
-
- futures.push_back(
- authorizeDestroyVolume(
- operation.destroy(), principal));
+ futures.push_back(authorize(
+ principal, ActionObject::destroyVolume(operation.destroy())));
break;
}
diff --git a/src/master/master.hpp b/src/master/master.hpp
index 0dd7b44..be1a0dd 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -750,6 +750,12 @@ protected:
const Option<process::http::authentication::Principal>& principal,
authorization::ActionObject&& actionObject);
+ // Overload of authorize() for cases which require multiple action-object
+ // pairs to be authorized simultaneously.
+ process::Future<bool> authorize(
+ const Option<process::http::authentication::Principal>& principal,
+ std::vector<authorization::ActionObject>&& actionObjects);
+
// TODO(asekretenko): get rid of action-specific authorizeSomething()
methods.
// Returns whether the framework is authorized.
@@ -806,48 +812,6 @@ protected:
const Offer::Operation::Unreserve& unreserve,
const Option<process::http::authentication::Principal>& principal);
- /**
- * Authorizes a `CREATE` operation.
- *
- * Returns whether the Create operation is authorized with the provided
- * principal. This function is used for authorization of operations
- * originating both from frameworks and operators. Note that operations may
be
- * validated AFTER authorization, so it's possible that `create` could be
- * malformed.
- *
- * @param create The `CREATE` operation to be performed.
- * @param principal An `Option` containing the principal attempting this
- * operation.
- *
- * @return A `Future` containing a boolean value representing the success or
- * failure of this authorization. A failed `Future` implies that
- * validation of the operation did not succeed.
- */
- process::Future<bool> authorizeCreateVolume(
- const Offer::Operation::Create& create,
- const Option<process::http::authentication::Principal>& principal);
-
- /**
- * Authorizes a `DESTROY` operation.
- *
- * Returns whether the Destroy operation is authorized with the provided
- * principal. This function is used for authorization of operations
- * originating both from frameworks and operators. Note that operations may
be
- * validated AFTER authorization, so it's possible that `destroy` could be
- * malformed.
- *
- * @param destroy The `DESTROY` operation to be performed.
- * @param principal An `Option` containing the principal attempting this
- * operation.
- *
- * @return A `Future` containing a boolean value representing the success or
- * failure of this authorization. A failed `Future` implies that
- * validation of the operation did not succeed.
- */
- process::Future<bool> authorizeDestroyVolume(
- const Offer::Operation::Destroy& destroy,
- const Option<process::http::authentication::Principal>& principal);
-
// Determine if a new executor needs to be launched.
bool isLaunchExecutor (
const ExecutorID& executorId,