Commit b39b190a authored by harshit aggarwal's avatar harshit aggarwal Committed by Hema Vishnu Pola [Microsoft]
Browse files

Adding AzureServicePrincipal class

parent 3fb619c5
......@@ -25,7 +25,6 @@ The following software have components provided under the terms of this license:
- Apache Log4j Core (from )
- Apache Log4j JUL Adapter (from )
- Apache Log4j SLF4J Binding (from )
- Apache Log4j to SLF4J Adapter (from )
- Asynchronous Http Client (from )
- Asynchronous Http Client Netty Utils (from )
- Bean Validation API (from http://beanvalidation.org)
......@@ -86,6 +85,7 @@ The following software have components provided under the terms of this license:
- Microsoft Application Insights Java SDK Spring Boot starter (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Java SDK Web Module (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Log4j 2 Appender (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Azure Netty HTTP Client Library (from https://github.com/Azure/azure-sdk-for-java)
- Mockito (from http://mockito.org)
- Netty Reactive Streams Implementation (from )
- Netty/Buffer (from http://netty.io/)
......@@ -113,7 +113,6 @@ The following software have components provided under the terms of this license:
- Okio (from )
- OpenCensus (from https://github.com/census-instrumentation/opencensus-java)
- OpenCensus (from https://github.com/census-instrumentation/opencensus-java)
- Reactive Object Pool (from https://github.com/reactor/reactor-pool)
- Reactive Streams Netty driver (from https://github.com/reactor/reactor-netty)
- Retrofit (from )
- SnakeYAML (from http://www.snakeyaml.org)
......@@ -125,7 +124,6 @@ The following software have components provided under the terms of this license:
- Spring Boot AutoConfigure (from http://projects.spring.io/spring-boot/)
- Spring Boot Json Starter (from https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-json)
- Spring Boot Log4J2 Starter (from http://projects.spring.io/spring-boot/)
- Spring Boot Logging Starter (from http://projects.spring.io/spring-boot/)
- Spring Boot Starter (from http://projects.spring.io/spring-boot/)
- Spring Boot Tomcat Starter (from http://projects.spring.io/spring-boot/)
- Spring Boot Validation Starter (from http://projects.spring.io/spring-boot/)
......@@ -246,8 +244,6 @@ EPL-1.0
The following software have components provided under the terms of this license:
- JUnit Jupiter (Aggregator) (from https://junit.org/junit5/)
- Logback Classic Module (from )
- Logback Core Module (from )
- Microsoft Application Insights Java SDK Core (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Java SDK Spring Boot starter (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Java SDK Web Module (from https://github.com/Microsoft/ApplicationInsights-Java)
......@@ -315,8 +311,6 @@ The following software have components provided under the terms of this license:
- Java Native Access (from https://github.com/java-native-access/jna)
- Java Native Access Platform (from https://github.com/java-native-access/jna)
- Javassist (from http://www.javassist.org/)
- Logback Classic Module (from )
- Logback Core Module (from )
- Microsoft Application Insights Java SDK Core (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Java SDK Spring Boot starter (from https://github.com/Microsoft/ApplicationInsights-Java)
- Microsoft Application Insights Java SDK Web Module (from https://github.com/Microsoft/ApplicationInsights-Java)
......
......@@ -21,7 +21,7 @@
<groupId>org.opengroup.osdu</groupId>
<artifactId>core-lib-azure</artifactId>
<packaging>jar</packaging>
<version>0.0.16</version>
<version>0.0.17</version>
<name>core-lib-azure</name>
<properties>
......@@ -57,6 +57,12 @@
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-http-netty</artifactId>
<version>1.5.3</version>
</dependency>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
......@@ -80,6 +86,16 @@
<groupId>org.opengroup.osdu</groupId>
<artifactId>os-core-common</artifactId>
<version>0.3.4</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Azure dependencies -->
......@@ -159,7 +175,6 @@
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<repositories>
......
package org.opengroup.osdu.azure.di;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* A configuration bean class to set up CosmosDb variables.
*/
@Configuration
@Getter
public class CosmosDBConfiguration {
@Value("${tenantInfo.container.name}")
private String tenantInfoContainer;
@Value("${azure.cosmosdb.database}")
private String cosmosDBName;
}
// Copyright © Microsoft Corporation
//
// 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
//
// 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.opengroup.osdu.azure.multitenancy;
import org.opengroup.osdu.azure.CosmosStore;
import org.opengroup.osdu.azure.di.CosmosDBConfiguration;
import org.opengroup.osdu.core.common.cache.ICache;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory;
import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
/**
* Implementation for ITenantFactory.
*/
@Component
@Lazy
public class TenantFactoryImpl implements ITenantFactory {
@Autowired
@Lazy
private CosmosDBConfiguration cosmosDBConfiguration;
@Autowired
@Lazy
private CosmosStore cosmosStore;
private Map<String, TenantInfo> tenants;
/**
* @param tenantName Tenant name
* @return true or false depending on whether tenant is present
*/
public boolean exists(final String tenantName) {
if (this.tenants == null) {
initTenants();
}
return this.tenants.containsKey(tenantName);
}
/**
* @param tenantName Tenant name
* @return tenantInfo object
*/
public TenantInfo getTenantInfo(final String tenantName) {
if (this.tenants == null) {
initTenants();
}
return this.tenants.get(tenantName);
}
/**
* @return list of tenantInfo objects for all the tenants
*/
public Collection<TenantInfo> listTenantInfo() {
if (this.tenants == null) {
initTenants();
}
return this.tenants.values();
}
/**
* @param tenantName Tenant name
* @param host Host name
* @param port Port
* @param expireTimeSeconds Expiry time in seconds
* @param classOfV Class reference
* @param <V> Template class
* @return cache
*/
public <V> ICache<String, V> createCache(final String tenantName, final String host, final int port, final int expireTimeSeconds, final Class<V> classOfV) {
return null;
}
/**
* Flush the cache.
*/
public void flushCache() {
}
/**
* Initialise the local cache for tenants.
*/
private void initTenants() {
this.tenants = new HashMap<>();
// TODO partition id should not be required because tenant details will be kept in a known partition
cosmosStore.findAllItems(DpsHeaders.DATA_PARTITION_ID, cosmosDBConfiguration.getCosmosDBName(), cosmosDBConfiguration.getTenantInfoContainer(), TenantInfoDoc.class).
forEach(tenantInfoDoc -> {
TenantInfo ti = new TenantInfo();
String tenantName = tenantInfoDoc.getId();
ti.setName(tenantName);
ti.setComplianceRuleSet(tenantInfoDoc.getComplianceRuleSet());
ti.setDataPartitionId(tenantName);
this.tenants.put(tenantName, ti);
}
);
}
}
\ No newline at end of file
// Copyright © Microsoft Corporation
//
// 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
//
// 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.opengroup.osdu.azure.multitenancy;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* TenantInfoDoc class.
*/
@Data
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TenantInfoDoc {
private String id;
private String serviceprincipalAppId;
private String complianceRuleSet;
private String[] groups;
}
// Copyright © Microsoft Corporation
//
// 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
//
// 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.opengroup.osdu.azure.util;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpMethod;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
import com.azure.core.http.netty.NettyAsyncHttpClientBuilder;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import reactor.core.publisher.Mono;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Class to generate the AAD authentication tokens.
*/
public final class AzureServicePrincipal {
/**
* @param sp_id AZURE CLIENT ID
* @param sp_secret AZURE CLIENT SECRET
* @param tenant_id AZURE TENANT ID
* @param app_resource_id AZURE APP RESOURCE ID
* @return AUTHENTICATION TOKEN
* @throws UnsupportedEncodingException throws UnsupportedEncodingException
*/
public String getIdToken(final String sp_id, final String sp_secret, final String tenant_id, final String app_resource_id) throws UnsupportedEncodingException {
String aadEndpoint = String.format("https://login.microsoftonline.com/%s/oauth2/token", tenant_id);
HttpRequest httpRequest = new HttpRequest(HttpMethod.POST, aadEndpoint);
Map<String, String> parameters = new HashMap<>();
parameters.put("grant_type", "client_credentials");
parameters.put("client_id", sp_id);
parameters.put("client_secret", sp_secret);
parameters.put("resource", app_resource_id);
httpRequest.setBody(getParamsString(parameters));
HttpClient client = createHttpClient();
Mono<HttpResponse> response = client.send(httpRequest);
String content = Objects.requireNonNull(response.block()).getBodyAsString().block();
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(content, JsonObject.class);
return jsonObject.get("access_token").getAsString();
}
/**
* @param params Map of request parameters
* @return parameter string
* @throws UnsupportedEncodingException throws exception unsupported encoding is found
*/
private String getParamsString(final Map<String, String> params)
throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
result.append("&");
}
String resultString = result.toString();
return resultString.length() > 0
? resultString.substring(0, resultString.length() - 1)
: resultString;
}
/**
* @return HttpClient
*/
HttpClient createHttpClient() {
return new NettyAsyncHttpClientBuilder().build();
}
}
#
# Copyright © Microsoft Corporation
#
# 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
#
# 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.
#
# Azure CosmosDB configuration
tenantInfo.container.name=TenantInfo
azure.cosmosdb.database=${cosmosdb_database}
\ No newline at end of file
package org.opengroup.osdu.azure.multitenancy;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
import org.opengroup.osdu.azure.CosmosStore;
import org.opengroup.osdu.azure.di.CosmosDBConfiguration;
import org.opengroup.osdu.core.common.model.tenant.TenantInfo;
@ExtendWith(MockitoExtension.class)
public class TenantFactoryImplTest {
private static final String tenantName = "opendes";
private static final String cosmosDatabase = "cosmos-database";
private static final String cosmosContainer = "tenant-info";
private static final String notFound = "not-found";
private static final String[] groups = {"first"};
private static final String serviceprincipalAppId = "sp-id";
private static final String complianceRuleSet = "compliance-rule-set";
@InjectMocks
private TenantFactoryImpl tenantFactory;
@Mock
private CosmosDBConfiguration cosmosDBConfiguration;
@Mock
private CosmosStore cosmosStore;
@BeforeEach
public void init() {
when(cosmosDBConfiguration.getCosmosDBName()).thenReturn(cosmosDatabase);
when(cosmosDBConfiguration.getTenantInfoContainer()).thenReturn(cosmosContainer);
}
@Test
public void returnsTrueWhenTenantNameFound() {
TenantInfoDoc doc = new TenantInfoDoc(tenantName, serviceprincipalAppId, complianceRuleSet, groups);
doReturn(Collections.singletonList(doc)).when(cosmosStore).findAllItems(eq(DpsHeaders.DATA_PARTITION_ID), eq(cosmosDatabase), eq(cosmosContainer), any());
boolean result = tenantFactory.exists(tenantName);
assertTrue(result);
}
@Test
public void returnsFalseWhenTenantNameNotFound() {
TenantInfoDoc doc = new TenantInfoDoc(tenantName + notFound, serviceprincipalAppId, complianceRuleSet, groups);
doReturn(Collections.singletonList(doc)).when(cosmosStore).findAllItems(eq(DpsHeaders.DATA_PARTITION_ID), eq(cosmosDatabase), eq(cosmosContainer), any());
boolean result = tenantFactory.exists(tenantName);
assertFalse(result);
}
@Test
public void returnsTenantInfoObjectWhenTenantNameFound() {
TenantInfoDoc doc = new TenantInfoDoc(tenantName, serviceprincipalAppId, complianceRuleSet, groups);
doReturn(Collections.singletonList(doc)).when(cosmosStore).findAllItems(eq(DpsHeaders.DATA_PARTITION_ID), eq(cosmosDatabase), eq(cosmosContainer), any());
TenantInfo result = tenantFactory.getTenantInfo(tenantName);
TenantInfo expected = new TenantInfo();
expected.setName(tenantName);
expected.setDataPartitionId(tenantName);
expected.setComplianceRuleSet(complianceRuleSet);
assertEquals(expected, result);
}
@Test
public void returnsNullWhenTenantNameNotFound() {
TenantInfoDoc doc = new TenantInfoDoc(tenantName + notFound, serviceprincipalAppId, complianceRuleSet, groups);
doReturn(Collections.singletonList(doc)).when(cosmosStore).findAllItems(eq(DpsHeaders.DATA_PARTITION_ID), eq(cosmosDatabase), eq(cosmosContainer), any());
TenantInfo result = tenantFactory.getTenantInfo(tenantName);
assertNull(result);
}
@Test
public void returnsListOfAllTenants() {
TenantInfoDoc doc = new TenantInfoDoc(tenantName, serviceprincipalAppId, complianceRuleSet, groups);
doReturn(Collections.singletonList(doc)).when(cosmosStore).findAllItems(eq(DpsHeaders.DATA_PARTITION_ID), eq(cosmosDatabase), eq(cosmosContainer), any());
List<TenantInfo> result = new ArrayList<>(tenantFactory.listTenantInfo());
TenantInfo tenantInfo = new TenantInfo();
tenantInfo.setName(tenantName);
tenantInfo.setDataPartitionId(tenantName);
tenantInfo.setComplianceRuleSet(complianceRuleSet);
List<TenantInfo> expected = Collections.singletonList(tenantInfo);
assertEquals(expected, result);
}
}
package org.opengroup.osdu.azure.util;
import com.azure.core.http.HttpClient;
import com.azure.core.http.HttpRequest;
import com.azure.core.http.HttpResponse;
import com.google.gson.JsonObject;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class AzureServicePrincipalTest {
private static final String accessToken = "some-access-token";
private static final String spId = "client-id";
private static final String spSecret = "client-secret";
private static final String tenantId = "tenant-id";
private static final String appResourceId = "app-resource-id";
@Mock
private HttpClient httpClient;
@Mock
private Mono<HttpResponse> responseMono;
@Mock
private HttpResponse httpResponse;
@Spy
private AzureServicePrincipal azureServicePrincipal;
@Test
public void ShouldSuccessfullyGenerateToken() throws Exception {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("access_token", accessToken);
Mono<String> contentMono = Mono.just(jsonObject.toString());
when(azureServicePrincipal.createHttpClient()).thenReturn(httpClient);
when(httpClient.send(any(HttpRequest.class))).thenReturn(responseMono);
when(responseMono.block()).thenReturn(httpResponse);
when(httpResponse.getBodyAsString()).thenReturn(contentMono);
String result = azureServicePrincipal.getIdToken(spId, spSecret, tenantId, appResourceId);
assertEquals(accessToken, result);
verify(httpClient, times(1)).send(any(HttpRequest.class));
verify(responseMono, times(1)).block();
verify(httpResponse, times(1)).getBodyAsString();
}
/**
* This test is added for end to verification whether tokens are getting generated.
*/
//
@Disabled
@Test
public void VerifyingEndToEndScenario() throws Exception {
String spId = "";
String spSecret = "";
String tenantId = "";
String appResourceId = "";
String result = new AzureServicePrincipal().getIdToken(spId, spSecret, tenantId, appResourceId);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment