Skip to content

Commit

Permalink
add assume-role base class
Browse files Browse the repository at this point in the history
  • Loading branch information
balamurugana committed Sep 2, 2020
1 parent b7dee7f commit d0cd54b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 157 deletions.
86 changes: 86 additions & 0 deletions api/src/main/java/io/minio/credentials/AssumeRoleBaseProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2020 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.minio.credentials;

import io.minio.errors.XmlParserException;
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/** Base class to AssumeRole based providers. */
public abstract class AssumeRoleBaseProvider implements Provider {
private final OkHttpClient httpClient;
private Credentials credentials;

public AssumeRoleBaseProvider(OkHttpClient customHttpClient) {
this.httpClient = (customHttpClient != null) ? customHttpClient : new OkHttpClient();
}

@Override
public synchronized Credentials fetch() {
if (credentials != null && !credentials.isExpired()) {
return credentials;
}

try (Response response = httpClient.newCall(getRequest()).execute()) {
if (!response.isSuccessful()) {
throw new IllegalStateException(
"STS service failed with HTTP status code " + response.code());
}

credentials = parseResponse(response);
return credentials;
} catch (XmlParserException | IOException e) {
throw new IllegalStateException("Unable to parse STS response", e);
}
}

protected HttpUrl.Builder newUrlBuilder(
HttpUrl url,
String action,
int durationSeconds,
String policy,
String roleArn,
String roleSessionName) {
HttpUrl.Builder urlBuilder = url.newBuilder().addQueryParameter("Version", "2011-06-15");

if (durationSeconds > 0) {
urlBuilder.addQueryParameter("DurationSeconds", String.valueOf(durationSeconds));
}

if (policy != null) {
urlBuilder.addQueryParameter("Policy", policy);
}

if (roleArn != null) {
urlBuilder.addQueryParameter("RoleArn", roleArn);
}

if (roleSessionName != null) {
urlBuilder.addQueryParameter("RoleSessionName", roleSessionName);
}

return urlBuilder;
}

protected abstract Request getRequest();

protected abstract Credentials parseResponse(Response response)
throws XmlParserException, IOException;
}
69 changes: 20 additions & 49 deletions api/src/main/java/io/minio/credentials/AssumeRoleProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,13 @@
* href="https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html">AssumeRole
* API</a>.
*/
public class AssumeRoleProvider implements Provider {
public class AssumeRoleProvider extends AssumeRoleBaseProvider {
public static final int DEFAULT_DURATION_SECONDS = (int) TimeUnit.HOURS.toSeconds(1);
private final String accessKey;
private final String secretKey;
private final String region;
private final OkHttpClient httpClient;
private final String contentSha256;
private final Request request;
private Credentials credentials;

public AssumeRoleProvider(
@Nonnull String stsEndpoint,
Expand All @@ -67,6 +65,7 @@ public AssumeRoleProvider(
@Nullable String externalId,
@Nullable OkHttpClient customHttpClient)
throws NoSuchAlgorithmException {
super(customHttpClient);
stsEndpoint = Objects.requireNonNull(stsEndpoint, "STS endpoint cannot be empty");
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(stsEndpoint), "Invalid STS endpoint");
accessKey = Objects.requireNonNull(accessKey, "Access key must not be null");
Expand All @@ -76,7 +75,6 @@ public AssumeRoleProvider(
this.accessKey = accessKey;
this.secretKey = Objects.requireNonNull(secretKey, "Secret key must not be null");
this.region = (region != null) ? region : "";
this.httpClient = (customHttpClient != null) ? customHttpClient : new OkHttpClient();

if (externalId != null && (externalId.length() < 2 || externalId.length() > 1224)) {
throw new IllegalArgumentException("Length of ExternalId must be in between 2 and 1224");
Expand All @@ -88,23 +86,7 @@ public AssumeRoleProvider(
: DEFAULT_DURATION_SECONDS;

HttpUrl.Builder urlBuilder =
url.newBuilder()
.addQueryParameter("Action", "AssumeRole")
.addQueryParameter("Version", "2011-06-15")
.addQueryParameter("DurationSeconds", String.valueOf(durationSeconds));

if (roleArn != null) {
urlBuilder.addQueryParameter("RoleArn", roleArn);
}

if (roleSessionName != null) {
urlBuilder.addQueryParameter("RoleSessionName", roleSessionName);
}

if (policy != null) {
urlBuilder.addQueryParameter("Policy", policy);
}

newUrlBuilder(url, "AssumeRole", durationSeconds, policy, roleArn, roleSessionName);
if (externalId != null) {
urlBuilder.addQueryParameter("ExternalId", externalId);
}
Expand All @@ -121,40 +103,29 @@ public AssumeRoleProvider(
}

@Override
public synchronized Credentials fetch() {
if (credentials != null && !credentials.isExpired()) {
return credentials;
}

protected Request getRequest() {
try {
Request request =
Signer.signV4(
this.request
.newBuilder()
.header("x-amz-date", ZonedDateTime.now().format(Time.AMZ_DATE_FORMAT))
.build(),
region,
accessKey,
secretKey,
contentSha256);
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IllegalStateException(
"STS service failed with HTTP status code " + response.code());
}

AssumeRoleResponse result =
Xml.unmarshal(AssumeRoleResponse.class, response.body().charStream());
credentials = result.credentials();
return credentials;
}
} catch (XmlParserException | IOException e) {
throw new IllegalStateException("Unable to parse STS response", e);
return Signer.signV4(
this.request
.newBuilder()
.header("x-amz-date", ZonedDateTime.now().format(Time.AMZ_DATE_FORMAT))
.build(),
region,
accessKey,
secretKey,
contentSha256);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new IllegalStateException("Signature calculation failed", e);
}
}

@Override
protected Credentials parseResponse(Response response) throws XmlParserException, IOException {
AssumeRoleResponse result =
Xml.unmarshal(AssumeRoleResponse.class, response.body().charStream());
return result.credentials();
}

/** Object representation of response XML of AssumeRole API. */
@Root(name = "AssumeRoleResponse", strict = false)
@Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/")
Expand Down
42 changes: 40 additions & 2 deletions api/src/main/java/io/minio/credentials/ClientGrantsProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@

package io.minio.credentials;

import io.minio.Xml;
import io.minio.errors.XmlParserException;
import java.io.IOException;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Path;
import org.simpleframework.xml.Root;

/**
* Credential provider using <a
Expand All @@ -36,7 +45,36 @@ public ClientGrantsProvider(
super(supplier, stsEndpoint, durationSeconds, policy, null, null, customHttpClient);
}

protected boolean isWebIdentity() {
return false;
@Override
protected HttpUrl.Builder newUrlBuilder(Jwt jwt) {
HttpUrl.Builder urlBuilder =
newUrlBuilder(
stsEndpoint,
"AssumeRoleWithClientGrants",
getDurationSeconds(jwt.expiry()),
policy,
null,
null);
return urlBuilder.addQueryParameter("Token", jwt.token());
}

@Override
protected Credentials parseResponse(Response response) throws XmlParserException, IOException {
ClientGrantsResponse result =
Xml.unmarshal(ClientGrantsResponse.class, response.body().charStream());
return result.credentials();
}

/** Object representation of response XML of AssumeRoleWithClientGrants API. */
@Root(name = "AssumeRoleWithClientGrantsResponse", strict = false)
@Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/")
public static class ClientGrantsResponse {
@Path(value = "AssumeRoleWithClientGrantsResult")
@Element(name = "Credentials")
private Credentials credentials;

public Credentials credentials() {
return credentials;
}
}
}
Loading

0 comments on commit d0cd54b

Please sign in to comment.