From d21b12321cde3bdf9e1febda15ecf6d00949e62f Mon Sep 17 00:00:00 2001 From: Komal Makkar <komakkar@microsoft.com> Date: Fri, 14 Aug 2020 00:44:28 -0400 Subject: [PATCH] Notification Service Azure Provider Changes --- pom.xml | 5 +- provider/notification-azure/README.md | 121 ++++++++++++ provider/notification-azure/pom.xml | 177 ++++++++++++++++++ .../provider/azure/Application.java | 31 +++ .../provider/azure/cache/JwtCache.java | 57 ++++++ .../di/ServiceAccountJwtClientFactory.java | 34 ++++ .../azure/security/SecurityConfig.java | 37 ++++ .../provider/azure/util/AppProperties.java | 118 ++++++++++++ .../azure/util/AzureCosmosProperties.java | 59 ++++++ .../azure/util/AzureKeyVaultProperties.java | 20 ++ .../AzureServiceAccountValidatorImpl.java | 40 ++++ .../azure/util/GoogleServiceAccountImpl.java | 32 ++++ .../ServiceAccountJwtAzureClientImpl.java | 108 +++++++++++ .../main/resources/application-dev.properties | 15 ++ .../resources/application-local.properties | 39 ++++ .../src/main/resources/application.properties | 22 +++ .../util/ServiceAccountClientImplTest.java | 156 +++++++++++++++ testing/notification-test-azure/pom.xml | 76 ++++++++ .../api/TestPubsubEndpointHMAC.java | 34 ++++ .../notification/util/AzureTestUtils.java | 55 ++++++ .../osdu/notification/util/Config.java | 2 - 21 files changed, 1234 insertions(+), 4 deletions(-) create mode 100644 provider/notification-azure/README.md create mode 100644 provider/notification-azure/pom.xml create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/Application.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/cache/JwtCache.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/di/ServiceAccountJwtClientFactory.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AppProperties.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureCosmosProperties.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureKeyVaultProperties.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureServiceAccountValidatorImpl.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/GoogleServiceAccountImpl.java create mode 100644 provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java create mode 100644 provider/notification-azure/src/main/resources/application-dev.properties create mode 100644 provider/notification-azure/src/main/resources/application-local.properties create mode 100644 provider/notification-azure/src/main/resources/application.properties create mode 100644 provider/notification-azure/src/test/java/org/opengroup/osdu/notification/util/ServiceAccountClientImplTest.java create mode 100644 testing/notification-test-azure/pom.xml create mode 100644 testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java create mode 100644 testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java diff --git a/pom.xml b/pom.xml index 4050891b5..82222e319 100644 --- a/pom.xml +++ b/pom.xml @@ -15,8 +15,8 @@ limitations under the License. --> <project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.opengroup.osdu</groupId> <artifactId>os-notification</artifactId> @@ -77,6 +77,7 @@ <modules> <module>notification-core</module> <module>provider/notification-gcp</module> + <module>provider/notification-azure</module> </modules> <distributionManagement> diff --git a/provider/notification-azure/README.md b/provider/notification-azure/README.md new file mode 100644 index 000000000..bacbd92b8 --- /dev/null +++ b/provider/notification-azure/README.md @@ -0,0 +1,121 @@ +## Running Locally + +### Requirements + +In order to run this service locally, you will need the following: + +- [Maven 3.6.0+](https://maven.apache.org/download.cgi) +- [AdoptOpenJDK8](https://adoptopenjdk.net/) +- Infrastructure dependencies, deployable through the relevant [infrastructure template](https://dev.azure.com/slb-des-ext-collaboration/open-data-ecosystem/_git/infrastructure-templates?path=%2Finfra&version=GBmaster&_a=contents) +- While not a strict dependency, example commands in this document use [bash](https://www.gnu.org/software/bash/) + +### General Tips + +**Environment Variable Management** +The following tools make environment variable configuration simpler + - [direnv](https://direnv.net/) - for a shell/terminal environment + - [EnvFile](https://plugins.jetbrains.com/plugin/7861-envfile) - for [Intellij IDEA](https://www.jetbrains.com/idea/) + +**Lombok** +This project uses [Lombok](https://projectlombok.org/) for code generation. You may need to configure your IDE to take advantage of this tool. + - [Intellij configuration](https://projectlombok.org/setup/intellij) + - [VSCode configuration](https://projectlombok.org/setup/vscode) + + +### Environment Variables + +In order to run the service locally, you will need to have the following environment variables defined. + +**Note** The following command can be useful to pull secrets from keyvault: +```bash +az keyvault secret show --vault-name $KEY_VAULT_NAME --name $KEY_VAULT_SECRET_NAME --query value -otsv +``` + +**Required to run service** + +| name | value | description | sensitive? | source | +| --- | --- | --- | --- | --- | +| `LOG_PREFIX` | `notification` | Logging prefix | no | - | +| `app.entitlements` | ex `https://foo-entitlements.azurewebsites.net` | Entitlements API endpoint | no | output of infrastructure deployment | +| `app.register`| ex `https://foo-register.azurewebsites.net`| Registration Service API endpoint | no | output of infrastructure deployment | +| `AUTHORIZE_API_KEY` | `********` | The API key clients will need to use when calling the entitlements | yes | -- | +| `azure.application-insights.instrumentation-key` | `********` | API Key for App Insights | yes | output of infrastructure deployment | +| `azure.activedirectory.client-id` | `********` | AAD client application ID | yes | output of infrastructure deployment | +| `azure.activedirectory.AppIdUri` | `api://${azure.activedirectory.client-id}` | URI for AAD Application | no | -- | +| `azure.activedirectory.session-stateless` | `true` | Flag run in stateless mode (needed by AAD dependency) | no | -- | +| `KEYVAULT_URI` | ex `https://foo-keyvault.vault.azure.net/` | URI of KeyVault that holds application secrets | no | output of infrastructure deployment | + + +### Configure Maven + +Check that maven is installed: +```bash +$ mvn --version +Apache Maven 3.6.0 +Maven home: /usr/share/maven +Java version: 1.8.0_212, vendor: AdoptOpenJDK, runtime: /usr/lib/jvm/jdk8u212-b04/jre +... +``` + +You will need to configure access to the remote maven repository that holds the OSDU dependencies. This file should live within `~/.m2/settings.xml`: +```bash +$ cat ~/.m2/settings.xml +<?xml version="1.0" encoding="UTF-8"?> +<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> + <servers> + <server> + <id>os-core</id> + <username>mvn-pat</username> + <!-- Treat this auth token like a password. Do not share it with anyone, including Microsoft support. --> + <password>$PERSONAL_ACCESS_TOKEN_GOES_HERE</password> + </server> + </servers> +</settings> +``` + +### Build and run the application + +After configuring your environment as specified above, you can follow these steps to build and run the application. +1. Navigate to the root of the schema project, os-schema. For building the project using command line, run below command : + ```bash + mvn clean install + ``` + This will build the core project as well as all the underlying projects. If we want to build projects for specific cloud vendor, we can use mvn --projects command. For example, if we want to build only for Azure, we can use below command : + ```bash + mvn --projects notification-core,provider/notification-azure clean install + ``` +2. Run schema service in command line. We need to select which cloud vendor specific schema-service we want to run. For example, if we want to run schema-service for Azure, run the below command : + ```bash + # Running Azure : + java -jar provider\schema-azure\target\os-notification-azure-0.0.1-SNAPSHOT-spring-boot.jar +3. The port and path for the service endpoint can be configured in ```application.properties``` in the provider folder as following. If not specified, then the web container (ex. Tomcat) default is used: + ```bash + server.servlet.contextPath=/api/notification/v1/ + server.port=8080 + ``` + +## Debugging + +Jet Brains - the authors of Intellij IDEA, have written an [excellent guide](https://www.jetbrains.com/help/idea/debugging-your-first-java-application.html) on how to debug java programs. + + +## Deploying service + +TBD + +## License +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](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. diff --git a/provider/notification-azure/pom.xml b/provider/notification-azure/pom.xml new file mode 100644 index 000000000..69593aaf8 --- /dev/null +++ b/provider/notification-azure/pom.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.opengroup.osdu</groupId> + <artifactId>notification-azure</artifactId> + <version>1.0.0</version> + <name>notification-azure</name> + <description>Azure implementation for Notification service</description> + <packaging>jar</packaging> + + <parent> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-notification</artifactId> + <version>1.0.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <properties> + <java.version>8</java.version> + <maven.compiler.target>${java.version}</maven.compiler.target> + <maven.compiler.source>${java.version}</maven.compiler.source> + <azure.version>2.1.7</azure.version> + <springframework.version>4.3.0.RELEASE</springframework.version> + <reactor.netty.version>0.9.0.RELEASE</reactor.netty.version> + <reactor.core.version>3.3.0.RELEASE</reactor.core.version> + </properties> + <dependencies> + <dependency> + <groupId>com.microsoft.azure</groupId> + <artifactId>azure-active-directory-spring-boot-starter</artifactId> + <version>${azure.version}</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.security.oauth</groupId> + <artifactId>spring-security-oauth2</artifactId> + <version>2.3.6.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-jwt</artifactId> + <version>1.0.10.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-client</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-jose</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-configuration-processor</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>com.microsoft.azure</groupId> + <artifactId>applicationinsights-logging-logback</artifactId> + <version>[2.0,)</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-common</artifactId> + <version>0.3.4</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>core-lib-azure</artifactId> + <version>0.0.17</version> + </dependency> + <dependency> + <groupId>com.auth0</groupId> + <artifactId>java-jwt</artifactId> + <version>3.8.1</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>notification-core</artifactId> + <version>1.0.0</version> + </dependency> + + <!-- + Override the spring-boot version of these dependencies to the ones + required by the azure-core library. This needs to be done for each + app that depends on this library + --> + <dependency> + <groupId>io.projectreactor.netty</groupId> + <artifactId>reactor-netty</artifactId> + <version>${reactor.netty.version}</version> + </dependency> + <dependency> + <groupId>io.projectreactor</groupId> + <artifactId>reactor-core</artifactId> + <version>${reactor.core.version}</version> + </dependency> + + <!-- Test Dependencies --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + <version>2.1.6.RELEASE</version> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>3.0.0</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + <configuration> + <classifier>spring-boot</classifier> + <mainClass> + org.opengroup.osdu.notification.provider.azure.Application + </mainClass> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.4.2</version> + <configuration> + <useSystemClassLoader>false</useSystemClassLoader> + <threadCount>1</threadCount> + </configuration> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/Application.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/Application.java new file mode 100644 index 000000000..79a9ffdb6 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/Application.java @@ -0,0 +1,31 @@ +// 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.notification.provider.azure; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableAsync; + +@SpringBootApplication +@ComponentScan({"org.opengroup.osdu"}) +@EnableAsync +public class Application { + + public static void main(String[] args) { + SpringApplication.run(new Class[]{Application.class}, args); + } +} + diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/cache/JwtCache.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/cache/JwtCache.java new file mode 100644 index 000000000..4c3dffbc9 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/cache/JwtCache.java @@ -0,0 +1,57 @@ +// 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.notification.provider.azure.cache; + +import org.opengroup.osdu.core.common.cache.VmCache; +import org.opengroup.osdu.core.common.model.search.IdToken; +import org.opengroup.osdu.core.common.provider.interfaces.IJwtCache; +import org.opengroup.osdu.notification.provider.azure.util.AppProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.inject.Named; + +@Component +public class JwtCache implements IJwtCache<String, IdToken> { + private VmCache<String, IdToken> cache; + + // Azure service account id_token can be requested only for 1 hr + private final static int EXPIRED_AFTER = 59; + + public JwtCache(@Named("MAX_CACHE_VALUE_SIZE") String cacheSize){ + cache = new VmCache<>(EXPIRED_AFTER * 60, Integer.parseInt(cacheSize)); + } + + @Override + public void put(String s, IdToken o) { + this.cache.put(s, o); + } + + @Override + public IdToken get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/di/ServiceAccountJwtClientFactory.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/di/ServiceAccountJwtClientFactory.java new file mode 100644 index 000000000..708834922 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/di/ServiceAccountJwtClientFactory.java @@ -0,0 +1,34 @@ +// 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.notification.provider.azure.di; + +import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient; +import org.opengroup.osdu.notification.provider.azure.util.AppProperties; +import org.opengroup.osdu.notification.provider.azure.util.ServiceAccountJwtAzureClientImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.AbstractFactoryBean; + +public class ServiceAccountJwtClientFactory extends AbstractFactoryBean<IServiceAccountJwtClient> { + + @Override + public IServiceAccountJwtClient createInstance() throws Exception { + return new ServiceAccountJwtAzureClientImpl(); + } + + @Override + public final Class<?> getObjectType() { + return IServiceAccountJwtClient.class; + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java new file mode 100644 index 000000000..853b37b2a --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java @@ -0,0 +1,37 @@ +// 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.notification.provider.azure.security; + +import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.inject.Inject; + +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + // TODO : Add antMatchers after Swagger changes merge + // TODO : Add the authZ + httpSecurity + .csrf().disable(); + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AppProperties.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AppProperties.java new file mode 100644 index 000000000..10bac0d15 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AppProperties.java @@ -0,0 +1,118 @@ +/* +// 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.notification.provider.azure.util; + +import com.azure.cosmos.CosmosClient; +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.models.KeyVaultSecret; +import org.opengroup.osdu.azure.KeyVaultFacade; +import org.opengroup.osdu.notification.utils.IAppProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import javax.inject.Named; + +@Component +public class AppProperties implements IAppProperties { + + @Value("${app.entitlements}") + private String authorizeAPI; + + @Value("${app.register}") + private String registerAPI; + + @Value("${app.expireTime}") + private String expireTime; + + @Value("${app.maxCacheSize}") + private String maxCacheSize; + + @Value("${aad.oboApi}") + private String aadOboAPI; + + @Value("${app.maxCacheSize") + private String CacheValueSize; + + @Autowired + private SecretClient secretClient; + + private String authURL; + + private String authClientID; + + private String authClientSecret; + + public String getAadClientID() { + return aadOboAPI; + } + + public String getAuthorizeAPI() { + return this.authorizeAPI; + } + + public String getRegisterAPI() { + return this.registerAPI; + } + + public String getAuthClientSecret() { + if(this.authClientSecret == null) { + this.authClientSecret = getKeyVaultSecret(this.secretClient, "app-dev-sp-password"); + } + + return this.authClientSecret; + } + + public String getAuthClientID() { + if(this.authClientID == null) { + this.authClientID = getKeyVaultSecret(this.secretClient, "app-dev-sp-username"); + } + + return this.authClientID; + } + + public String getAuthURL() { + if(this.authURL == null) { + String urlFormat = "https://login.microsoftonline.com/%s/oauth2/token/"; + String tenant = getKeyVaultSecret(this.secretClient, "app-dev-sp-tenant-id"); + this.authURL = String.format(urlFormat, tenant); + } + + return this.authURL; + } + + @Bean + @Named("MAX_CACHE_VALUE_SIZE") + public String getMaxCacheSize() { + return maxCacheSize; + } + + private String getKeyVaultSecret(SecretClient kv, String secretName) { + KeyVaultSecret secret = kv.getSecret(secretName); + if (secret == null) { + throw new IllegalStateException(String.format("No secret found with name %s", secretName)); + } + + String secretValue = secret.getValue(); + if (secretValue == null) { + throw new IllegalStateException(String.format( + "Secret unexpectedly missing from KeyVault response for secret with name %s", secretName)); + } + + return secretValue; + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureCosmosProperties.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureCosmosProperties.java new file mode 100644 index 000000000..22bb7ee41 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureCosmosProperties.java @@ -0,0 +1,59 @@ +package org.opengroup.osdu.notification.provider.azure.util; + +import com.azure.security.keyvault.secrets.SecretClient; +import com.azure.security.keyvault.secrets.models.KeyVaultSecret; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import javax.inject.Named; + +@Component +public class AzureCosmosProperties { + + @Value("${tenantinfo.container.name}") + private String tenantInfoContainerName; + + @Value("${azure.cosmosdb.database}") + private String cosmosDBName; + + // TODO : Move away from Named beans. + @Bean + @Named("COSMOS_ENDPOINT") + public String cosmosEndpoint(SecretClient kv) { + return getKeyVaultSecret(kv, "cosmos-endpoint"); + } + + @Bean + @Named("COSMOS_KEY") + public String cosmosKey(SecretClient kv) { + return getKeyVaultSecret(kv, "cosmos-primary-key"); + } + + @Bean + @Named("COSMOS_CONTAINER_NAME") + public String containerName() { + return tenantInfoContainerName; + } + + @Bean + @Named("COSMOS_DB_NAME") + public String cosmosDBName() { + return cosmosDBName; + } + + public String getKeyVaultSecret(SecretClient kv, String secretName) { + KeyVaultSecret secret = kv.getSecret(secretName); + if (secret == null) { + throw new IllegalStateException(String.format("No secret found with name %s", secretName)); + } + + String secretValue = secret.getValue(); + if (secretValue == null) { + throw new IllegalStateException(String.format( + "Secret unexpectedly missing from KeyVault response for secret with name %s", secretName)); + } + + return secretValue; + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureKeyVaultProperties.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureKeyVaultProperties.java new file mode 100644 index 000000000..6c4fe6f19 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureKeyVaultProperties.java @@ -0,0 +1,20 @@ +package org.opengroup.osdu.notification.provider.azure.util; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import javax.inject.Named; + +@Component +public class AzureKeyVaultProperties { + + @Value("${azure.keyvault.url}") + private String keyVaultURL; + + @Bean + @Named("KEY_VAULT_URL") + public String getKeyVaultURL() { + return keyVaultURL; + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureServiceAccountValidatorImpl.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureServiceAccountValidatorImpl.java new file mode 100644 index 000000000..7f153b364 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/AzureServiceAccountValidatorImpl.java @@ -0,0 +1,40 @@ +// 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.notification.provider.azure.util; + +import org.opengroup.osdu.notification.utils.ServiceAccountValidator; +import org.springframework.stereotype.Component; + +@Component +public class AzureServiceAccountValidatorImpl implements ServiceAccountValidator { + + @Override + public boolean isValidPublisherServiceAccount(String jwt) { + // TODO : Implement the service account verification for PubSub Role + // TODO : Also check the need and means to verify the other Roles. + + // Marking it to return true, for the integration tests. + return true; + } + + @Override + public boolean isValidServiceAccount(String jwt, String userIdentity, String... audiences) { + // TODO : Implement the service account verification for PubSub Role + // TODO : Also check the need and means to verify the other Roles. + + // Marking it to return true, for the integration tests. + return true; + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/GoogleServiceAccountImpl.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/GoogleServiceAccountImpl.java new file mode 100644 index 000000000..ed4631926 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/GoogleServiceAccountImpl.java @@ -0,0 +1,32 @@ +// 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.notification.provider.azure.util; + +import lombok.SneakyThrows; +import org.opengroup.osdu.notification.utils.IGoogleServiceAccount; +import org.springframework.stereotype.Component; + +import javax.naming.AuthenticationNotSupportedException; + +@Component +public class GoogleServiceAccountImpl implements IGoogleServiceAccount { + + @SneakyThrows + @Override + public String getIdToken(String keyString, String audience) { + // TODO : Check if it is to be supported + throw new AuthenticationNotSupportedException(); + } +} diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java new file mode 100644 index 000000000..5d6d99334 --- /dev/null +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java @@ -0,0 +1,108 @@ +// 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.notification.provider.azure.util; + +import com.auth0.jwt.JWT; +import com.microsoft.aad.adal4j.AuthenticationContext; +import com.microsoft.aad.adal4j.AuthenticationResult; +import com.microsoft.aad.adal4j.ClientCredential; +import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.search.IdToken; +import org.opengroup.osdu.core.common.model.tenant.TenantInfo; +import org.opengroup.osdu.core.common.provider.interfaces.IJwtCache; +import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory; +import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.net.MalformedURLException; +import java.util.concurrent.*; + +@Component +public class ServiceAccountJwtAzureClientImpl implements IServiceAccountJwtClient { + + @Autowired + private AppProperties config; + + @Autowired + private ITenantFactory tenantInfoServiceProvider; + + @Autowired + private IJwtCache tenantJwtCache; + + public String getIdToken(String tenantName) { + // TODO : Add logs. + TenantInfo tenant = tenantInfoServiceProvider.getTenantInfo(tenantName); + if (tenant == null) { + throw new AppException(HttpStatus.SC_BAD_REQUEST, "Invalid tenant Name", "Invalid tenant Name from azure"); + } + + String ACCESS_TOKEN = ""; + ExecutorService service = null; + + try { + // TODO : Refactor to move ID token form Common.Core.model.search to Common.core + IdToken cachedToken = (IdToken) tenantJwtCache.get(tenant.getName()); + + if ((cachedToken != null) && !IdToken.refreshToken(cachedToken)) { + return cachedToken.getTokenValue(); + } + + // TODO : Control the thread count via config and pool should be created once. + service = Executors.newFixedThreadPool(1); + + ACCESS_TOKEN = getAccessToken(service); + IdToken idToken = IdToken.builder().tokenValue(ACCESS_TOKEN).expirationTimeMillis(JWT.decode(ACCESS_TOKEN).getExpiresAt().getTime()).build(); + tenantJwtCache.put(tenant.getName(), idToken); + } catch (AppException e) { + throw e; + } catch (Exception e) { + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Persistence error", "Error generating token", e); + } finally { + if(service != null) { + service.shutdown(); + } + } + return ACCESS_TOKEN; + } + + // TODO : Refactor for making it test-able. + // THIS METHOD IS PUBLIC ONLY TO ENABLE UNIT TESTING + public String getAccessToken(ExecutorService service) { + AuthenticationContext context = null; + ClientCredential credential = null; + String ACCESS_TOKEN = null; + try { + context = new AuthenticationContext(this.config.getAuthURL(), false, service); + credential = new ClientCredential(this.config.getAuthClientID(), this.config.getAuthClientSecret()); + + Future<AuthenticationResult> future = context.acquireToken(this.config.getAadClientID(), credential, null); + + if (future == null) { + throw new AppException(HttpStatus.SC_FORBIDDEN, "Access denied", "The user is not authorized to perform this action"); + } + ACCESS_TOKEN = future.get().getAccessToken(); + } catch (MalformedURLException malformedURLException) { + malformedURLException.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + return ACCESS_TOKEN; + } +} + diff --git a/provider/notification-azure/src/main/resources/application-dev.properties b/provider/notification-azure/src/main/resources/application-dev.properties new file mode 100644 index 000000000..8b813dc17 --- /dev/null +++ b/provider/notification-azure/src/main/resources/application-dev.properties @@ -0,0 +1,15 @@ +# 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. + +spring.profiles.active=dev diff --git a/provider/notification-azure/src/main/resources/application-local.properties b/provider/notification-azure/src/main/resources/application-local.properties new file mode 100644 index 000000000..dede88f26 --- /dev/null +++ b/provider/notification-azure/src/main/resources/application-local.properties @@ -0,0 +1,39 @@ +# 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. + +# Log settings +logging.level.org.springframework.web=DEBUG +spring.profiles.active=local + +# Service settings +app.entitlements=${entitlements_service_endpoint} +app.register=${registeration_service_endpoint} +app.project=opendes +server.port=8080 + +# Azure AD configuration +azure.activedirectory.client-id=${aad_client_id} +azure.activedirectory.AppIdUri=api://${azure.activedirectory.client-id} +azure.activedirectory.session-stateless=true + +aad.oboApi=${aad_client_id} +azure.application-insights.instrumentation-key=${appinsights_key} + +# Azure CosmosDB configuration +azure.cosmosdb.uri=${cosmosdb_account} +azure.cosmosdb.database=${cosmosdb_database} +tenantInfo.container.name=TenantInfo + +# Azure KeyVault configuration +azure.keyvault.url=${KEYVAULT_URI} diff --git a/provider/notification-azure/src/main/resources/application.properties b/provider/notification-azure/src/main/resources/application.properties new file mode 100644 index 000000000..b002ba804 --- /dev/null +++ b/provider/notification-azure/src/main/resources/application.properties @@ -0,0 +1,22 @@ +# 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. + +LOG_PREFIX=notification +server.servlet.contextPath=/ +app.expireTime=300 +app.maxCacheSize=10 +server.error.whitelabel.enabled=false + + + diff --git a/provider/notification-azure/src/test/java/org/opengroup/osdu/notification/util/ServiceAccountClientImplTest.java b/provider/notification-azure/src/test/java/org/opengroup/osdu/notification/util/ServiceAccountClientImplTest.java new file mode 100644 index 000000000..5153d6205 --- /dev/null +++ b/provider/notification-azure/src/test/java/org/opengroup/osdu/notification/util/ServiceAccountClientImplTest.java @@ -0,0 +1,156 @@ +// 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.notification.util; + +import org.apache.http.HttpStatus; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.search.IdToken; +import org.opengroup.osdu.core.common.model.tenant.TenantInfo; +import org.opengroup.osdu.core.common.provider.interfaces.ITenantFactory; +import org.opengroup.osdu.notification.provider.azure.cache.JwtCache; +import org.opengroup.osdu.notification.provider.azure.util.AppProperties; +import org.opengroup.osdu.notification.provider.azure.util.ServiceAccountJwtAzureClientImpl; + +import java.util.concurrent.ExecutorService; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ServiceAccountClientImplTest { + + final String tenantName = "Test Tenant"; + final String validToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkdW1teUBkdW1teS5jb20iLCJpc3MiOiJkdW1teUBkdW1teS5jb20iLCJhdWQiOiJkdW1teS5kdW1teS5jb20iLCJpYXQiOjE1NTYxMzcyNzMsImV4cCI6MTU1NjIzMDk3OSwicHJvdmlkZXIiOiJkdW1teS5jb20iLCJjbGllbnQiOiJkdW1teS5jb20iLCJ1c2VyaWQiOiJkdW1teXRlc3Rlci5jb20iLCJlbWFpbCI6ImR1bW15dGVzdGVyLmNvbSIsImF1dGh6IjoiIiwibGFzdG5hbWUiOiJkdW1teSIsImZpcnN0bmFtZSI6ImR1bW15IiwiY291bnRyeSI6IiIsImNvbXBhbnkiOiIiLCJqb2J0aXRsZSI6IiIsInN1YmlkIjoiZHVtbXlpZCIsImlkcCI6ImR1bW15IiwiaGQiOiJkdW1teS5jb20iLCJkZXNpZCI6ImR1bW15aWQiLCJjb250YWN0X2VtYWlsIjoiZHVtbXlAZHVtbXkuY29tIiwianRpIjoiNGEyMWYyYzItZjU5Yy00NWZhLTk0MTAtNDNkNDdhMTg4ODgwIn0.nkiyKtfXXxAlC60iDjXuB2EAGDfZiVglP-CyU1T4etc"; + final String invalidToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkdW1teUBkdW1teS5jb20iLCJpc3MiOiJkdW1teUBkdW1teS5jb20iLCJhdWQiOiJkdW1teS5kdW1teS5jb20iLCJpYXQiOjE1NTYxMzcyNzMsImV4cCI6MTU1NjIzMDk3OSwicHJvdmlkZXIiOiJkdW1teS5jb20iLCJjbGllbnQiOiJkdW1teS5jb20iLCJ1c2VyaWQiOiJkdW1teXRlc3Rlci5jb20iLCJlbWFpbCI6ImR1bW15dGVzdGVyLmNvbSIsImF1dGh6IjoiIiwibGFzdG5hbWUiOiJkdW1teSIsImZpcnN0bmFtZSI6ImR1bW15IiwiY291bnRyeSI6IiIsImNvbXBhbnkiOiIiLCJqb2J0aXRsZSI6IiIsInN1YmlkIjoiZHVtbXlpZCIsImlkcCI6ImR1bW15IiwiaGQiOiJkdW1teS5jb20iLCJkZXNpZCI6ImR1bW15aWQiLCJjb250YWN0X2VtYWlsIjoiZHVtbXlAZHVtbXkuY29tIiwianRpIjoiNGEyMWYyYzItZjU5Yy00NWZhLTk0MTAtNDNkNDdhMTg4ODgwIn0.nkiyKtfXXxAlC60iDjXuB2EAGDfZiVglP-CyU1T4etc"; + + @Mock + private ITenantFactory tenantInfoServiceProvider; + + @Mock + private TenantInfo tenantInfo; + + @Mock + private IdToken idToken; + + @Mock + private ExecutorService executorService; + + @Mock + private AppProperties appProperties; + + @Mock + private JwtCache tenantJwtCacheMock; + + @InjectMocks + @Spy + private ServiceAccountJwtAzureClientImpl sut; + + @Before + public void setup() { + initMocks(this); + when(tenantInfo.getName()).thenReturn(tenantName); + when(this.tenantInfoServiceProvider.getTenantInfo(tenantName)).thenReturn(tenantInfo); + idToken = IdToken.builder().tokenValue(validToken).expirationTimeMillis(System.currentTimeMillis() + 10000000L).build(); + } + + @Test + public void should_throwAppException_getIdTokenTest() { + // Set up + when(this.tenantInfoServiceProvider.getTenantInfo(tenantName)).thenReturn(null); + try { + // Act + sut.getIdToken(tenantName); + + // Assert + fail("Should throw exception"); + } catch (AppException e) { + Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, e.getError().getCode()); + Assert.assertEquals("Invalid tenant Name from azure", e.getError().getMessage()); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_getTokenFromCache_getIdTokenTest() { + // SetUp + when(tenantJwtCacheMock.get(any())).thenReturn(idToken); + + // Act + String returnedIdToken = sut.getIdToken(tenantName); + + // Assert + Assert.assertEquals(idToken.getTokenValue(), returnedIdToken); + } + + @Test + public void should_updateCache_getIdTokenTest() { + // Set up + when(tenantJwtCacheMock.get(any())).thenReturn(idToken); + + // Act + String returnedToken = this.sut.getIdToken(tenantName); + + // Assert + Assert.assertEquals(validToken, returnedToken); + } + + @Test + public void should_throw500ForInvalidAccessTokenResponse_getIdToken() { + try { + // Act + this.sut.getIdToken(tenantName); + + // Assert + fail("Should throw exception"); + } catch (AppException e) { + Assert.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getError().getCode()); + Assert.assertEquals("Error generating token", e.getError().getMessage()); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } + + @Test + public void should_return403GivenInvalidApplicationProperties_getAccessToken() { + when(appProperties.getAuthURL()).thenReturn("https://login.microsoftonline.com/s/oauth2/token/"); + when(appProperties.getAuthClientID()).thenReturn("testAuthClientID"); + when(appProperties.getAuthClientSecret()).thenReturn("testAuthClientSecret"); + when(appProperties.getAadClientID()).thenReturn("testAadClientID"); + + try { + // Act + sut.getAccessToken(executorService); + + // Assert + fail("Should throw exception"); + } catch (AppException appException) { + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, appException.getError().getCode()); + } catch (Exception e) { + fail("Should not throw this exception" + e.getMessage()); + } + } +} + diff --git a/testing/notification-test-azure/pom.xml b/testing/notification-test-azure/pom.xml new file mode 100644 index 000000000..cacbfac57 --- /dev/null +++ b/testing/notification-test-azure/pom.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2017-2020, Schlumberger + ~ + ~ 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. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.opengroup.osdu</groupId> + <artifactId>notification-test-azure</artifactId> + <version>1.0-SNAPSHOT</version> + <name>notification-test-azure</name> + <description>Integration tests Azure for notification</description> + <packaging>jar</packaging> + + <properties> + <java.version>8</java.version> + <maven.compiler.target>${java.version}</maven.compiler.target> + <maven.compiler.source>${java.version}</maven.compiler.source> + </properties> + + <repositories> + <repository> + <id>${gitlab-server}</id> + <url>https://community.opengroup.org/api/v4/groups/17/-/packages/maven</url> + </repository> + </repositories> + + <distributionManagement> + <repository> + <id>${gitlab-server}</id> + <url>https://community.opengroup.org/api/v4/projects/143/packages/maven</url> + </repository> + <snapshotRepository> + <id>${gitlab-server}</id> + <url>https://community.opengroup.org/api/v4/projects/143/packages/maven</url> + </snapshotRepository> + </distributionManagement> + + <dependencies> + <dependency> + <groupId>org.opengroup.osdu.notification</groupId> + <artifactId>notification-test-core</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-common</artifactId> + <version>0.0.20</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>core-lib-azure</artifactId> + <version>0.0.17</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + </dependency> + </dependencies> + +</project> diff --git a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java new file mode 100644 index 000000000..a18328c34 --- /dev/null +++ b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java @@ -0,0 +1,34 @@ +package org.opengroup.osdu.notification.api; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.opengroup.osdu.notification.util.Config; +import org.opengroup.osdu.notification.util.AzureTestUtils; +import org.opengroup.osdu.notification.util.RestDescriptor; +import org.opengroup.osdu.notification.util.TestUtils; + +public class TestPubsubEndpointHMAC extends PubsubEndpointHMACTests { + + @BeforeClass + public static void classSetup() throws Exception { + PubsubEndpointHMACTests.classSetup(); + } + + @AfterClass + public static void classTearDown() throws Exception { + } + + @Before + @Override + public void setup() throws Exception { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() throws Exception { + this.testUtils = null; + } +} \ No newline at end of file diff --git a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java new file mode 100644 index 000000000..0af4600be --- /dev/null +++ b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java @@ -0,0 +1,55 @@ +package org.opengroup.osdu.notification.util; + +import com.google.common.base.Strings; +import org.opengroup.osdu.azure.util.AzureServicePrincipal; + +public class AzureTestUtils extends TestUtils { + + public AzureTestUtils() { + + } + + // TODO : Revisit for synchronized block + @Override + public synchronized String getOpsToken() throws Exception { + if (Strings.isNullOrEmpty(opsToken)) { + opsToken = getToken(System.getProperty("INTEGRATION_TESTER", System.getenv("INTEGRATION_TESTER")), + System.getProperty("TESTER_SERVICEPRINCIPAL_SECRET", System.getenv("TESTER_SERVICEPRINCIPAL_SECRET"))); + } + return "Bearer " + opsToken; + } + + @Override + public synchronized String getAdminToken() throws Exception { + if (Strings.isNullOrEmpty(adminToken)) { + adminToken = getToken(System.getProperty("INTEGRATION_TESTER", System.getenv("INTEGRATION_TESTER")), + System.getProperty("TESTER_SERVICEPRINCIPAL_SECRET", System.getenv("TESTER_SERVICEPRINCIPAL_SECRET"))); + } + return "Bearer " + adminToken; + } + + @Override + public synchronized String getEditorToken() throws Exception { + if (Strings.isNullOrEmpty(editorToken)) { + editorToken = getToken(System.getProperty("INTEGRATION_TESTER", System.getenv("INTEGRATION_TESTER")), + System.getProperty("TESTER_SERVICEPRINCIPAL_SECRET", System.getenv("TESTER_SERVICEPRINCIPAL_SECRET"))); + } + return "Bearer " + editorToken; + } + + @Override + public synchronized String getNoAccessToken() throws Exception { + if (Strings.isNullOrEmpty(noAccessToken)) { + noAccessToken = getToken(System.getProperty("NO_DATA_ACCESS_TESTER", System.getenv("NO_DATA_ACCESS_TESTER")), + System.getProperty("NO_DATA_ACCESS_TESTER_SERVICEPRINCIPAL_SECRET", System.getenv("NO_DATA_ACCESS_TESTER_SERVICEPRINCIPAL_SECRET"))); + } + return "Bearer " + noAccessToken; + } + + private String getToken(String sp_id, String sp_secret) throws Exception { + String tenant_id = System.getProperty("AZURE_AD_TENANT_ID", System.getenv("AZURE_AD_TENANT_ID")); + String app_resource_id = System.getProperty("AZURE_AD_APP_RESOURCE_ID", System.getenv("AZURE_AD_APP_RESOURCE_ID")); + + return new AzureServicePrincipal().getIdToken(sp_id, sp_secret, tenant_id, app_resource_id); + } +} diff --git a/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java b/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java index e1fafaf80..486680fb5 100644 --- a/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java +++ b/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java @@ -33,14 +33,12 @@ public class Config { public static Config Instance() { String env = getEnvironment(); - config.ClientTenant = "nonexistenttenant"; config.IntegrationAudience = "245464679631-ktfdfpl147m1mjpbutl00b3cmffissgq.apps.googleusercontent.com"; config.OsduTenant = "opendes"; config.Topic = "records-changed"; config.hmacSecretValue = System.getProperty("HMAC_SECRET", System.getenv("HMAC_SECRET")); - if (env.equalsIgnoreCase("LOCAL")) { //make sure to run register service on a different port. You can also choose to point to Register service that is running in cloud String registerUrl = "http://localhost:8081/"; -- GitLab