Repository: mesos
Updated Branches:
  refs/heads/master 0183a73f3 -> 105755a9e


Refactored endpoint extraction from URL path.

The short path of HTTP endpoint is now extracted from `URL.path` in
separate functions in both `Slave::Http` and `Master::Http`, making
`extractEndpoint()` consistent in both classes. In the future,
`extractEndpoint() should be replaced in favor of a more general
solution, see MESOS-5369 for details.

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


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

Branch: refs/heads/master
Commit: 105755a9e20a917b2a3fe1492487f9a64b482eec
Parents: 0183a73
Author: Jan Schlicht <j...@mesosphere.io>
Authored: Fri May 13 18:40:55 2016 +0200
Committer: Alexander Rukletsov <al...@apache.org>
Committed: Fri May 13 19:07:07 2016 +0200

----------------------------------------------------------------------
 src/master/http.cpp   | 50 ++++++++++++++++-------------
 src/master/master.hpp |  3 ++
 src/slave/http.cpp    | 78 ++++++++++++++++++++++++++++++----------------
 src/slave/slave.hpp   | 24 ++++++++------
 4 files changed, 99 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/105755a9/src/master/http.cpp
----------------------------------------------------------------------
diff --git a/src/master/http.cpp b/src/master/http.cpp
index 6cefcc8..0245b86 100644
--- a/src/master/http.cpp
+++ b/src/master/http.cpp
@@ -870,31 +870,21 @@ Future<Response> Master::Http::flags(
     return MethodNotAllowed({"GET"}, request.method);
   }
 
-  // Paths are of the form "/master/endpoint". We're only interested
-  // in the part after "/master" and tokenize the path accordingly.
-  //
-  // TODO(nfnt): Refactor this into a helper function in `Master::Http` so
-  // that other endpoints can reuse it. In the long run, absolute paths for
-  // endpoins should be supported, see MESOS-5369.
-  const vector<string> pathComponents =
-    strings::tokenize(request.url.path, "/", 2);
-
-  if (pathComponents.size() != 2u ||
-      pathComponents[0] != master->self().id) {
-    return Failure("Unexpected path '" + request.url.path + "'");
+  Try<string> endpoint = extractEndpoint(request.url);
+  if (endpoint.isError()) {
+    return Failure("Failed to extract endpoint: " + endpoint.error());
   }
-  const string endpoint = "/" + pathComponents[1];
 
-  return authorizeEndpoint(principal, endpoint, request.method)
+  return authorizeEndpoint(principal, endpoint.get(), request.method)
     .then(defer(
-          master->self(),
-          [this, request](bool authorized) -> Future<Response> {
-            if (!authorized) {
-              return Forbidden();
-            }
+        master->self(),
+        [this, request](bool authorized) -> Future<Response> {
+          if (!authorized) {
+            return Forbidden();
+          }
 
-            return _flags(request);
-          }));
+          return _flags(request);
+        }));
 }
 
 
@@ -2853,6 +2843,24 @@ Future<Response> Master::Http::_operation(
 }
 
 
+Try<string> Master::Http::extractEndpoint(const process::http::URL& url) const
+{
+  // Paths are of the form "/master/endpoint". We're only interested
+  // in the part after "/master" and tokenize the path accordingly.
+  //
+  // TODO(nfnt): In the long run, absolute paths for
+  // endpoins should be supported, see MESOS-5369.
+  const vector<string> pathComponents = strings::tokenize(url.path, "/", 2);
+
+  if (pathComponents.size() < 2u ||
+      pathComponents[0] != master->self().id) {
+    return Error("Unexpected path '" + url.path + "'");
+  }
+
+  return "/" + pathComponents[1];
+}
+
+
 Future<bool> Master::Http::authorizeEndpoint(
     const Option<string>& principal,
     const string& endpoint,

http://git-wip-us.apache.org/repos/asf/mesos/blob/105755a9/src/master/master.hpp
----------------------------------------------------------------------
diff --git a/src/master/master.hpp b/src/master/master.hpp
index bd0fa98..380be95 100644
--- a/src/master/master.hpp
+++ b/src/master/master.hpp
@@ -1232,6 +1232,9 @@ private:
         Resources required,
         const Offer::Operation& operation) const;
 
+    // Helper routines for endpoint authorization.
+    Try<std::string> extractEndpoint(const process::http::URL& url) const;
+
     // Authorizes access to an HTTP endpoint. The `method` parameter
     // determines which ACL action will be used in the authorization.
     // It is expected that the caller has validated that `method` is

http://git-wip-us.apache.org/repos/asf/mesos/blob/105755a9/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index 0fb9b81..c5d6a7a 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -360,9 +360,20 @@ Future<Response> Slave::Http::flags(
     const Request& request,
     const Option<string>& principal) const
 {
+  // TODO(nfnt): Remove check for enabled
+  // authorization as part of MESOS-5346.
+  if (request.method != "GET" && slave->authorizer.isSome()) {
+    return MethodNotAllowed({"GET"}, request.method);
+  }
+
   const Flags slaveFlags = slave->flags;
 
-  return authorizeEndpoint(request, principal)
+  Try<string> endpoint = extractEndpoint(request.url);
+  if (endpoint.isError()) {
+    return Failure("Failed to extract endpoint: " + endpoint.error());
+  }
+
+  return authorizeEndpoint(principal, endpoint.get(), request.method)
     .then(defer(
         slave->self(),
         [request, slaveFlags](bool authorized) -> Future<Response> {
@@ -631,10 +642,21 @@ Future<Response> Slave::Http::statistics(
     const Request& request,
     const Option<string>& principal) const
 {
+  // TODO(nfnt): Remove check for enabled
+  // authorization as part of MESOS-5346.
+  if (request.method != "GET" && slave->authorizer.isSome()) {
+    return MethodNotAllowed({"GET"}, request.method);
+  }
+
   const PID<Slave> pid = slave->self();
   Shared<RateLimiter> limiter = statisticsLimiter;
 
-  return authorizeEndpoint(request, principal)
+  Try<string> endpoint = extractEndpoint(request.url);
+  if (endpoint.isError()) {
+    return Failure("Failed to extract endpoint: " + endpoint.error());
+  }
+
+  return authorizeEndpoint(principal, endpoint.get(), request.method)
     .then(defer(
         pid,
         [pid, limiter, request](bool authorized) -> Future<Response> {
@@ -812,51 +834,55 @@ Future<Response> Slave::Http::containers(const Request& 
request) const
 }
 
 
-Future<bool> Slave::Http::authorizeEndpoint(
-    const Request& httpRequest,
-    const Option<string>& principal) const
+Try<string> Slave::Http::extractEndpoint(const process::http::URL& url) const
 {
-  if (slave->authorizer.isNone()) {
-    return true;
-  }
-
   // Paths are of the form "/slave(n)/endpoint". We're only interested
   // in the part after "/slave(n)" and tokenize the path accordingly.
   //
-  // TODO(alexr): Refactor this into a helper function in `Slave::Http` so
-  // that other endpoints can reuse it. In the long run, absolute paths for
+  // TODO(alexr): In the long run, absolute paths for
   // endpoins should be supported, see MESOS-5369.
-  const vector<string> pathComponents =
-    strings::tokenize(httpRequest.url.path, "/", 2);
+  const vector<string> pathComponents = strings::tokenize(url.path, "/", 2);
 
-  if (pathComponents.size() != 2u ||
+  if (pathComponents.size() < 2u ||
       pathComponents[0] != slave->self().id) {
-    return Failure("Unexpected path '" + httpRequest.url.path + "'");
+    return Error("Unexpected path '" + url.path + "'");
+  }
+
+  return "/" + pathComponents[1];
+}
+
+
+Future<bool> Slave::Http::authorizeEndpoint(
+    const Option<string>& principal,
+    const string& endpoint,
+    const string& method) const
+{
+  if (slave->authorizer.isNone()) {
+    return true;
   }
-  const string endpoint = "/" + pathComponents[1];
 
-  authorization::Request authorizationRequest;
+  authorization::Request request;
 
-  // TODO(nfnt): Add an additional case when POST requests need to be
-  // authorized separately from GET requests.
-  if (httpRequest.method == "GET") {
-    authorizationRequest.set_action(authorization::GET_ENDPOINT_WITH_PATH);
+  // TODO(nfnt): Add an additional case when POST requests
+  // need to be authorized separately from GET requests.
+  if (method == "GET") {
+    request.set_action(authorization::GET_ENDPOINT_WITH_PATH);
   } else {
-    return Failure("Unexpected request method '" + httpRequest.method + "'");
+    return Failure("Unexpected request method '" + method + "'");
   }
 
   if (principal.isSome()) {
-    authorizationRequest.mutable_subject()->set_value(principal.get());
+    request.mutable_subject()->set_value(principal.get());
   }
 
-  authorizationRequest.mutable_object()->set_value(endpoint);
+  request.mutable_object()->set_value(endpoint);
 
   LOG(INFO) << "Authorizing principal '"
             << (principal.isSome() ? principal.get() : "ANY")
-            << "' to " <<  httpRequest.method
+            << "' to " <<  method
             << " the '" << endpoint << "' endpoint";
 
-  return slave->authorizer.get()->authorized(authorizationRequest);
+  return slave->authorizer.get()->authorized(request);
 }
 
 } // namespace slave {

http://git-wip-us.apache.org/repos/asf/mesos/blob/105755a9/src/slave/slave.hpp
----------------------------------------------------------------------
diff --git a/src/slave/slave.hpp b/src/slave/slave.hpp
index be622d3..89e3348 100644
--- a/src/slave/slave.hpp
+++ b/src/slave/slave.hpp
@@ -472,21 +472,27 @@ private:
         const process::http::Request& request,
         const Flags& flags);
 
-    // Authorizes access to an HTTP endpoint. It extracts the endpoint
-    // from the URL of the request by removing the "/slave(n)" part of
-    // the URL's path. The request's `method` determines which ACL action
-    // will be used in the authorization.
-    process::Future<bool> authorizeEndpoint(
-        const process::http::Request& request,
-        const Option<std::string>& principal) const;
-
-
     // Make continuation for `statistics` `static` as it might
     // execute when the invoking `Http` is already destructed.
     static process::http::Response _statistics(
         const ResourceUsage& usage,
         const process::http::Request& request);
 
+    // Helper routines for endpoint authorization.
+    Try<std::string> extractEndpoint(const process::http::URL& url) const;
+
+    // Authorizes access to an HTTP endpoint. The `method` parameter
+    // determines which ACL action will be used in the authorization.
+    // It is expected that the caller has validated that `method` is
+    // supported by this function. Currently "GET" is supported.
+    //
+    // TODO(nfnt): Prefer types instead of strings
+    // for `endpoint` and `method`, see MESOS-5300.
+    process::Future<bool> authorizeEndpoint(
+        const Option<std::string>& principal,
+        const std::string& endpoint,
+        const std::string& method) const;
+
     Slave* slave;
 
     // Used to rate limit the statistics endpoint.

Reply via email to