Skip to content
Permalink
Browse files
Injectable current service principal
  • Loading branch information
nacx committed Dec 1, 2017
1 parent 13a0440 commit 86fc88f1b07c9d964d554ab32eaf7c07228a127f
Showing 11 changed files with 396 additions and 57 deletions.
@@ -20,30 +20,34 @@

import javax.ws.rs.PathParam;

import org.jclouds.azurecompute.arm.domain.ServicePrincipal;
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
import org.jclouds.azurecompute.arm.features.DeploymentApi;
import org.jclouds.azurecompute.arm.features.DiskApi;
import org.jclouds.azurecompute.arm.features.ImageApi;
import org.jclouds.azurecompute.arm.features.JobApi;
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
import org.jclouds.azurecompute.arm.features.LocationApi;
import org.jclouds.azurecompute.arm.features.MetricDefinitionsApi;
import org.jclouds.azurecompute.arm.features.MetricsApi;
import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
import org.jclouds.azurecompute.arm.features.OSImageApi;
import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
import org.jclouds.azurecompute.arm.features.ResourceProviderApi;
import org.jclouds.azurecompute.arm.features.StorageAccountApi;
import org.jclouds.azurecompute.arm.features.SubnetApi;
import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
import org.jclouds.azurecompute.arm.features.VMSizeApi;
import org.jclouds.azurecompute.arm.features.VirtualMachineApi;
import org.jclouds.azurecompute.arm.features.VirtualMachineScaleSetApi;
import org.jclouds.azurecompute.arm.features.VMSizeApi;
import org.jclouds.azurecompute.arm.features.OSImageApi;
import org.jclouds.azurecompute.arm.features.DeploymentApi;
import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
import org.jclouds.azurecompute.arm.features.ResourceProviderApi;
import org.jclouds.azurecompute.arm.features.DiskApi;
import org.jclouds.azurecompute.arm.features.ImageApi;
import org.jclouds.azurecompute.arm.features.MetricsApi;
import org.jclouds.azurecompute.arm.features.MetricDefinitionsApi;
import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
import org.jclouds.rest.annotations.Delegate;

import com.google.common.base.Supplier;
import com.google.inject.Provides;

/**
* The Azure Resource Manager API is a REST API for managing your services and deployments.
* <p>
@@ -237,4 +241,10 @@ NetworkSecurityRuleApi getNetworkSecurityRuleApi(@PathParam("resourcegroup") Str
*/
@Delegate
MetricDefinitionsApi getMetricsDefinitionsApi(@PathParam("resourceid") String resourceid);

/**
* Returns the information about the current service principal.
*/
@Provides
Supplier<ServicePrincipal> getServicePrincipal();
}
@@ -37,28 +37,29 @@
import java.net.URI;
import java.util.Properties;

import org.jclouds.azurecompute.arm.config.AzureComputeHttpApiModule.CurrentServicePrincipal;
import org.jclouds.azurecompute.arm.domain.Region;
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
import org.jclouds.azurecompute.arm.features.DeploymentApi;
import org.jclouds.azurecompute.arm.features.DiskApi;
import org.jclouds.azurecompute.arm.features.ImageApi;
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
import org.jclouds.azurecompute.arm.features.LocationApi;
import org.jclouds.azurecompute.arm.features.MetricDefinitionsApi;
import org.jclouds.azurecompute.arm.features.MetricsApi;
import org.jclouds.azurecompute.arm.features.NetworkInterfaceCardApi;
import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
import org.jclouds.azurecompute.arm.features.NetworkSecurityRuleApi;
import org.jclouds.azurecompute.arm.features.OSImageApi;
import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
import org.jclouds.azurecompute.arm.features.PublicIPAddressApi;
import org.jclouds.azurecompute.arm.features.ResourceGroupApi;
import org.jclouds.azurecompute.arm.features.ResourceProviderApi;
import org.jclouds.azurecompute.arm.features.StorageAccountApi;
import org.jclouds.azurecompute.arm.features.SubnetApi;
import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
import org.jclouds.azurecompute.arm.features.VMSizeApi;
import org.jclouds.azurecompute.arm.features.VirtualMachineApi;
import org.jclouds.azurecompute.arm.features.LoadBalancerApi;
import org.jclouds.azurecompute.arm.features.AvailabilitySetApi;
import org.jclouds.azurecompute.arm.features.DiskApi;
import org.jclouds.azurecompute.arm.features.ImageApi;
import org.jclouds.azurecompute.arm.features.MetricDefinitionsApi;
import org.jclouds.azurecompute.arm.features.MetricsApi;
import org.jclouds.azurecompute.arm.features.VirtualMachineScaleSetApi;
import org.jclouds.azurecompute.arm.features.VirtualNetworkApi;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadata;

