mirror of https://github.com/apache/jclouds.git
Configure the Graph RBAC API and allow mocking service endpoints
This commit is contained in:
parent
6c759930d7
commit
3efce9a3a5
|
@ -24,6 +24,7 @@ import org.jclouds.azurecompute.arm.domain.ServicePrincipal;
|
||||||
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
|
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
|
||||||
import org.jclouds.azurecompute.arm.features.DeploymentApi;
|
import org.jclouds.azurecompute.arm.features.DeploymentApi;
|
||||||
import org.jclouds.azurecompute.arm.features.DiskApi;
|
import org.jclouds.azurecompute.arm.features.DiskApi;
|
||||||
|
import org.jclouds.azurecompute.arm.features.GraphRBACApi;
|
||||||
import org.jclouds.azurecompute.arm.features.ImageApi;
|
import org.jclouds.azurecompute.arm.features.ImageApi;
|
||||||
import org.jclouds.azurecompute.arm.features.JobApi;
|
import org.jclouds.azurecompute.arm.features.JobApi;
|
||||||
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
|
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
|
||||||
|
@ -242,6 +243,15 @@ public interface AzureComputeApi extends Closeable {
|
||||||
@Delegate
|
@Delegate
|
||||||
MetricDefinitionsApi getMetricsDefinitionsApi(@PathParam("resourceid") String resourceid);
|
MetricDefinitionsApi getMetricsDefinitionsApi(@PathParam("resourceid") String resourceid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Azure Active Directory Graph API provides programmatic access to Azure
|
||||||
|
* AD through REST API endpoints.
|
||||||
|
*
|
||||||
|
* @see <a href="https://docs.microsoft.com/en-us/rest/api/graphrbac/">docs</a>
|
||||||
|
*/
|
||||||
|
@Delegate
|
||||||
|
GraphRBACApi getGraphRBACApi();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the information about the current service principal.
|
* Returns the information about the current service principal.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -37,11 +37,11 @@ import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule.CurrentServicePrincipal;
|
|
||||||
import org.jclouds.azurecompute.arm.domain.Region;
|
import org.jclouds.azurecompute.arm.domain.Region;
|
||||||
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
|
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
|
||||||
import org.jclouds.azurecompute.arm.features.DeploymentApi;
|
import org.jclouds.azurecompute.arm.features.DeploymentApi;
|
||||||
import org.jclouds.azurecompute.arm.features.DiskApi;
|
import org.jclouds.azurecompute.arm.features.DiskApi;
|
||||||
|
import org.jclouds.azurecompute.arm.features.GraphRBACApi;
|
||||||
import org.jclouds.azurecompute.arm.features.ImageApi;
|
import org.jclouds.azurecompute.arm.features.ImageApi;
|
||||||
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
|
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
|
||||||
import org.jclouds.azurecompute.arm.features.LocationApi;
|
import org.jclouds.azurecompute.arm.features.LocationApi;
|
||||||
|
@ -125,7 +125,7 @@ public class AzureComputeProviderMetadata extends BaseProviderMetadata {
|
||||||
properties.put(API_VERSION_PREFIX + MetricDefinitionsApi.class.getSimpleName(), "2017-05-01-preview");
|
properties.put(API_VERSION_PREFIX + MetricDefinitionsApi.class.getSimpleName(), "2017-05-01-preview");
|
||||||
properties.put(API_VERSION_PREFIX + MetricsApi.class.getSimpleName(), "2016-09-01");
|
properties.put(API_VERSION_PREFIX + MetricsApi.class.getSimpleName(), "2016-09-01");
|
||||||
properties.put(API_VERSION_PREFIX + VirtualMachineScaleSetApi.class.getSimpleName(), "2017-03-30");
|
properties.put(API_VERSION_PREFIX + VirtualMachineScaleSetApi.class.getSimpleName(), "2017-03-30");
|
||||||
properties.put(API_VERSION_PREFIX + CurrentServicePrincipal.class.getSimpleName(), "1.6");
|
properties.put(API_VERSION_PREFIX + GraphRBACApi.class.getSimpleName(), "1.6");
|
||||||
|
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.azurecompute.arm;
|
package org.jclouds.azurecompute.arm;
|
||||||
|
|
||||||
|
import static org.jclouds.reflect.Reflection2.typeToken;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
@ -32,8 +34,6 @@ import org.jclouds.rest.internal.BaseHttpApiMetadata;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
|
||||||
import static org.jclouds.reflect.Reflection2.typeToken;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link ApiMetadata} for Microsoft Azure Resource Manager REST API
|
* Implementation of {@link ApiMetadata} for Microsoft Azure Resource Manager REST API
|
||||||
*/
|
*/
|
||||||
|
@ -43,9 +43,13 @@ public class AzureManagementApiMetadata extends BaseHttpApiMetadata<AzureCompute
|
||||||
public Builder toBuilder() {
|
public Builder toBuilder() {
|
||||||
return new Builder().fromApiMetadata(this);
|
return new Builder().fromApiMetadata(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
public AzureManagementApiMetadata() {
|
public AzureManagementApiMetadata() {
|
||||||
this(new Builder());
|
this(builder());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AzureManagementApiMetadata(final Builder builder) {
|
protected AzureManagementApiMetadata(final Builder builder) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.jclouds.azurecompute.arm.config;
|
package org.jclouds.azurecompute.arm.config;
|
||||||
|
|
||||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||||
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;
|
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -26,14 +25,10 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.ws.rs.Consumes;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.Path;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.azurecompute.arm.AzureComputeApi;
|
import org.jclouds.azurecompute.arm.AzureComputeApi;
|
||||||
|
import org.jclouds.azurecompute.arm.config.GraphRBAC.GraphRBACForTenant;
|
||||||
import org.jclouds.azurecompute.arm.domain.ServicePrincipal;
|
import org.jclouds.azurecompute.arm.domain.ServicePrincipal;
|
||||||
import org.jclouds.azurecompute.arm.filters.ApiVersionFilter;
|
|
||||||
import org.jclouds.azurecompute.arm.handlers.AzureComputeErrorHandler;
|
import org.jclouds.azurecompute.arm.handlers.AzureComputeErrorHandler;
|
||||||
import org.jclouds.http.HttpErrorHandler;
|
import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.annotation.ClientError;
|
import org.jclouds.http.annotation.ClientError;
|
||||||
|
@ -43,28 +38,22 @@ import org.jclouds.location.suppliers.ImplicitLocationSupplier;
|
||||||
import org.jclouds.location.suppliers.implicit.FirstRegion;
|
import org.jclouds.location.suppliers.implicit.FirstRegion;
|
||||||
import org.jclouds.oauth.v2.config.OAuthConfigFactory;
|
import org.jclouds.oauth.v2.config.OAuthConfigFactory;
|
||||||
import org.jclouds.oauth.v2.config.OAuthScopes;
|
import org.jclouds.oauth.v2.config.OAuthScopes;
|
||||||
import org.jclouds.oauth.v2.filters.OAuthFilter;
|
|
||||||
import org.jclouds.rest.AuthorizationException;
|
import org.jclouds.rest.AuthorizationException;
|
||||||
import org.jclouds.rest.ConfiguresHttpApi;
|
import org.jclouds.rest.ConfiguresHttpApi;
|
||||||
import org.jclouds.rest.annotations.Endpoint;
|
|
||||||
import org.jclouds.rest.annotations.OnlyElement;
|
|
||||||
import org.jclouds.rest.annotations.QueryParams;
|
|
||||||
import org.jclouds.rest.annotations.RequestFilters;
|
|
||||||
import org.jclouds.rest.annotations.SelectJson;
|
|
||||||
import org.jclouds.rest.config.HttpApiModule;
|
import org.jclouds.rest.config.HttpApiModule;
|
||||||
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
|
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.base.Suppliers;
|
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import com.google.inject.Scopes;
|
import com.google.inject.Scopes;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
@ConfiguresHttpApi
|
@ConfiguresHttpApi
|
||||||
public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> {
|
public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> {
|
||||||
|
|
||||||
private static final Pattern OAUTH_TENANT_PATTERN = Pattern
|
private static final Pattern OAUTH_TENANT_PATTERN = Pattern
|
||||||
.compile("https://login.microsoftonline.com/([^/]+)/oauth2/token");
|
.compile("https://login.microsoft(?:online)?.com/([^/]+)/oauth2/token");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void bindErrorHandlers() {
|
protected void bindErrorHandlers() {
|
||||||
|
@ -82,15 +71,20 @@ public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
super.configure();
|
super.configure();
|
||||||
bindHttpApi(binder(), CurrentServicePrincipal.class);
|
|
||||||
bind(OAuthScopes.class).toInstance(OAuthScopes.NoScopes.create());
|
bind(OAuthScopes.class).toInstance(OAuthScopes.NoScopes.create());
|
||||||
bind(OAuthConfigFactory.class).to(AzureOAuthConfigFactory.class).in(Scopes.SINGLETON);
|
bind(OAuthConfigFactory.class).to(AzureOAuthConfigFactory.class).in(Scopes.SINGLETON);
|
||||||
|
bindServiceEndpoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void bindServiceEndpoints() {
|
||||||
|
bind(new TypeLiteral<Supplier<URI>>() {
|
||||||
|
}).annotatedWith(GraphRBAC.class).to(GraphRBACForTenant.class).in(Scopes.SINGLETON);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@Tenant
|
@Tenant
|
||||||
protected String provideTenant(@Named("oauth.endpoint") final String oauthEndpoint) {
|
protected final String provideTenant(@Named("oauth.endpoint") final String oauthEndpoint) {
|
||||||
Matcher m = OAUTH_TENANT_PATTERN.matcher(oauthEndpoint);
|
Matcher m = OAUTH_TENANT_PATTERN.matcher(oauthEndpoint);
|
||||||
if (!m.matches()) {
|
if (!m.matches()) {
|
||||||
throw new IllegalArgumentException("Could not parse tenantId from: " + oauthEndpoint);
|
throw new IllegalArgumentException("Could not parse tenantId from: " + oauthEndpoint);
|
||||||
|
@ -100,37 +94,15 @@ public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@GraphRBAC
|
protected final Supplier<ServicePrincipal> provideServicePrincipal(final AzureComputeApi api,
|
||||||
protected Supplier<URI> graphRBACEndpoint(@Tenant String tenantId) {
|
|
||||||
return Suppliers.ofInstance(URI.create(GraphRBAC.ENDPOINT + tenantId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
protected Supplier<ServicePrincipal> provideServicePrincipal(final CurrentServicePrincipal currentServicePrincipal,
|
|
||||||
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
|
AtomicReference<AuthorizationException> authException, @Named(PROPERTY_SESSION_INTERVAL) long seconds) {
|
||||||
// This supplier must be defensive against any auth exception.
|
// This supplier must be defensive against any auth exception.
|
||||||
return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
|
return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
|
||||||
new Supplier<ServicePrincipal>() {
|
new Supplier<ServicePrincipal>() {
|
||||||
@Override
|
@Override
|
||||||
public ServicePrincipal get() {
|
public ServicePrincipal get() {
|
||||||
return currentServicePrincipal.get();
|
return api.getGraphRBACApi().getCurrentServicePrincipal();
|
||||||
}
|
}
|
||||||
}, seconds, TimeUnit.SECONDS);
|
}, seconds, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestFilters({ OAuthFilter.class, ApiVersionFilter.class })
|
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
|
||||||
@Endpoint(GraphRBAC.class)
|
|
||||||
@OAuthResource(GraphRBAC.ENDPOINT)
|
|
||||||
public interface CurrentServicePrincipal {
|
|
||||||
|
|
||||||
@Named("servicePrincipal:get")
|
|
||||||
@GET
|
|
||||||
@Path("/servicePrincipals")
|
|
||||||
@QueryParams(keys = "$filter", values = "appId eq '{jclouds.identity}'")
|
|
||||||
@SelectJson("value")
|
|
||||||
@OnlyElement
|
|
||||||
ServicePrincipal get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,13 @@ import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the Graph RBAC API endpoint for the current tenant.
|
* Provides the Graph RBAC API endpoint for the current tenant.
|
||||||
*/
|
*/
|
||||||
|
@ -32,4 +36,19 @@ import javax.inject.Qualifier;
|
||||||
public @interface GraphRBAC {
|
public @interface GraphRBAC {
|
||||||
|
|
||||||
String ENDPOINT = "https://graph.windows.net/";
|
String ENDPOINT = "https://graph.windows.net/";
|
||||||
|
|
||||||
|
static class GraphRBACForTenant implements Supplier<URI> {
|
||||||
|
private final String tenantId;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
GraphRBACForTenant(@Tenant String tenantId) {
|
||||||
|
this.tenantId = tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI get() {
|
||||||
|
return URI.create(GraphRBAC.ENDPOINT + tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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
|
||||||
|
*
|
||||||
|
* http://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 org.jclouds.azurecompute.arm.features;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.jclouds.azurecompute.arm.config.GraphRBAC;
|
||||||
|
import org.jclouds.azurecompute.arm.config.OAuthResource;
|
||||||
|
import org.jclouds.azurecompute.arm.domain.ServicePrincipal;
|
||||||
|
import org.jclouds.azurecompute.arm.filters.ApiVersionFilter;
|
||||||
|
import org.jclouds.oauth.v2.filters.OAuthFilter;
|
||||||
|
import org.jclouds.rest.annotations.Endpoint;
|
||||||
|
import org.jclouds.rest.annotations.OnlyElement;
|
||||||
|
import org.jclouds.rest.annotations.QueryParams;
|
||||||
|
import org.jclouds.rest.annotations.RequestFilters;
|
||||||
|
import org.jclouds.rest.annotations.SelectJson;
|
||||||
|
|
||||||
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
|
@RequestFilters({ OAuthFilter.class, ApiVersionFilter.class })
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Endpoint(GraphRBAC.class)
|
||||||
|
@OAuthResource(GraphRBAC.ENDPOINT)
|
||||||
|
public interface GraphRBACApi {
|
||||||
|
|
||||||
|
@Named("servicePrincipal:get")
|
||||||
|
@GET
|
||||||
|
@Path("/servicePrincipals")
|
||||||
|
@QueryParams(keys = "$filter", values = "appId eq '{jclouds.identity}'")
|
||||||
|
@SelectJson("value")
|
||||||
|
@OnlyElement
|
||||||
|
ServicePrincipal getCurrentServicePrincipal();
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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
|
||||||
|
*
|
||||||
|
* http://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 org.jclouds.azurecompute.arm.config;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
import static org.testng.Assert.fail;
|
||||||
|
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Test(groups = "unit", testName = "ParseTenantIdTest")
|
||||||
|
public class ParseTenantIdTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseTenantId() {
|
||||||
|
AzureComputeHttpApiModule module = new AzureComputeHttpApiModule();
|
||||||
|
|
||||||
|
assertEquals(module.provideTenant("https://login.microsoftonline.com/tenantId/oauth2/token"), "tenantId");
|
||||||
|
assertEquals(module.provideTenant("https://login.microsoft.com/tenant2/oauth2/token"), "tenant2");
|
||||||
|
|
||||||
|
assertInvalid(module, "https://login.microsoftonline.com/a/b/c/oauth2/token");
|
||||||
|
assertInvalid(module, "https://login.microsoft.com/a/b/c/oauth2/token");
|
||||||
|
assertInvalid(module, "https://login.microsoftonline.com//oauth2/token");
|
||||||
|
assertInvalid(module, "https://login.microsoft.com//oauth2/token");
|
||||||
|
assertInvalid(module, "https://login.microsoftabc.com/tenant/oauth2/token");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertInvalid(AzureComputeHttpApiModule module, String endpoint) {
|
||||||
|
try {
|
||||||
|
module.provideTenant(endpoint);
|
||||||
|
fail("Expected an IllegalArgumentException for endpoint: " + endpoint);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
assertEquals(ex.getMessage(), "Could not parse tenantId from: " + endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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
|
||||||
|
*
|
||||||
|
* http://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 org.jclouds.azurecompute.arm.features;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jclouds.azurecompute.arm.domain.ServicePrincipal;
|
||||||
|
import org.jclouds.azurecompute.arm.internal.BaseAzureComputeApiMockTest;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
@Test(groups = "unit", testName = "GraphRBACApiMockTest", singleThreaded = true)
|
||||||
|
public class GraphRBACApiMockTest extends BaseAzureComputeApiMockTest {
|
||||||
|
|
||||||
|
public void testGetCurrentServicePrincipal() throws IOException, InterruptedException {
|
||||||
|
server.enqueue(jsonResponse("/serviceprincipals.json"));
|
||||||
|
|
||||||
|
ServicePrincipal sp = api.getGraphRBACApi().getCurrentServicePrincipal();
|
||||||
|
|
||||||
|
assertEquals(sp.appId(), "applicationId");
|
||||||
|
assertSent(server, "GET", "/graphrbac/tenant-id/servicePrincipals?$filter=appId%20eq%20%27mock%27&api-version=1.6");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,7 +17,6 @@
|
||||||
package org.jclouds.azurecompute.arm.features;
|
package org.jclouds.azurecompute.arm.features;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import org.jclouds.azurecompute.arm.domain.DataDisk;
|
|
||||||
import org.jclouds.azurecompute.arm.domain.Extension;
|
import org.jclouds.azurecompute.arm.domain.Extension;
|
||||||
import org.jclouds.azurecompute.arm.domain.ExtensionProfile;
|
import org.jclouds.azurecompute.arm.domain.ExtensionProfile;
|
||||||
import org.jclouds.azurecompute.arm.domain.ExtensionProfileSettings;
|
import org.jclouds.azurecompute.arm.domain.ExtensionProfileSettings;
|
||||||
|
@ -69,8 +68,6 @@ public class VirtualMachineScaleSetApiLiveTest extends BaseAzureComputeApiLiveTe
|
||||||
|
|
||||||
private String subscriptionid;
|
private String subscriptionid;
|
||||||
private String vmssName;
|
private String vmssName;
|
||||||
private VirtualMachineScaleSetSKU SKU;
|
|
||||||
private String nicName;
|
|
||||||
private String virtualNetworkName;
|
private String virtualNetworkName;
|
||||||
private String subnetId;
|
private String subnetId;
|
||||||
private Subnet subnet;
|
private Subnet subnet;
|
||||||
|
@ -151,25 +148,6 @@ public class VirtualMachineScaleSetApiLiveTest extends BaseAzureComputeApiLiveTe
|
||||||
return VirtualMachineScaleSetUpgradePolicy.create("Manual");
|
return VirtualMachineScaleSetUpgradePolicy.create("Manual");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DataDisk> getDataDisks() {
|
|
||||||
List<DataDisk> datadisks = new ArrayList<DataDisk>();
|
|
||||||
|
|
||||||
datadisks.add(DataDisk.create(null, "10", 1, null,
|
|
||||||
null, "FromImage",
|
|
||||||
"None", getManagedDiskParameters(),
|
|
||||||
null));
|
|
||||||
|
|
||||||
return datadisks;
|
|
||||||
}
|
|
||||||
|
|
||||||
private StorageProfile getStorageProfile() {
|
|
||||||
return StorageProfile.create(getWindowsImageReference(), getWindowsOSDisk(), getDataDisks());
|
|
||||||
}
|
|
||||||
|
|
||||||
private StorageProfile getWindowsStorageProfile_Default() {
|
|
||||||
return StorageProfile.create(getWindowsImageReference(), getWindowsOSDisk(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private StorageProfile getLinuxStorageProfile_Default() {
|
private StorageProfile getLinuxStorageProfile_Default() {
|
||||||
return StorageProfile.create(getLinuxImageReference(), getLinuxOSDisk(), null);
|
return StorageProfile.create(getLinuxImageReference(), getLinuxOSDisk(), null);
|
||||||
}
|
}
|
||||||
|
@ -178,21 +156,11 @@ public class VirtualMachineScaleSetApiLiveTest extends BaseAzureComputeApiLiveTe
|
||||||
return ManagedDiskParameters.create(null, "Standard_LRS");
|
return ManagedDiskParameters.create(null, "Standard_LRS");
|
||||||
}
|
}
|
||||||
|
|
||||||
private OSDisk getWindowsOSDisk() {
|
|
||||||
return OSDisk.create("Windows", null, null, null, "FromImage",
|
|
||||||
null, getManagedDiskParameters(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OSDisk getLinuxOSDisk() {
|
private OSDisk getLinuxOSDisk() {
|
||||||
return OSDisk.create("Linux", null, null, null, "FromImage",
|
return OSDisk.create("Linux", null, null, null, "FromImage",
|
||||||
null, getManagedDiskParameters(), null);
|
null, getManagedDiskParameters(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImageReference getWindowsImageReference() {
|
|
||||||
return ImageReference.create(null, "Microsoft.Windows", "Windows2016",
|
|
||||||
"Enterprise", "latest");
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImageReference getLinuxImageReference() {
|
private ImageReference getLinuxImageReference() {
|
||||||
return ImageReference.create(null, "Canonical", "UbuntuServer",
|
return ImageReference.create(null, "Canonical", "UbuntuServer",
|
||||||
"16.04-LTS", "latest");
|
"16.04-LTS", "latest");
|
||||||
|
|
|
@ -15,30 +15,44 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jclouds.azurecompute.arm.internal;
|
package org.jclouds.azurecompute.arm.internal;
|
||||||
|
|
||||||
|
import static com.google.common.base.Predicates.not;
|
||||||
|
import static com.google.common.collect.Iterables.filter;
|
||||||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||||
|
import static org.assertj.core.util.Sets.newHashSet;
|
||||||
import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS;
|
import static org.jclouds.oauth.v2.config.CredentialType.BEARER_TOKEN_CREDENTIALS;
|
||||||
import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
|
import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
import org.jclouds.ContextBuilder;
|
||||||
import org.jclouds.azurecompute.arm.AzureComputeApi;
|
import org.jclouds.azurecompute.arm.AzureComputeApi;
|
||||||
import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
|
import org.jclouds.azurecompute.arm.AzureComputeProviderMetadata;
|
||||||
|
import org.jclouds.azurecompute.arm.AzureManagementApiMetadata;
|
||||||
|
import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule;
|
||||||
|
import org.jclouds.azurecompute.arm.config.GraphRBAC;
|
||||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
||||||
import org.jclouds.date.DateService;
|
import org.jclouds.date.DateService;
|
||||||
|
import org.jclouds.providers.ProviderMetadata;
|
||||||
import org.jclouds.rest.ApiContext;
|
import org.jclouds.rest.ApiContext;
|
||||||
|
import org.jclouds.rest.ConfiguresHttpApi;
|
||||||
import org.testng.annotations.AfterMethod;
|
import org.testng.annotations.AfterMethod;
|
||||||
import org.testng.annotations.BeforeMethod;
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||||
|
@ -48,8 +62,6 @@ public class BaseAzureComputeApiMockTest {
|
||||||
private static final String MOCK_BEARER_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9";
|
private static final String MOCK_BEARER_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSIsImtpZCI6Ik1uQ19WWmNBVGZNNXBPWWlKSE1iYTlnb0VLWSJ9";
|
||||||
private static final String DEFAULT_ENDPOINT = new AzureComputeProviderMetadata().getEndpoint();
|
private static final String DEFAULT_ENDPOINT = new AzureComputeProviderMetadata().getEndpoint();
|
||||||
|
|
||||||
private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor()));
|
|
||||||
|
|
||||||
protected MockWebServer server;
|
protected MockWebServer server;
|
||||||
protected AzureComputeApi api;
|
protected AzureComputeApi api;
|
||||||
protected ApiContext<AzureComputeApi> context;
|
protected ApiContext<AzureComputeApi> context;
|
||||||
|
@ -62,25 +74,42 @@ public class BaseAzureComputeApiMockTest {
|
||||||
public void start() throws IOException {
|
public void start() throws IOException {
|
||||||
server = new MockWebServer();
|
server = new MockWebServer();
|
||||||
server.play();
|
server.play();
|
||||||
AzureComputeProviderMetadata pm = AzureComputeProviderMetadata.builder().build();
|
|
||||||
context = ContextBuilder.newBuilder(pm)
|
context = ContextBuilder.newBuilder(testProviderMetadata())
|
||||||
.credentials("", MOCK_BEARER_TOKEN)
|
.credentials("mock", MOCK_BEARER_TOKEN)
|
||||||
.endpoint(server.getUrl("/").toString() + "subscriptions/SUBSCRIPTIONID")
|
.endpoint(server.getUrl("/").toString() + "subscriptions/SUBSCRIPTIONID")
|
||||||
.modules(modules)
|
.modules(setupModules())
|
||||||
.overrides(setupProperties())
|
.overrides(setupProperties())
|
||||||
.build();
|
.build();
|
||||||
api = context.getApi();
|
api = context.getApi();
|
||||||
dateService = context.utils().injector().getInstance(DateService.class);
|
dateService = context.utils().injector().getInstance(DateService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ProviderMetadata testProviderMetadata() {
|
||||||
|
// Omit the default HTTP API modules to allow overriding
|
||||||
|
Set<Class<? extends Module>> defaultModules = newHashSet(filter(
|
||||||
|
new AzureManagementApiMetadata().getDefaultModules(),
|
||||||
|
not(Predicates.<Class<? extends Module>> equalTo(AzureComputeHttpApiModule.class))));
|
||||||
|
return AzureComputeProviderMetadata.builder()
|
||||||
|
.apiMetadata(AzureManagementApiMetadata.builder().defaultModules(defaultModules).build()).build();
|
||||||
|
}
|
||||||
|
|
||||||
protected Properties setupProperties() {
|
protected Properties setupProperties() {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
|
|
||||||
properties.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString());
|
properties.put(CREDENTIAL_TYPE, BEARER_TOKEN_CREDENTIALS.toString());
|
||||||
properties.put("oauth.endpoint", "https://login.microsoftonline.com/tenant-id/oauth2/token");
|
properties.put("oauth.endpoint", "https://login.microsoftonline.com/tenant-id/oauth2/token");
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Set<Module> setupModules() {
|
||||||
|
ImmutableSet.Builder<Module> modules = ImmutableSet.builder();
|
||||||
|
modules.add(new ExecutorServiceModule(sameThreadExecutor()));
|
||||||
|
// Override the default HTTP module to accomodate custom bindings for the
|
||||||
|
// hardcoded endpoints such as the Graph RBAC API one.
|
||||||
|
modules.add(new TestAzureComputeHttpApiModule(server));
|
||||||
|
return modules.build();
|
||||||
|
}
|
||||||
|
|
||||||
@AfterMethod(alwaysRun = true)
|
@AfterMethod(alwaysRun = true)
|
||||||
public void stop() throws IOException {
|
public void stop() throws IOException {
|
||||||
server.shutdown();
|
server.shutdown();
|
||||||
|
@ -107,21 +136,22 @@ public class BaseAzureComputeApiMockTest {
|
||||||
return new MockResponse().setStatus("HTTP/1.1 202 Accepted");
|
return new MockResponse().setStatus("HTTP/1.1 202 Accepted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected MockResponse response204() {
|
protected MockResponse response204() {
|
||||||
return new MockResponse().setStatus("HTTP/1.1 204 No Content");
|
return new MockResponse().setStatus("HTTP/1.1 204 No Content");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MockResponse response202WithHeader() {
|
protected MockResponse response202WithHeader() {
|
||||||
return new MockResponse()
|
return new MockResponse()
|
||||||
.setStatus("HTTP/1.1 202 Accepted")
|
.setStatus("HTTP/1.1 202 Accepted")
|
||||||
.addHeader("Location", "https://management.azure.com/subscriptions/SUBSCRIPTIONID/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1SVEVTVC1DRU5UUkFMVVMiLCJqb2JMb2NhdGlvbiI6ImNlbnRyYWx1cyJ9?api-version=2014-04-01");
|
.addHeader(
|
||||||
|
"Location",
|
||||||
|
"https://management.azure.com/subscriptions/SUBSCRIPTIONID/operationresults/eyJqb2JJZCI6IlJFU09VUkNFR1JPVVBERUxFVElPTkpPQi1SVEVTVC1DRU5UUkFMVVMiLCJqb2JMb2NhdGlvbiI6ImNlbnRyYWx1cyJ9?api-version=2014-04-01");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String stringFromResource(String resourceName) {
|
protected String stringFromResource(String resourceName) {
|
||||||
try {
|
try {
|
||||||
return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8)
|
return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8).replace(DEFAULT_ENDPOINT,
|
||||||
.replace(DEFAULT_ENDPOINT, url(""));
|
url(""));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw Throwables.propagate(e);
|
throw Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
|
@ -137,10 +167,27 @@ public class BaseAzureComputeApiMockTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json)
|
protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
RecordedRequest request = assertSent(server, method, path);
|
RecordedRequest request = assertSent(server, method, path);
|
||||||
assertEquals(request.getHeader("Content-Type"), "application/json");
|
assertEquals(request.getHeader("Content-Type"), "application/json");
|
||||||
assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json));
|
assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json));
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfiguresHttpApi
|
||||||
|
private static class TestAzureComputeHttpApiModule extends AzureComputeHttpApiModule {
|
||||||
|
private final MockWebServer server;
|
||||||
|
|
||||||
|
public TestAzureComputeHttpApiModule(MockWebServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void bindServiceEndpoints() {
|
||||||
|
// Override the hardcoded service URIs to allow mocking service endpoints
|
||||||
|
bind(new TypeLiteral<Supplier<URI>>() {
|
||||||
|
}).annotatedWith(GraphRBAC.class).toInstance(
|
||||||
|
Suppliers.ofInstance(URI.create(server.getUrl("/graphrbac").toString() + "/tenant-id")));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
{
|
||||||
|
"odata.metadata": "https://graph.windows.net/tenantId/$metadata#directoryObjects/Microsoft.DirectoryServices.ServicePrincipal",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"odata.type": "Microsoft.DirectoryServices.ServicePrincipal",
|
||||||
|
"objectType": "ServicePrincipal",
|
||||||
|
"objectId": "objectId",
|
||||||
|
"deletionTimestamp": null,
|
||||||
|
"accountEnabled": true,
|
||||||
|
"addIns": [],
|
||||||
|
"alternativeNames": [],
|
||||||
|
"appDisplayName": "jclouds",
|
||||||
|
"appId": "applicationId",
|
||||||
|
"appOwnerTenantId": "tenantId",
|
||||||
|
"appRoleAssignmentRequired": false,
|
||||||
|
"appRoles": [],
|
||||||
|
"displayName": "jclouds",
|
||||||
|
"errorUrl": null,
|
||||||
|
"homepage": "https://jclouds.apache.org",
|
||||||
|
"keyCredentials": [],
|
||||||
|
"logoutUrl": null,
|
||||||
|
"oauth2Permissions": [
|
||||||
|
{
|
||||||
|
"adminConsentDescription": "Allow the application to access jclouds on behalf of the signed-in user.",
|
||||||
|
"adminConsentDisplayName": "Access jclouds",
|
||||||
|
"id": "id",
|
||||||
|
"isEnabled": true,
|
||||||
|
"type": "User",
|
||||||
|
"userConsentDescription": "Allow the application to access jclouds on your behalf.",
|
||||||
|
"userConsentDisplayName": "Access jclouds",
|
||||||
|
"value": "user_impersonation"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"passwordCredentials": [],
|
||||||
|
"preferredTokenSigningKeyThumbprint": null,
|
||||||
|
"publisherName": "Default Directory",
|
||||||
|
"replyUrls": [
|
||||||
|
"https://jclouds.apache.org"
|
||||||
|
],
|
||||||
|
"samlMetadataUrl": null,
|
||||||
|
"servicePrincipalNames": [
|
||||||
|
"https://jclouds.onmicrosoft.com/jcloudsid",
|
||||||
|
"applicationId"
|
||||||
|
],
|
||||||
|
"servicePrincipalType": "Application",
|
||||||
|
"tags": [
|
||||||
|
"WindowsAzureActiveDirectoryIntegratedApp"
|
||||||
|
],
|
||||||
|
"tokenEncryptionKeyId": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue