diff --git a/docs/tutorial/ActionService.md b/docs/tutorial/ActionService.md index 802a8361e2b7f44474283a9e871db77392857ef3..c8c250511c472fae1856e9cc058fcb11c2d562f9 100644 --- a/docs/tutorial/ActionService.md +++ b/docs/tutorial/ActionService.md @@ -308,4 +308,4 @@ There are mainly 2 limitations currently: - Users need to individually register for each data partition they want the action enabled from. - We don't support a wildcard in the action URL for the partition id. Something like myaction.com/kind?dpid={data-partition-id} may be supported in the future so that the request can be data partition aware. -[Back to Table of Contents](#TOC) \ No newline at end of file +[Back to Table of Contents](#TOC) diff --git a/pom.xml b/pom.xml index 342e9aab35c16b4e0754bf7be8530c3f9cab8bbb..4a33e6496386e93b7002b38e9b490416020f359c 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,7 @@ <modules> <module>register-core</module> <module>provider/register-gcp</module> + <module>provider/register-azure</module> </modules> <distributionManagement> diff --git a/provider/register-azure/README.md b/provider/register-azure/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c1c9b2ea74e88bb5dfe8cce4e64036708a1ee147 --- /dev/null +++ b/provider/register-azure/README.md @@ -0,0 +1,119 @@ +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. + +# os-register-azure + +os-register-azure is a [Spring Boot](https://spring.io/projects/spring-boot) service that hosts CRUD APIs that allows consumers to register a push endpoint that can be triggered when data change events happen within the OSDU R2 ecosystem. + +## Running locally + +### Requirements +* Java 8 +* [Maven 3.6.0+](https://maven.apache.org/download.cgi) + +### 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) + + +### 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>slb-des-ext-collaboration</username> + <!-- Treat this auth token like a password. Do not share it with anyone, including Microsoft support. --> + <password>${VSTS_FEED_TOKEN}</password> + </server> + </servers> +</settings> +``` + +### Environment Variables + +In order to run the service locally, you will need to have the following environment variables defined. + +**System Environment required to run service** +Refer to [application.properties](./src/main/resources/application.properties) + +Definitions for some variables used + +| name | value | description | sensitive? | source | +| --- | --- | --- | --- | --- | +| `LOG_PREFIX` | `storage` | Logging prefix | no | - | +| `server.servlet.contextPath` | `/api/storage/v2/` | Servlet context path | no | - | +| `AUTHORIZE_API` | ex `https://foo-entitlements.azurewebsites.net` | Entitlements 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 | -- | +| `cosmosdb_account` | ex `devintosdur2cosmosacct` | Cosmos account name | no | output of infrastructure deployment | +| `cosmosdb_key` | `********` | Key for CosmosDB | yes | output of infrastructure deployments | +| `cosmosdb_database` | ex `dev-osdu-r2-db` | Cosmos database for storage documents | no | output of infrastructure deployment | +| `azure.storage.account-name` | ex `foo-storage-account` | Storage account for storing documents | no | output of infrastructure deployment | +| `azure.storage.enable-https` | `true` | Used by spring boot starter library | no | - | +| `servicebus_topic_name` | `recordstopic` | Topic for async messaging | no | output of infrastructure deployment | +| `servicebus_namespace_name` | ex `foo-sb-namespace` | Namespace for async messaging | no | output of infrastructure deployment | +| `KEYVAULT_URI` | ex `https://foo-keyvault.vault.azure.net/` | URI of KeyVault that holds application secrets | no | output of infrastructure deployment | +| `AZURE_CLIENT_ID` | `********` | Identity to run the service locally. This enables access to Azure resources. You only need this if running locally | yes | keyvault secret: `$KEYVAULT_URI/secrets/app-dev-sp-username` | +| `AZURE_TENANT_ID` | `********` | AD tenant to authenticate users from | yes | keyvault secret: `$KEYVAULT_URI/secrets/app-dev-sp-tenant-id` | +| `AZURE_CLIENT_SECRET` | `********` | Secret for `$AZURE_CLIENT_ID` | yes | keyvault secret: `$KEYVAULT_URI/secrets/app-dev-sp-password` | + + + +### Build and run the application +After configuring your environment as specified above, you can follow these steps to build and run the application. These steps should be invoked from the *repository root*. +```bash +# build + test + install core service code +$ cd register-core/ && mvn clean install + +# build + test + package azure service code +$ cd provider/register-azure/ && mvn clean package + +# run service +# +# Note: this assumes that the environment variables for running the service as outlined +# above are already exported in your environment. +$ mvn spring-boot:run -Dspring-boot.run.profiles=local +# or directly run the jar file +$ cd provider/register-azure/ && java -jar target\register-azure-1.0.0-spring-boot.jar +``` + +### Test the application +After the service has started it should be accessible via a web browser by visiting [http://localhost:8080/api/register/v1/swagger-ui.html](http://localhost:8080/api/register/v1/swagger-ui.html). If the request does not fail, you can then run the integration tests. \ No newline at end of file diff --git a/provider/register-azure/pom.xml b/provider/register-azure/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a035b36c346887507e8acc7f3d1bc54c4ce05d00 --- /dev/null +++ b/provider/register-azure/pom.xml @@ -0,0 +1,168 @@ +<?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"> + <parent> + <artifactId>os-register</artifactId> + <groupId>org.opengroup.osdu</groupId> + <version>1.0.0</version> + <relativePath>../../</relativePath> + </parent> + <modelVersion>4.0.0</modelVersion> + <artifactId>register-azure</artifactId> + <description>Register service on Azure</description> + <packaging>jar</packaging> + <properties> + <azure.version>2.1.7</azure.version> + <azure.appservice.resourcegroup></azure.appservice.resourcegroup> + <azure.appservice.plan></azure.appservice.plan> + <azure.appservice.appname></azure.appservice.appname> + <azure.appservice.subscription></azure.appservice.subscription> + </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>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </exclusion> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-to-slf4j</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-client</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter</artifactId> + <exclusions> + <exclusion> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-logging</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-jose</artifactId> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>core-lib-azure</artifactId> + <version>0.0.12</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>register-core</artifactId> + <version>1.0.0</version> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </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>0.9.0.RELEASE</version> + </dependency> + <dependency> + <groupId>io.projectreactor</groupId> + <artifactId>reactor-core</artifactId> + <version>3.3.0.RELEASE</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>5.6.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <version>2.23.0</version> + <scope>test</scope> + </dependency> + </dependencies> + + <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/44/packages/maven</url> + </repository> + <snapshotRepository> + <id>${gitlab-server}</id> + <url>https://community.opengroup.org/api/v4/projects/44/packages/maven</url> + </snapshotRepository> + </distributionManagement> + <build> + <plugins> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.22.2</version> + </plugin> + <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.register.provider.azure.RegisterApplication</mainClass> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/RegisterApplication.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/RegisterApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..2045d5be47f2ffd7f98d3d89a0c9f0a378eed6bc --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/RegisterApplication.java @@ -0,0 +1,46 @@ +// 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.register.provider.azure; + +import org.opengroup.osdu.azure.dependencies.AzureOSDUConfig; +import org.opengroup.osdu.register.provider.azure.di.AzureBootstrapConfig; +import org.opengroup.osdu.register.provider.azure.di.CosmosContainerConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication(exclude = { + MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class +}) +@ComponentScan(value = { + "org.opengroup.osdu.register", + "org.opengroup.osdu.core", + "org.opengroup.osdu.azure" +}) +public class RegisterApplication { + + public static void main(String[] args) { + Class<?>[] sources = new Class<?>[]{ + RegisterApplication.class, + AzureBootstrapConfig.class, + AzureOSDUConfig.class, + CosmosContainerConfig.class + }; + SpringApplication.run(sources, args); + } +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/Util/GoogleServiceAccountImpl.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/Util/GoogleServiceAccountImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b4f61f10b40cb4c0fd544dfc0090ea2585afa88d --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/Util/GoogleServiceAccountImpl.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.register.provider.azure.Util; + +import lombok.SneakyThrows; +import org.opengroup.osdu.register.utils.IGoogleServiceAccount; +import org.springframework.stereotype.Component; + +@Component +public class GoogleServiceAccountImpl implements IGoogleServiceAccount { + + @SneakyThrows + @Override + public String getIdToken(String keyString, String audience) { + // TODO Add implementation for generating GSA Tokens + return "Token"; + } + + @SneakyThrows + @Override + public String getPrivateKeyId(String keyString) { + // TODO Add implementation for fetching GSA Private Keys + return "Private-Key"; + } +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/action/ActionDoc.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/action/ActionDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..c7a08b56479213212f66a0830f24a668f765322b --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/action/ActionDoc.java @@ -0,0 +1,50 @@ +// 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.register.provider.azure.action; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.opengroup.osdu.register.action.model.Action; +import org.opengroup.osdu.register.action.model.Filter; + +import java.sql.Timestamp; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ActionDoc { + private String id; + private String name; + private String description; + private String img; + private String url; + private String contactEmail; + private Timestamp createdOnEpoch; + private Filter filter; + private String dataPartitionId; + + public ActionDoc(Action action, String dataPartitionId){ + this.id = action.getId(); + this.name = action.getName(); + this.description = action.getDescription(); + this.img = action.getImg(); + this.url = action.getUrl(); + this.contactEmail = action.getContactEmail(); + this.filter = action.getFilter(); + this.createdOnEpoch = new Timestamp(System.currentTimeMillis()); + this.dataPartitionId = dataPartitionId; + } +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/action/ActionRepository.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/action/ActionRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..577e94fa6d3dec404043f114991b281740b7fc7e --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/action/ActionRepository.java @@ -0,0 +1,125 @@ +// 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.register.provider.azure.action; + +import com.azure.cosmos.FeedOptions; +import com.azure.cosmos.SqlParameter; +import com.azure.cosmos.SqlParameterList; +import com.azure.cosmos.SqlQuerySpec; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.register.action.model.Action; +import org.opengroup.osdu.register.provider.interfaces.action.IActionRepo; +import org.opengroup.osdu.azure.CosmosStore; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; +import com.google.cloud.Timestamp; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; + +@Repository +public class ActionRepository implements IActionRepo { + + private final ReentrantLock mutex = new ReentrantLock(); + + @Autowired + private String actionContainer; + + @Autowired + private CosmosStore cosmosStore; + + @Autowired + private DpsHeaders headers; + + @Autowired + private String cosmosDBName; + + @Override + public Action createAction(Action action) { + + if(action.getId() == null){ + throw new AppException(400, "Bad Request", "Action id cannot be null"); + } + try { + mutex.lock(); + + if (exists(action.getId())) { + throwConflict(); + } + + ActionDoc doc = new ActionDoc(action, headers.getPartitionId()); + cosmosStore.upsertItem(headers.getPartitionId(), cosmosDBName, actionContainer, doc); + + } finally { + mutex.unlock(); + } + + return action; + } + + @Override + public Action get(String id) { + + ActionDoc doc = cosmosStore.findItem(headers.getPartitionId(), cosmosDBName, actionContainer, id, headers.getPartitionId(), ActionDoc.class) + .orElseThrow(() -> new AppException(404, "Not found", String.format("Action with id %s does not exist.", id))); + + return convertToActionClass(doc); + } + + @Override + public boolean delete(String id) { + if(!exists(id)){ + return false; + } + + cosmosStore.deleteItem(headers.getPartitionId(), cosmosDBName, actionContainer, id, headers.getPartitionId()); + return true; + } + + @Override + public List<Action> getAllActions() { + SqlQuerySpec query = new SqlQuerySpec("SELECT * FROM c where c.dataPartitionId = @partitionId"); + + SqlParameterList pars = query.getParameters(); + pars.add(new SqlParameter("@partitionId", headers.getPartitionId())); + + FeedOptions options = new FeedOptions(); + return cosmosStore.queryItems(headers.getPartitionId(), cosmosDBName, actionContainer, query, options, ActionDoc.class) + .stream().map(this::convertToActionClass).collect(Collectors.toList()); + } + + private boolean exists(String id) { + return cosmosStore.findItem(headers.getPartitionId(), cosmosDBName, actionContainer, id, headers.getPartitionId(), ActionDoc.class).isPresent(); + } + + private Action convertToActionClass(ActionDoc doc){ + Action action = new Action(); + action.setId(doc.getId()); + action.setName(doc.getName()); + action.setDescription(doc.getDescription()); + action.setImg(doc.getImg()); + action.setUrl(doc.getUrl()); + action.setContactEmail(doc.getContactEmail()); + action.setCreatedOnEpoch(Timestamp.of(doc.getCreatedOnEpoch())); + action.setFilter(doc.getFilter()); + return action; + } + + private void throwConflict() { + throw new AppException(409, "Conflict", "An action already exists with the same message and endpoint combination"); + } + +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsDoc.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..c8341c1a1654653a0c30fade308cc64937cff9cd --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsDoc.java @@ -0,0 +1,55 @@ +// 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.register.provider.azure.ddms; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.opengroup.osdu.register.ddms.model.Ddms; +import org.opengroup.osdu.register.ddms.model.RegisteredInterface; + +import java.sql.Timestamp; +import java.util.Set; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DdmsDoc { + + private String id; + private String name; + private String description; + private String contactEmail; + private Timestamp createdDateTimeEpoch; + private String dataPartitionId; + private Set<RegisteredInterfaceDoc> interfaces; + + public DdmsDoc(Ddms ddms, String dataPartitionId){ + this.id = ddms.getId(); + this.name = ddms.getName(); + this.description = ddms.getDescription(); + this.contactEmail = ddms.getContactEmail(); + this.createdDateTimeEpoch = new Timestamp(System.currentTimeMillis()); + this.dataPartitionId = dataPartitionId; + this.interfaces = ddms.getInterfaces().stream().map(this::convertToRegisteredInterfaceDoc).collect(Collectors.toSet()); + } + + private RegisteredInterfaceDoc convertToRegisteredInterfaceDoc(RegisteredInterface item){ + return new RegisteredInterfaceDoc(item.getEntityType(), item.getSchema()); + } + +} + diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsRepository.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..e7c4f7d35368eacd385d65d375a334774167de75 --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsRepository.java @@ -0,0 +1,130 @@ +// 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.register.provider.azure.ddms; + +import com.azure.cosmos.FeedOptions; +import com.azure.cosmos.SqlParameter; +import com.azure.cosmos.SqlParameterList; +import com.azure.cosmos.SqlQuerySpec; +import com.google.cloud.Timestamp; +import org.opengroup.osdu.azure.CosmosStore; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.register.ddms.model.Ddms; +import org.opengroup.osdu.register.ddms.model.RegisteredInterface; +import org.opengroup.osdu.register.provider.interfaces.ddms.IDdmsRepository; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +@Repository +public class DdmsRepository implements IDdmsRepository { + + private final ReentrantLock mutex = new ReentrantLock(); + + @Autowired + private String ddmsContainer; + + @Autowired + private CosmosStore cosmosStore; + + @Autowired + private DpsHeaders headers; + + @Autowired + private String cosmosDBName; + + @Override + public Ddms create(Ddms ddms) { + + try { + mutex.lock(); + + if(exists(ddms.getId())){ + throw new AppException(409, "Conflict", "A DDMS already exists with the same id"); + } + // TODO Size check for Ddms schema [Maximum Size for a CosmosDb item is 2MB] + + DdmsDoc doc = new DdmsDoc(ddms, headers.getPartitionId()); + cosmosStore.upsertItem(headers.getPartitionId(), cosmosDBName, ddmsContainer, doc); + + } finally { + mutex.unlock(); + } + + return ddms; + } + + @Override + public Ddms get(String id) { + + DdmsDoc doc = cosmosStore.findItem(headers.getPartitionId(), cosmosDBName, ddmsContainer, id, headers.getPartitionId(), DdmsDoc.class) + .orElseThrow(() -> new AppException(404, "Not found", String.format("DDMS with id %s does not exist.", id))); + + return ConvertToDdmsClass(doc); + } + + @Override + public List<Ddms> query(String type) { + + // Select * not supported, therefore writing all required fields + SqlQuerySpec query = new SqlQuerySpec("SELECT f.id, f.name, f.description, f.contactEmail, f.createdDateTimeEpoch, " + + "f.interfaces FROM f JOIN c IN f.interfaces where c.entityType = @type and f.dataPartitionId = @partitionId"); + + SqlParameterList pars = query.getParameters(); + pars.add(new SqlParameter("@type", type)); + pars.add(new SqlParameter("@partitionId", headers.getPartitionId())); + + FeedOptions options = new FeedOptions(); + return cosmosStore.queryItems(headers.getPartitionId(), cosmosDBName, ddmsContainer, query, options, DdmsDoc.class) + .stream().map(this::ConvertToDdmsClass).collect(Collectors.toList()); + } + + @Override + public boolean delete(String id) { + + if(!exists(id)){ + return false; + } + + cosmosStore.deleteItem(headers.getPartitionId(), cosmosDBName, ddmsContainer, id, headers.getPartitionId()); + return true; + } + + private boolean exists(String id) { + return cosmosStore.findItem(headers.getPartitionId(), cosmosDBName, ddmsContainer, id, headers.getPartitionId(), DdmsDoc.class).isPresent(); + } + + private Ddms ConvertToDdmsClass(DdmsDoc doc){ + Ddms ddms= new Ddms(); + ddms.setId(doc.getId()); + ddms.setName(doc.getName()); + ddms.setDescription(doc.getDescription()); + ddms.setContactEmail(doc.getContactEmail()); + ddms.setCreatedDateTimeEpoch(Timestamp.of(doc.getCreatedDateTimeEpoch())); + ddms.setInterfaces(doc.getInterfaces().stream().map(this::convertToRegisteredInterfaceClass).collect(Collectors.toSet())); + return ddms; + } + + private RegisteredInterface convertToRegisteredInterfaceClass(RegisteredInterfaceDoc doc){ + RegisteredInterface registeredInterface = new RegisteredInterface(); + registeredInterface.setEntityType(doc.getEntityType()); + registeredInterface.setSchema(doc.getSchema()); + return registeredInterface; + } +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/RegisteredInterfaceDoc.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/RegisteredInterfaceDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..a37fd34f2ff4d7a1d4b7d905cf5b96a493fa72f3 --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/ddms/RegisteredInterfaceDoc.java @@ -0,0 +1,29 @@ +// 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.register.provider.azure.ddms; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RegisteredInterfaceDoc { + private String entityType; + private Map<String, Object> schema; +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/di/AzureBootstrapConfig.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/di/AzureBootstrapConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..3caaa89aa53b06362ba8e3cb8b167cfc1b77eb34 --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/di/AzureBootstrapConfig.java @@ -0,0 +1,71 @@ +// 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.register.provider.azure.di; + +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.context.annotation.Configuration; + +import javax.inject.Named; + +@Configuration +public class AzureBootstrapConfig { + @Value("${azure.keyvault.url}") + private String keyVaultURL; + + @Value("${azure.cosmosdb.database}") + private String cosmosDBName; + + @Bean + @Named("KEY_VAULT_URL") + public String keyVaultURL() { + return keyVaultURL; + } + + @Bean + @Named("COSMOS_DB_NAME") + public String cosmosDBName() { + return cosmosDBName; + } + + @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"); + } + + 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/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/di/CosmosContainerConfig.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/di/CosmosContainerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..497510f5709a71c8eb87ff3861edfe1d413f6e9e --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/di/CosmosContainerConfig.java @@ -0,0 +1,45 @@ +// 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.register.provider.azure.di; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; + +public class CosmosContainerConfig { + + @Value("${registerAction.container.name}") + private String actionContainerName; + + @Value("${registerDdms.container.name}") + private String ddmsContainerName; + + @Value("${registerSubscription.container.name}") + private String subscriptionContainerName; + + @Bean + public String actionContainer(){ + return actionContainerName; + } + + @Bean + public String ddmsContainer(){ + return ddmsContainerName; + } + + @Bean + public String subscriptionContainer(){ + return subscriptionContainerName; + } +} \ No newline at end of file diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/security/AADSecurityConfig.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/security/AADSecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..9c658795fe86dabd08f44c7f7fd8edd48f30d9c2 --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/security/AADSecurityConfig.java @@ -0,0 +1,52 @@ +// 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.register.provider.azure.security; + +import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter; +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.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import javax.inject.Inject; + +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class AADSecurityConfig extends WebSecurityConfigurerAdapter { + @Inject + private AADAppRoleStatelessAuthenticationFilter appRoleAuthFilter; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER) + .and() + .authorizeRequests() + .antMatchers("/", "/index.html", + "/v2/api-docs", + "/configuration/ui", + "/swagger-resources/**", + "/configuration/security", + "/swagger", + "/swagger-ui.html", + "/webjars/**").permitAll() + .anyRequest().authenticated() + .and() + .addFilterBefore(appRoleAuthFilter, UsernamePasswordAuthenticationFilter.class); + } +} \ No newline at end of file diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/security/WhoamiController.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/security/WhoamiController.java new file mode 100644 index 0000000000000000000000000000000000000000..db37944c350d5646085e318ddd2da9554aa3501b --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/security/WhoamiController.java @@ -0,0 +1,38 @@ +// 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.register.provider.azure.security; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class WhoamiController { + @RequestMapping(value = "/whoami") + @ResponseBody + public String whoami() { + final Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + String userName = auth.getName(); + String roles = String.valueOf(auth.getAuthorities()); + String details = String.valueOf(auth.getPrincipal()); + + return "user: " + userName + "<BR>" + + "roles: " + roles + "<BR>" + + "details: " + details + "<BR>"; + } +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/subscriber/SubscriptionDoc.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/subscriber/SubscriptionDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..8d0d375c932f3849964db01e0e13599d1bf9aede --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/subscriber/SubscriptionDoc.java @@ -0,0 +1,53 @@ +// 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.register.provider.azure.subscriber; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.opengroup.osdu.register.subscriber.model.Subscription; + +import java.sql.Timestamp; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SubscriptionDoc { + private String id; + private String name; + private String description; + private String topic; + private String pushEndpoint; + private String createdBy; + private Timestamp createdOnEpoch; + private String notificationId; + private String secretType; + private String secretValue; + private String dataPartitionId; + + public SubscriptionDoc(Subscription subscription, String dataPartitionId){ + this.id = subscription.getId(); + this.name = subscription.getName(); + this.description = subscription.getDescription(); + this.topic = subscription.getTopic(); + this.pushEndpoint = subscription.getPushEndpoint(); + this.createdBy = subscription.getCreatedBy(); + this.createdOnEpoch = new Timestamp(System.currentTimeMillis()); + this.notificationId = subscription.getNotificationId(); + this.secretType = subscription.getSecret().getSecretType(); + this.secretValue = subscription.getSecret().toString(); + this.dataPartitionId = dataPartitionId; + } +} diff --git a/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/subscriber/SubscriptionRepository.java b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/subscriber/SubscriptionRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..3616cc98fa603f28d8abfea06ec3c3e087588fe4 --- /dev/null +++ b/provider/register-azure/src/main/java/org/opengroup/osdu/register/provider/azure/subscriber/SubscriptionRepository.java @@ -0,0 +1,154 @@ +package org.opengroup.osdu.register.provider.azure.subscriber; + +import com.azure.cosmos.FeedOptions; +import com.azure.cosmos.SqlParameter; +import com.azure.cosmos.SqlParameterList; +import com.azure.cosmos.SqlQuerySpec; +import com.google.cloud.Timestamp; +import org.opengroup.osdu.azure.CosmosStore; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.register.subscriber.model.*; +import org.opengroup.osdu.register.provider.interfaces.subscriber.ISubscriptionRepository; +import org.opengroup.osdu.register.utils.Constants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.stream.Collectors; + +@Repository +public class SubscriptionRepository implements ISubscriptionRepository { + + @Autowired + private String subscriptionContainer; + + @Autowired + private CosmosStore cosmosStore; + + @Autowired + private DpsHeaders headers; + + @Autowired + private String cosmosDBName; + + @Override + public Subscription create(Subscription input) throws Exception { + + // TODO Validate if is there any upper limit required for maximum number of subscriptions for a tenant + + if(exists(input.getId())){ + throw new AppException(409, "Conflict", "A subscriber already exists with the same topic and endpoint combination"); + } + + // TODO Encryption required before storing the secret or Do we need Keyvault to know the secrets + + SubscriptionDoc doc = new SubscriptionDoc(input, headers.getPartitionId()); + cosmosStore.upsertItem(headers.getPartitionId(), cosmosDBName, subscriptionContainer, doc); + + try{ + // TODO Add the flow to create subscription to the given topic + + return input; + } + catch (Exception e){ + cosmosStore.deleteItem(headers.getPartitionId(), cosmosDBName, subscriptionContainer, input.getId(), headers.getPartitionId()); + throw new AppException(500, "Server Error", "Unexpected error creating subscription", e); + } + } + + @Override + public Subscription get(String id) { + + SubscriptionDoc doc = cosmosStore.findItem(headers.getPartitionId(), cosmosDBName, subscriptionContainer, id, headers.getPartitionId(), SubscriptionDoc.class) + .orElseThrow(() -> new AppException(404, "Not found", String.format("Subscriber with id %s does not exist.", id))); + + return convertToSubscriptionClass(doc); + } + + @Override + public List<Subscription> query(String notificationId) { + + SqlQuerySpec query = new SqlQuerySpec("SELECT * from c where c.notificationId = @notificationId and c.dataPartitionId = @partitionId"); + + SqlParameterList pars = query.getParameters(); + pars.add(new SqlParameter("@notificationId", notificationId)); + pars.add(new SqlParameter("@partitionId", headers.getPartitionId())); + + FeedOptions options = new FeedOptions(); + + return cosmosStore.queryItems(headers.getPartitionId(), cosmosDBName, subscriptionContainer, query, options, SubscriptionDoc.class).stream() + .map(this::convertToSubscriptionClass) + .collect(Collectors.toList()); + + } + + @Override + public boolean delete(String id) { + + if(!exists(id)){ + return false; + } + + try{ + // TODO Add flow for deleting the subscription to the topic + + cosmosStore.deleteItem(headers.getPartitionId(), cosmosDBName, subscriptionContainer, id, headers.getPartitionId()); + return true; + } + catch (Exception e){ + throw new AppException(500, "Server Error", "Unexpected error deleting subscription.", e); + } + + } + + @Override + public boolean patch(Subscription subscription, Secret secret){ + return updateSecret(subscription, secret); + } + + @Override + public List<Subscription> getAll() throws Exception { + + SqlQuerySpec query = new SqlQuerySpec("SELECT * from c where c.dataPartitionId = @partitionId"); + + SqlParameterList pars = query.getParameters(); + pars.add(new SqlParameter("@partitionId", headers.getPartitionId())); + + FeedOptions options = new FeedOptions(); + + return cosmosStore.queryItems(headers.getPartitionId(), cosmosDBName, subscriptionContainer, query, options, SubscriptionDoc.class).stream() + .map(this::convertToSubscriptionClass) + .collect(Collectors.toList()); + } + + private boolean updateSecret(Subscription subscription, Secret secret) { + SubscriptionDoc doc = new SubscriptionDoc(subscription, headers.getPartitionId()); + doc.setSecretType(secret.getSecretType()); + doc.setSecretValue(secret.toString()); + + cosmosStore.upsertItem(headers.getPartitionId(), cosmosDBName, subscriptionContainer, doc); + return true; + } + + private boolean exists(String id) { + return cosmosStore.findItem(headers.getPartitionId(), cosmosDBName, subscriptionContainer, id, headers.getPartitionId(), SubscriptionDoc.class).isPresent(); + } + + private Subscription convertToSubscriptionClass(SubscriptionDoc doc) { + String secretValue = doc.getSecretValue(); + Secret secret; + if (doc.getSecretType().equals(Constants.GSASecret)) { + GsaSecret gsaSecret = new GsaSecret(); + String[] splitSecret = secretValue.split("`"); + gsaSecret.setValue(new GsaSecretValue(splitSecret[0], splitSecret[1])); + secret = gsaSecret; + } else { + HmacSecret hmacSecret = new HmacSecret(); + hmacSecret.setValue(secretValue); + secret = hmacSecret; + } + return new Subscription(doc.getId(), doc.getName(),doc.getDescription(), doc.getTopic(), doc.getPushEndpoint(), + doc.getCreatedBy(), Timestamp.of(doc.getCreatedOnEpoch()), doc.getNotificationId(), secret); + } +} diff --git a/provider/register-azure/src/main/resources/application.properties b/provider/register-azure/src/main/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..a7e53f05ea460fe8b43c8838ab6f7b0a836e594f --- /dev/null +++ b/provider/register-azure/src/main/resources/application.properties @@ -0,0 +1,45 @@ +# +# 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=register + +server.servlet.contextPath=/api/register/v1 +service.domain.name=${service_domain_name} + +# Azure AD configuration +azure.activedirectory.client-id=${aad_client_id} +azure.activedirectory.AppIdUri=api://${azure.activedirectory.client-id} +azure.activedirectory.session-stateless=true + +# Azure CosmosDB configuration +azure.cosmosdb.database=${cosmosdb_database} + +# Azure KeyVault configuration +azure.keyvault.url=${KEYVAULT_URI} + +# Azure App Insights configuration +azure.application-insights.instrumentation-key=${appinsights_key} + +# Application name +spring.application.name=register-azure + +# Cosmosdb container name +registerAction.container.name=RegisterAction +registerDdms.container.name=RegisterDdms +registerSubscription.container.name=RegisterSubscription + +#logging configuration +logging.transaction.enabled=true +logging.slf4jlogger.enabled=true diff --git a/provider/register-azure/src/test/java/org/opengroup/osdu/register/provider/azure/action/ActionRepositoryTest.java b/provider/register-azure/src/test/java/org/opengroup/osdu/register/provider/azure/action/ActionRepositoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..46e570b4fbc30564b8ce6721030e7a119373cefd --- /dev/null +++ b/provider/register-azure/src/test/java/org/opengroup/osdu/register/provider/azure/action/ActionRepositoryTest.java @@ -0,0 +1,200 @@ +// 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.register.provider.azure.action; + +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.azure.CosmosStore; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.register.action.model.Action; +import org.opengroup.osdu.register.action.model.Filter; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class ActionRepositoryTest { + + private static final String dataPartitionId = "data-partition-id"; + private static final String actionId = "id-1"; + + @Mock + private DpsHeaders headers; + + @Mock + private CosmosStore cosmosStore; + + @InjectMocks + private ActionRepository repo; + + @BeforeEach + void init() { + lenient().doReturn(dataPartitionId).when(headers).getPartitionId(); + } + + @Test + public void createAction_throws400_whenNullId() { + Action action = createAction(); + action.setId(null); + + AppException exception = assertThrows(AppException.class, () -> { + repo.createAction(action); + }); + assertEquals(400, exception.getError().getCode()); + assertEquals("Bad Request", exception.getError().getReason()); + assertEquals("Action id cannot be null", exception.getError().getMessage()); + } + + @Test + public void createAction_throws409_whenIdExists() { + Action action = createAction(); + Optional<ActionDoc> cosmosItem = Optional.of(new ActionDoc()); + + doReturn(cosmosItem).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + AppException exception = assertThrows(AppException.class, () -> { + repo.createAction(action); + }); + + assertEquals(409, exception.getError().getCode()); + assertEquals("Conflict", exception.getError().getReason()); + assertEquals("An action already exists with the same message and endpoint combination", exception.getError().getMessage()); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, actionId, dataPartitionId, ActionDoc.class); + } + + @Test + public void createAction_documentInsertedSuccessfully() { + Action action = createAction(); + + Optional<ActionDoc> cosmosItem = Optional.empty(); + doReturn(cosmosItem).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + doNothing().when(cosmosStore).upsertItem(anyString(), any(), any(), any()); + + Action output = repo.createAction(action); + + assertEquals(action, output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, actionId, dataPartitionId, ActionDoc.class); + } + + @Test + public void getAction_throws404_whenIdNotFound() { + + doReturn(Optional.empty()).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + AppException exception = assertThrows(AppException.class, () -> { + repo.get(actionId); + }); + + assertEquals(404, exception.getError().getCode()); + assertEquals("Not found", exception.getError().getReason()); + assertEquals("Action with id id-1 does not exist.", exception.getError().getMessage()); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, actionId, dataPartitionId, ActionDoc.class); + } + + @Test + public void getAction_itemRetrievedSuccessfully() { + + Action action = createAction(); + ActionDoc doc = new ActionDoc(action, dataPartitionId); + + doReturn(Optional.of(doc)).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + Action output = repo.get(actionId); + + assertEquals(action, output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, actionId, dataPartitionId, ActionDoc.class); + } + + @Test + public void deleteAction_returnsFalse_whenIdNotFound() { + + doReturn(Optional.empty()).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + boolean output = repo.delete(actionId); + + assertFalse(output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, actionId, dataPartitionId, ActionDoc.class); + } + + @Test + public void deleteAction_returnsTrue_whenDeletedSuccessfully() { + + Action action = createAction(); + ActionDoc doc = new ActionDoc(action, dataPartitionId); + + Optional<ActionDoc> cosmosItem = Optional.of(doc); + doReturn(cosmosItem).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + boolean output = repo.delete(actionId); + + assertTrue(output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, actionId, dataPartitionId, ActionDoc.class); + verify(cosmosStore, times(1)).deleteItem(dataPartitionId, null, null, actionId, dataPartitionId); + } + + @Test + public void getAllActions_returnAllItemsSuccessfully() { + + Action action1 = createAction(); + Action action2 = createAction(); + action2.setId("id-2"); + + ActionDoc doc1 = new ActionDoc(action1, dataPartitionId); + ActionDoc doc2 = new ActionDoc(action2, dataPartitionId); + + List<Action> actions = Stream.of(action1, action2).collect(Collectors.toList()); + List<ActionDoc> actionDocs = Stream.of(doc1, doc2).collect(Collectors.toList()); + + doReturn(actionDocs).when(cosmosStore).queryItems(anyString(), any(), any(), any(), any(), any()); + + List<Action> output = repo.getAllActions(); + + actions.get(0).setCreatedOnEpoch(output.get(0).getCreatedOnEpoch()); + actions.get(1).setCreatedOnEpoch(output.get(1).getCreatedOnEpoch()); + + assertEquals(actions, output); + } + + private Action createAction() { + Action action = new Action(); + action.setId(actionId); + action.setName("name"); + action.setDescription("description"); + action.setImg("https://mycdn.com/img.png"); + action.setUrl("https://myapp.osdu.opengroup.org/action/{id}/{data.project}"); + action.setFilter(new Filter()); + + return action; + } +} \ No newline at end of file diff --git a/provider/register-azure/src/test/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsRepositoryTest.java b/provider/register-azure/src/test/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsRepositoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1edd43af47e5e7a815aa7d8473066da44a7128c1 --- /dev/null +++ b/provider/register-azure/src/test/java/org/opengroup/osdu/register/provider/azure/ddms/DdmsRepositoryTest.java @@ -0,0 +1,191 @@ +// 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.register.provider.azure.ddms; + +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.azure.CosmosStore; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.register.ddms.model.Ddms; +import org.opengroup.osdu.register.ddms.model.RegisteredInterface; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class DdmsRepositoryTest { + + private static final String dataPartitionId = "data-partition-id"; + private static final String ddmsId = "id-1"; + + @Mock + private DpsHeaders headers; + + @Mock + private CosmosStore cosmosStore; + + @InjectMocks + private DdmsRepository repo; + + @BeforeEach + void init() { + lenient().doReturn(dataPartitionId).when(headers).getPartitionId(); + } + + @Test + public void createDdms_throws409_whenIdExists() { + Ddms ddms = createDdms(); + Optional<DdmsDoc> cosmosItem = Optional.of(new DdmsDoc()); + + doReturn(cosmosItem).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + AppException exception = assertThrows(AppException.class, () -> { + repo.create(ddms); + }); + + assertEquals(409, exception.getError().getCode()); + assertEquals("Conflict", exception.getError().getReason()); + assertEquals("A DDMS already exists with the same id", exception.getError().getMessage()); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, ddmsId, dataPartitionId, DdmsDoc.class); + } + + @Test + public void createDdms_documentInsertedSuccessfully() { + Ddms ddms = createDdms(); + + Optional<DdmsDoc> cosmosItem = Optional.empty(); + doReturn(cosmosItem).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + doNothing().when(cosmosStore).upsertItem(anyString(), any(), any(), any()); + + Ddms output = repo.create(ddms); + + assertEquals(ddms, output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, ddmsId, dataPartitionId, DdmsDoc.class); + } + + @Test + public void getDdms_throws404_whenIdNotFound() { + + doReturn(Optional.empty()).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + AppException exception = assertThrows(AppException.class, () -> { + repo.get(ddmsId); + }); + + assertEquals(404, exception.getError().getCode()); + assertEquals("Not found", exception.getError().getReason()); + assertEquals("DDMS with id id-1 does not exist.", exception.getError().getMessage()); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, ddmsId, dataPartitionId, DdmsDoc.class); + } + + @Test + public void getDdms_itemRetrievedSuccessfully() { + + Ddms ddms = createDdms(); + DdmsDoc doc = new DdmsDoc(ddms, dataPartitionId); + + doReturn(Optional.of(doc)).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + Ddms output = repo.get(ddmsId); + + assertEquals(ddms, output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, ddmsId, dataPartitionId, DdmsDoc.class); + } + + @Test + public void deleteDdms_returnsFalse_whenIdNotFound() { + + doReturn(Optional.empty()).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + boolean output = repo.delete(ddmsId); + + assertFalse(output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, ddmsId, dataPartitionId, DdmsDoc.class); + } + + @Test + public void deleteDdms_returnsTrue_whenDeletedSuccessfully() { + + Ddms ddms = createDdms(); + DdmsDoc doc = new DdmsDoc(ddms, dataPartitionId); + + Optional<DdmsDoc> cosmosItem = Optional.of(doc); + doReturn(cosmosItem).when(cosmosStore).findItem(anyString(), any(), any(), anyString(), anyString(), any()); + + boolean output = repo.delete(ddmsId); + + assertTrue(output); + verify(cosmosStore, times(1)).findItem(dataPartitionId, null, null, ddmsId, dataPartitionId, DdmsDoc.class); + verify(cosmosStore, times(1)).deleteItem(dataPartitionId, null, null, ddmsId, dataPartitionId); + } + + @Test + public void queryDdms_returnItemsSuccessfully() { + + Ddms ddms1 = createDdms(); + Ddms ddms2 = createDdms(); + ddms2.setId("id-2"); + + DdmsDoc doc1 = new DdmsDoc(ddms1, dataPartitionId); + DdmsDoc doc2 = new DdmsDoc(ddms2, dataPartitionId); + + List<Ddms> ddmsList = Stream.of(ddms1, ddms2).collect(Collectors.toList()); + List<DdmsDoc> ddmsDocList = Stream.of(doc1, doc2).collect(Collectors.toList()); + + doReturn(ddmsDocList).when(cosmosStore).queryItems(anyString(), any(), any(), any(), any(), any()); + + List<Ddms> output = repo.query("type"); + + ddmsList.get(0).setCreatedDateTimeEpoch(output.get(0).getCreatedDateTimeEpoch()); + ddmsList.get(1).setCreatedDateTimeEpoch(output.get(1).getCreatedDateTimeEpoch()); + + assertEquals(ddmsList, output); + } + + private Ddms createDdms() { + Ddms ddms = new Ddms(); + RegisteredInterface ri = new RegisteredInterface(); + ri.setEntityType("type"); + ri.setSchema(Collections.singletonMap("first", "second")); + + ddms.setId(ddmsId); + ddms.setName("mock-name"); + ddms.setDescription("mock-description"); + ddms.setContactEmail("mock-contactEmail"); + ddms.getInterfaces().add(ri); + return ddms; + } + +} diff --git a/testing/register-test-azure/Readme.md b/testing/register-test-azure/Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..9342a00bda976d6881f7c9984f8d06a39f6e33ed --- /dev/null +++ b/testing/register-test-azure/Readme.md @@ -0,0 +1,43 @@ +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. + +# Register Azure integration tests + +Register integration tests are refactored so that the business logic for integration tests resides in the `register-test-core` module and provider specific logic and execution steps reside in provider module (e.g. `register-test-azure`). To run the integration tests, the core module is built first and then the provider module is executed. Please read further to know more details. + +### Dependencies needed to run the integration tests +* JDK8 +* Maven +* Azure Devops access to slb-des-ext-collaboration organization. You need to generate a PAT that can access dependencies held in the Azure artifacts +* Values for the following environment variables in Config.java + + ``` + INTEGRATION_TESTER (System identity to assume for API calls. Note: this user must have entitlements configured already) + TESTER_SERVICEPRINCIPAL_SECRET (service account key which has admins api access) + NO_DATA_ACCESS_TESTER (Secret for $INTEGRATION_TESTER) + NO_DATA_ACCESS_TESTER_SERVICEPRINCIPAL_SECRET (Secret for $NO_DATA_ACCESS_TESTER) + ENVIRONMENT ('local' for local testing or 'dev' for dev testing) + AZURE_AD_TENANT_ID (AD tenant to authenticate users from) + AZURE_AD_APP_RESOURCE_ID (AAD client application ID) + DOMAIN (OSDU R2 to run tests under) + SUBSCRIBER_SECRET (sensitive secret to run HMAC tests) + REGISTER_BASE_URL (register service endpoint) + + ``` + + Above variables should be configured in the release pipeline to run integration tests. You should also replace them with proper values if you wish to run tests locally. + +### Commands to run tests +* Integration tests are refactored into two pieces: Core and Provider. Core contains business logic for tests and is a dependency for executing the tests from provider module. To build the core module, simply navigate to `register-test-core` directory and run `mvn clean install`. This will build the core module +* Next use the Junit template in IntelliJ to run all the tests in register-test-azure [ Use the EnvFile plugin to use environment variables directly from .env or .json/.yaml file] diff --git a/testing/register-test-azure/pom.xml b/testing/register-test-azure/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..796478b4f0b488e5396022c20fb84aeb20137f45 --- /dev/null +++ b/testing/register-test-azure/pom.xml @@ -0,0 +1,141 @@ +<?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 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.opengroup.osdu.register</groupId> + <artifactId>register-test-azure</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <description>Register service Azure integration tests </description> + + <properties> + <maven.compiler.target>1.8</maven.compiler.target> + <maven.compiler.source>1.8</maven.compiler.source> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.8</version> + </dependency> + <dependency> + <groupId>javax.json</groupId> + <artifactId>javax.json-api</artifactId> + <version>1.1.2</version> + </dependency> + <dependency> + <groupId>org.glassfish</groupId> + <artifactId>javax.json</artifactId> + <version>1.1.2</version> + </dependency> + <dependency> + <groupId>com.google.oauth-client</groupId> + <artifactId>google-oauth-client</artifactId> + <version>1.30.2</version> + </dependency> + <dependency> + <groupId>com.google.api-client</groupId> + <artifactId>google-api-client</artifactId> + <version>1.30.2</version> + <scope>compile</scope> + <exclusions> + <exclusion> + <artifactId>guava-jdk5</artifactId> + <groupId>com.google.guava</groupId> + </exclusion> + <exclusion> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.19.4</version> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.8.5</version> + </dependency> + <dependency> + <groupId>com.google.auth</groupId> + <artifactId>google-auth-library-oauth2-http</artifactId> + <version>0.15.0</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + <version>0.9.1</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>27.1-jre</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu.register</groupId> + <artifactId>register-test-core</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>au.com.dius</groupId> + <artifactId>pact-jvm-provider-junit_2.12</artifactId> + <version>3.5.5</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest</artifactId> + <version>2.1</version> + <scope>test</scope> + </dependency> + </dependencies> + + <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/157/packages/maven</url> + </repository> + <snapshotRepository> + <id>${gitlab-server}</id> + <url>https://community.opengroup.org/api/v4/projects/157/packages/maven</url> + </snapshotRepository> + </distributionManagement> +</project> diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestCreateActionApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestCreateActionApi.java new file mode 100644 index 0000000000000000000000000000000000000000..61ae03f55fbea7a7863d96cd41853b6bd71b3103 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestCreateActionApi.java @@ -0,0 +1,45 @@ +// 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.register.action; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestCreateActionApi extends CreateActionApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestDeleteActionApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestDeleteActionApi.java new file mode 100644 index 0000000000000000000000000000000000000000..4568b57f7c76ab5a22edac92932407a1ad02c2b6 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestDeleteActionApi.java @@ -0,0 +1,45 @@ +// 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.register.action; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestDeleteActionApi extends DeleteActionApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestGetActionByIdApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestGetActionByIdApi.java new file mode 100644 index 0000000000000000000000000000000000000000..0361e167677ebc80b87d73cda50c737cf7cf0178 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestGetActionByIdApi.java @@ -0,0 +1,45 @@ +// 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.register.action; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestGetActionByIdApi extends GetActionByIdApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestRetrieveActionApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestRetrieveActionApi.java new file mode 100644 index 0000000000000000000000000000000000000000..43e12504e6759e75b8fb9c6e67816aa74569ac99 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/action/TestRetrieveActionApi.java @@ -0,0 +1,45 @@ +// 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.register.action; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestRetrieveActionApi extends RetrieveActionApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestCreateRegistrationApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestCreateRegistrationApi.java new file mode 100644 index 0000000000000000000000000000000000000000..ed01212993a00a9ab4175e6b86545939fd0dfc08 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestCreateRegistrationApi.java @@ -0,0 +1,45 @@ +// 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.register.ddms; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestCreateRegistrationApi extends CreateRegistrationApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestDeleteRegistrationApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestDeleteRegistrationApi.java new file mode 100644 index 0000000000000000000000000000000000000000..bb29ed00edd49ded73c61238bbc3d1c739b5e6dd --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestDeleteRegistrationApi.java @@ -0,0 +1,45 @@ +// 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.register.ddms; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestDeleteRegistrationApi extends DeleteRegistrationApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestGetConsumptionByIdApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestGetConsumptionByIdApi.java new file mode 100644 index 0000000000000000000000000000000000000000..9885eb911f2ffac4bd61e03222be4b0befb96722 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestGetConsumptionByIdApi.java @@ -0,0 +1,45 @@ +// 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.register.ddms; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestGetConsumptionByIdApi extends GetConsumptionByIdApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestGetRegistrationByIdApi.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestGetRegistrationByIdApi.java new file mode 100644 index 0000000000000000000000000000000000000000..01537b432c1a6dcfde27ea77ae02acc2007f74c6 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestGetRegistrationByIdApi.java @@ -0,0 +1,45 @@ +// 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.register.ddms; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestGetRegistrationByIdApi extends GetRegistrationByIdApiTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestQueryDdmsByType.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestQueryDdmsByType.java new file mode 100644 index 0000000000000000000000000000000000000000..1a20bada2670ccf7834100f898863d547b912e7f --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/ddms/TestQueryDdmsByType.java @@ -0,0 +1,45 @@ +// 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.register.ddms; + +import com.sun.jersey.api.client.ClientResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.register.util.AzureTestUtils; + +import static org.junit.Assert.assertEquals; + +public class TestQueryDdmsByType extends QueryDdmsByTypeTest { + + @Before + @Override + public void setup() { + this.testUtils = new AzureTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getId(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } +} diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/util/AzureServicePrincipal.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/util/AzureServicePrincipal.java new file mode 100644 index 0000000000000000000000000000000000000000..ebe11ce88a1ecb8370ac4f33944d6070ab0f00ea --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/util/AzureServicePrincipal.java @@ -0,0 +1,84 @@ +// 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.register.util; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; + +public class AzureServicePrincipal { + public static String getIdToken(String sp_id, String sp_secret, String tenant_id, String app_resource_id) throws Exception { + String aad_endpoint = String.format("https://login.microsoftonline.com/%s/oauth2/token", tenant_id); + URL url = new URL(aad_endpoint); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("POST"); + con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + + 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); + + con.setDoOutput(true); + DataOutputStream out = new DataOutputStream(con.getOutputStream()); + out.writeBytes(getParamsString(parameters)); + out.flush(); + out.close(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer content = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + content.append(inputLine); + } + in.close(); + + con.disconnect(); + + Gson gson = new Gson(); + JsonObject jobj = gson.fromJson(content.toString(), JsonObject.class); + String token = jobj.get("access_token").getAsString(); + return token; + } + + private static String getParamsString(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; + } +} + diff --git a/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/util/AzureTestUtils.java b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/util/AzureTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..6a3db22003fcb37a4d43546b17c5fdbe46753490 --- /dev/null +++ b/testing/register-test-azure/src/test/java/org/opengroup/osdu/register/util/AzureTestUtils.java @@ -0,0 +1,62 @@ +// 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.register.util; + +import com.google.common.base.Strings; + +public class AzureTestUtils extends TestUtils{ + + @Override + public synchronized String getOpsAccessToken() 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 getAdmAccessToken() throws Exception { + if (Strings.isNullOrEmpty(admToken)) { + admToken = getToken(System.getProperty("INTEGRATION_TESTER", System.getenv("INTEGRATION_TESTER")), + System.getProperty("TESTER_SERVICEPRINCIPAL_SECRET", System.getenv("TESTER_SERVICEPRINCIPAL_SECRET"))); + } + return "Bearer " + admToken; + } + + @Override + public synchronized String getEditorAccessToken() 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 getNoDataAccessToken() 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 AzureServicePrincipal.getIdToken(sp_id, sp_secret, tenant_id, app_resource_id); + } +}