Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package com.microsoft.azure.management.resources.fluentcore.arm;

import com.microsoft.azure.credentials.AzureTokenCredentials;
import com.microsoft.rest.LogLevel;
import okhttp3.Authenticator;
import okhttp3.ConnectionPool;
Expand Down Expand Up @@ -38,6 +39,14 @@ public interface AzureConfigurable<T extends AzureConfigurable<T>> {
*/
T withInterceptor(Interceptor interceptor);

/**
* Set the cross-tenant auxiliary credentials for Azure which can hold up to three.
*
* @param tokens the AzureTokenCredentials list
* @return the configurable object itself
*/
T withAuxiliaryCredentials(AzureTokenCredentials... tokens);

/**
* Specify the user agent header.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.microsoft.azure.AzureResponseBuilder;
import com.microsoft.azure.credentials.AzureTokenCredentials;
import com.microsoft.azure.management.resources.fluentcore.arm.AzureConfigurable;
import com.microsoft.azure.management.resources.fluentcore.utils.AuxiliaryCredentialsInterceptor;
import com.microsoft.azure.management.resources.fluentcore.utils.ProviderRegistrationInterceptor;
import com.microsoft.azure.management.resources.fluentcore.utils.ResourceManagerThrottlingInterceptor;
import com.microsoft.azure.serializer.AzureJacksonAdapter;
Expand Down Expand Up @@ -54,6 +55,19 @@ public T withInterceptor(Interceptor interceptor) {
return (T) this;
}

@SuppressWarnings("unchecked")
@Override
public T withAuxiliaryCredentials(AzureTokenCredentials... tokens) {
if (tokens != null) {
if (tokens.length > 3) {
throw new IllegalArgumentException("Only can hold up to three auxiliary tokens.");
}
AuxiliaryCredentialsInterceptor interceptor = new AuxiliaryCredentialsInterceptor(tokens);
this.restClientBuilder = this.restClientBuilder.withInterceptor(interceptor);
}
return (T) this;
}

@SuppressWarnings("unchecked")
@Override
public T withUserAgent(String userAgent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/

package com.microsoft.azure.management.resources.fluentcore.utils;

import com.microsoft.azure.CloudError;
import com.microsoft.azure.credentials.AzureTokenCredentials;
import com.microsoft.azure.serializer.AzureJacksonAdapter;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;

import java.io.IOException;

/**
* An interceptor for cross-tenant authorization in Azure.
*/
public final class AuxiliaryCredentialsInterceptor implements Interceptor {

private static final String AUTHORIZATION_AUXILIARY_HEADER = "x-ms-authorization-auxiliary";
private static final String LINKED_AUTHORIZATION_FAILED = "LinkedAuthorizationFailed";
private static final String SCHEMA = "Bearer";

private final AzureTokenCredentials[] tokenCredentials;

/**
* Initialize an auxiliary interceptor with the list of AzureTokenCredentials.
*
* @param credentials the AzureTokenCredentials list
*/
public AuxiliaryCredentialsInterceptor(AzureTokenCredentials... credentials) {
this.tokenCredentials = credentials;
}

@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful() && tokenCredentials != null && this.tokenCredentials.length > 0) {
String content = errorBody(response.body());
AzureJacksonAdapter jacksonAdapter = new AzureJacksonAdapter();
CloudError cloudError = jacksonAdapter.deserialize(content, CloudError.class);
if (cloudError != null && LINKED_AUTHORIZATION_FAILED.equals(cloudError.code())) {
StringBuffer buff = new StringBuffer();
for (int i = 0; i < tokenCredentials.length; i++) {
buff.append(SCHEMA);
buff.append(" ");
buff.append(tokenCredentials[i].getToken(chain.request().url().scheme() + "://" + chain.request().url().host()));
if (i < tokenCredentials.length - 1) {
buff.append(";");
}
}
Request request = chain.request().newBuilder()
.header(AUTHORIZATION_AUXILIARY_HEADER, buff.toString())
.build();
// Retry
return chain.proceed(request);
}
}
return response;
}

private String errorBody(ResponseBody responseBody) throws IOException {
if (responseBody == null) {
return null;
}
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
return buffer.clone().readUtf8();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
import com.microsoft.rest.RestClient;
import okhttp3.Interceptor;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;

import java.io.IOException;
import java.util.regex.Matcher;
Expand All @@ -42,7 +39,7 @@ public ProviderRegistrationInterceptor(AzureTokenCredentials credentials) {
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (!response.isSuccessful()) {
String content = errorBody(response.body());
String content = Utils.getResponseBodyInString(response.body());
AzureJacksonAdapter jacksonAdapter = new AzureJacksonAdapter();
CloudError cloudError = jacksonAdapter.deserialize(content, CloudError.class);
if (cloudError != null && "MissingSubscriptionRegistration".equals(cloudError.code())) {
Expand Down Expand Up @@ -76,16 +73,6 @@ public Response intercept(Chain chain) throws IOException {
return response;
}

private String errorBody(ResponseBody responseBody) throws IOException {
if (responseBody == null) {
return null;
}
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
return buffer.clone().readUtf8();
}

private Provider registerProvider(String namespace, ResourceManager resourceManager) {
return resourceManager.providers().register(namespace);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import com.microsoft.azure.management.resources.implementation.PageImpl;
import com.microsoft.rest.RestClient;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import retrofit2.Retrofit;
import retrofit2.http.GET;
import retrofit2.http.Url;
Expand Down Expand Up @@ -252,6 +254,22 @@ public static String resourceGroupId(String id) {
resourceId.resourceGroupName());
}

/**
* Get the response body as string.
* @param responseBody response body object
* @return response body in string
* @throws IOException throw IOException
*/
public static String getResponseBodyInString(ResponseBody responseBody) throws IOException {
if (responseBody == null) {
return null;
}
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
return buffer.clone().readUtf8();
}

private Utils() {
}
}