@@ -124,7 +125,8 @@ public static Properties defaultProperties() {
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 + VirtualMachineScaleSetApi.class.getSimpleName(), "2017-03-30");

properties.put(API_VERSION_PREFIX + CurrentServicePrincipal.class.getSimpleName(), "1.6");

return properties;
}

@@ -16,23 +16,56 @@
*/
package org.jclouds.azurecompute.arm.config;

import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.jclouds.rest.config.BinderUtils.bindHttpApi;

import java.net.URI;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.domain.ServicePrincipal;
import org.jclouds.azurecompute.arm.filters.ApiVersionFilter;
import org.jclouds.azurecompute.arm.handlers.AzureComputeErrorHandler;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.location.suppliers.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.FirstRegion;
import org.jclouds.oauth.v2.config.OAuthConfigFactory;
import org.jclouds.oauth.v2.config.OAuthScopes;
import org.jclouds.oauth.v2.filters.OAuthFilter;
import org.jclouds.rest.AuthorizationException;
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.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.name.Named;

@ConfiguresHttpApi
public class AzureComputeHttpApiModule extends HttpApiModule<AzureComputeApi> {

private static final Pattern OAUTH_TENANT_PATTERN = Pattern
.compile("https://login.microsoftonline.com/([^/]+)/oauth2/token");

@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(AzureComputeErrorHandler.class);
@@ -46,10 +79,58 @@ protected void installLocations() {
bind(ImplicitLocationSupplier.class).to(FirstRegion.class).in(Scopes.SINGLETON);
}


@Override
protected void configure() {
super.configure();
bindHttpApi(binder(), CurrentServicePrincipal.class);
bind(OAuthScopes.class).toInstance(OAuthScopes.NoScopes.create());
bind(OAuthConfigFactory.class).to(AzureOAuthConfigFactory.class).in(Scopes.SINGLETON);
}

@Provides
@Singleton
@Tenant
protected String provideTenant(@Named("oauth.endpoint") final String oauthEndpoint) {
Matcher m = OAUTH_TENANT_PATTERN.matcher(oauthEndpoint);
if (!m.matches()) {
throw new IllegalArgumentException("Could not parse tenantId from: " + oauthEndpoint);
}
return m.group(1);
}

@Provides
@Singleton
@GraphRBAC
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) {
// This supplier must be defensive against any auth exception.
return MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.create(authException,
new Supplier<ServicePrincipal>() {
@Override
public ServicePrincipal get() {
return currentServicePrincipal.get();
}
}, 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();
}
}
@@ -0,0 +1,60 @@
/*
* 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.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;

import org.jclouds.http.HttpRequest;
import org.jclouds.oauth.v2.config.OAuthConfigFactory;
import org.jclouds.oauth.v2.config.OAuthScopes;
import org.jclouds.rest.internal.GeneratedHttpRequest;

import com.google.inject.Inject;
import com.google.inject.name.Named;

public class AzureOAuthConfigFactory implements OAuthConfigFactory {
private final OAuthScopes scopes;

@Named(AUDIENCE)
@Inject(optional = true)
private String audience;

@Named(RESOURCE)
@Inject(optional = true)
private String resource;

@Inject
AzureOAuthConfigFactory(OAuthScopes scopes) {
this.scopes = scopes;
}

@Override
public OAuthConfig forRequest(HttpRequest input) {
OAuthResource customResource = null;
if (input instanceof GeneratedHttpRequest) {
GeneratedHttpRequest request = (GeneratedHttpRequest) input;
customResource = request.getInvocation().getInvokable().getAnnotation(OAuthResource.class);
if (customResource == null) {
customResource = request.getInvocation().getInvokable().getDeclaringClass()
.getAnnotation(OAuthResource.class);
}
}
String oauthResource = customResource != null ? customResource.value() : resource;
return OAuthConfig.create(scopes.forRequest(input), audience, oauthResource);
}
}
@@ -0,0 +1,35 @@
/*
* 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

/**
* Provides the Graph RBAC API endpoint for the current tenant.
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Qualifier
public @interface GraphRBAC {

String ENDPOINT = "https://graph.windows.net/";
}
@@ -0,0 +1,35 @@
/*
* 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

/**
* Configures a custom OAuth resource for certain APIs and methods.
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = { ElementType.TYPE, ElementType.METHOD })
@Qualifier
public @interface OAuthResource {

String value();
}
@@ -0,0 +1,34 @@
/*
* 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

/**
* Qualifies an object that describes the current tenant.
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Qualifier
public @interface Tenant {

}

0 comments on commit 86fc88f

Please sign in to comment.