This is an automated email from the ASF dual-hosted git repository.

asekretenko pushed a commit to branch 1.7.x
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit d3eda2a65bd5637f4031e6c8f225049038a01389
Author: Andrei Sekretenko <asekrete...@mesosphere.com>
AuthorDate: Thu Jan 30 13:02:45 2020 +0100

    Factored out getting authorization service URI.
    
    Review: https://reviews.apache.org/r/72078
---
 src/uri/fetchers/docker.cpp | 173 ++++++++++++++++++++++++--------------------
 1 file changed, 93 insertions(+), 80 deletions(-)

diff --git a/src/uri/fetchers/docker.cpp b/src/uri/fetchers/docker.cpp
index 5fb0e93..9a64021 100644
--- a/src/uri/fetchers/docker.cpp
+++ b/src/uri/fetchers/docker.cpp
@@ -467,6 +467,59 @@ static URI getBlobUri(const URI& imageUri)
 }
 
 
+// Obtains URI of the V2 authorization service based on the
+// initial "401 unauthorized" response of the Docker V2 registry.
+static Future<string> getAuthServiceUri(const http::Response& initialResponse)
+{
+  Result<http::header::WWWAuthenticate> header =
+    initialResponse.headers.get<http::header::WWWAuthenticate>();
+
+  if (header.isError()) {
+    return Failure("Failed to get WWW-Authenticate header: " + header.error());
+  } else if (header.isNone()) {
+    return Failure("Unexpected empty WWW-Authenticate header");
+  }
+
+  // According to RFC, auth scheme should be case insensitive.
+  const string authScheme = strings::upper(header->authScheme());
+
+  if (authScheme == "BASIC") {
+    return Failure(
+        "Unexpected BASIC Authorization response status: " +
+        initialResponse.status);
+  }
+
+  if (authScheme != "BEARER") {
+    return Failure("Unsupported auth-scheme: " + authScheme);
+  }
+
+  hashmap<string, string> authParam = header->authParam();
+
+  // `authParam` is supposed to contain the 'realm', 'service'
+  // and 'scope' information for bearer authentication.
+  //
+  // TODO(asekretenko): Fall back to querying repository root and
+  // constructing scope if one of these is missing (see MESOS-10092).
+  if (!authParam.contains("realm")) {
+    return Failure("Missing 'realm' in WWW-Authenticate header");
+  }
+
+  if (!authParam.contains("service")) {
+    return Failure("Missing 'service' in WWW-Authenticate header");
+  }
+
+  if (!authParam.contains("scope")) {
+    return Failure("Missing 'scope' in WWW-Authenticate header");
+  }
+
+  // TODO(jieyu): Currently, we don't expect the auth server to return
+  // a service or a scope that needs encoding.
+  return authParam.at("realm") + "?" +
+    "service=" + authParam.at("service") + "&" +
+    "scope=" + authParam.at("scope");
+}
+
+
 //-------------------------------------------------------------------
 // DockerFetcherPlugin implementation.
 //-------------------------------------------------------------------
@@ -1068,91 +1121,51 @@ Future<Nothing> 
DockerFetcherPluginProcess::_urlFetchBlob(
 #endif
 
 
+// If a '401 Unauthorized' response is received and the auth-scheme
+// is 'Bearer', we expect a header 'Www-Authenticate' containing the
+// auth server information. We extract the auth server information
+// from the auth-param, and then contacts the auth server to get the
+// token. The token will then be placed in the subsequent HTTP
+// requests as a header.
+//
+// See details here:
+// https://docs.docker.com/registry/spec/auth/token/
 Future<http::Headers> DockerFetcherPluginProcess::getAuthHeader(
     const URI& uri,
     const http::Headers& basicAuthHeaders,
     const http::Response& response)
 {
-  Result<http::header::WWWAuthenticate> header =
-    response.headers.get<http::header::WWWAuthenticate>();
-
-  if (header.isError()) {
-    return Failure(
-        "Failed to get WWW-Authenticate header: " + header.error());
-  } else if (header.isNone()) {
-    return Failure("Unexpected empty WWW-Authenticate header");
-  }
-
-  // According to RFC, auth scheme should be case insensitive.
-  const string authScheme = strings::upper(header->authScheme());
-
-  // If a '401 Unauthorized' response is received and the auth-scheme
-  // is 'Bearer', we expect a header 'Www-Authenticate' containing the
-  // auth server information. We extract the auth server information
-  // from the auth-param, and then contacts the auth server to get the
-  // token. The token will then be placed in the subsequent HTTP
-  // requests as a header.
-  //
-  // See details here:
-  // https://docs.docker.com/registry/spec/auth/token/
-  if (authScheme == "BEARER") {
-    hashmap<string, string> authParam = header->authParam();
-
-    // `authParam` is supposed to contain the 'realm', 'service'
-    // and 'scope' information for bearer authentication.
-    if (!authParam.contains("realm")) {
-      return Failure("Missing 'realm' in WWW-Authenticate header");
-    }
-
-    if (!authParam.contains("service")) {
-      return Failure("Missing 'service' in WWW-Authenticate header");
-    }
-
-    if (!authParam.contains("scope")) {
-      return Failure("Missing 'scope' in WWW-Authenticate header");
-    }
-
-    // TODO(jieyu): Currently, we don't expect the auth server to return
-    // a service or a scope that needs encoding.
-    string authServerUri =
-      authParam.at("realm") + "?" +
-      "service=" + authParam.at("service") + "&" +
-      "scope=" + authParam.at("scope");
-
-    return curl(authServerUri, basicAuthHeaders, stallTimeout)
-      .then([authServerUri](
-          const http::Response& response) -> Future<http::Headers> {
-        if (response.code != http::Status::OK) {
-          return Failure(
-            "Unexpected HTTP response '" + response.status + "' "
-            "when trying to GET '" + authServerUri + "'");
-        }
-
-        CHECK_EQ(response.type, http::Response::BODY);
-
-        Try<JSON::Object> object = JSON::parse<JSON::Object>(response.body);
-        if (object.isError()) {
-          return Failure("Parsing the JSON object failed: " + object.error());
-        }
-
-        Result<JSON::String> token = object->find<JSON::String>("token");
-        if (token.isError()) {
-          return Failure(
-              "Finding token in JSON object failed: " + token.error());
-        } else if (token.isNone()) {
-          return Failure("Failed to find token in JSON object");
-        }
-
-        return getAuthHeaderBearer(token->value);
-      });
-  }
-
-  if (authScheme == "BASIC"){
-    return Failure(
-        "Unexpected BASIC Authorization response status: " + response.status);
-  }
-
-  return Failure("Unsupported auth-scheme: " + authScheme);
+  const auto stallTimeout = this->stallTimeout;
+
+  return getAuthServiceUri(response)
+    .then([basicAuthHeaders, stallTimeout](const string& authServiceUri) {
+      return curl(authServiceUri, basicAuthHeaders, stallTimeout)
+        .then([authServiceUri](const http::Response& response)
+        -> Future<http::Headers> {
+          if (response.code != http::Status::OK) {
+            return Failure(
+                "Unexpected HTTP response '" + response.status + "' "
+                "when trying to GET '" + authServiceUri + "'");
+          }
+
+          CHECK_EQ(response.type, http::Response::BODY);
+
+          Try<JSON::Object> object = JSON::parse<JSON::Object>(response.body);
+          if (object.isError()) {
+            return Failure("Parsing the JSON object failed: " + 
object.error());
+          }
+
+          Result<JSON::String> token = object->find<JSON::String>("token");
+          if (token.isError()) {
+            return Failure(
+                "Finding token in JSON object failed: " + token.error());
+          } else if (token.isNone()) {
+            return Failure("Failed to find token in JSON object");
+          }
+
+          return getAuthHeaderBearer(token->value);
+        });
+    });
 }
 
 

Reply via email to