diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05e9e5b54cd409492b7168f28c4ca52d90179361..7ac3f3c41202f49b965e61f10bca6c46ede2c314 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,9 @@ variables: AZURE_SERVICE: partition AZURE_BUILD_SUBDIR: provider/partition-azure AZURE_TEST_SUBDIR: testing/partition-test-azure + + IBM_BUILD_SUBDIR: provider/partition-ibm + IBM_INT_TEST_SUBDIR: testing/partition-test-ibm include: @@ -27,3 +30,6 @@ include: - project: "osdu/platform/ci-cd-pipelines" file: "cloud-providers/azure.yml" + + - project: "osdu/platform/ci-cd-pipelines" + file: "cloud-providers/ibm.yml" diff --git a/NOTICE b/NOTICE index 38955c732f300818a5b7660290915bf185a1fdea..bc04d6687fcc17987c157bcd619dd59570aac996 100644 --- a/NOTICE +++ b/NOTICE @@ -317,7 +317,6 @@ The following software have components provided under the terms of this license: - StAX (from http://stax.codehaus.org/) - Stax2 API (from http://github.com/FasterXML/stax2-api) - jaxen (from http://jaxen.codehaus.org/) -- oro (from ) ======================================================================== BSD-3-Clause @@ -355,7 +354,7 @@ The following software have components provided under the terms of this license: - jaxen (from http://jaxen.codehaus.org/) ======================================================================== -CC-BY-3.0 +CC-BY-2.5 ======================================================================== The following software have components provided under the terms of this license: @@ -383,7 +382,6 @@ CDDL-1.0 ======================================================================== The following software have components provided under the terms of this license: -- JavaBeans(TM) Activation Framework (from http://java.sun.com/javase/technologies/desktop/javabeans/jaf/index.jsp) - JavaMail API (from ) - Servlet Specification 2.5 API (from ) - javax.annotation-api (from http://jcp.org/en/jsr/detail?id=250) @@ -394,16 +392,23 @@ CDDL-1.1 The following software have components provided under the terms of this license: - JavaBeans Activation Framework (from ) +- JavaBeans(TM) Activation Framework (from http://java.sun.com/javase/technologies/desktop/javabeans/jaf/index.jsp) - tomcat-embed-core (from http://tomcat.apache.org/) ======================================================================== -EPL-1.0 +CPL-1.0 ======================================================================== The following software have components provided under the terms of this license: -- Expression Language 3.0 (from https://projects.eclipse.org/projects/ee4j.el) - JUnit (from http://junit.org) - JUnit (from http://junit.org) + +======================================================================== +EPL-1.0 +======================================================================== +The following software have components provided under the terms of this license: + +- Expression Language 3.0 (from https://projects.eclipse.org/projects/ee4j.el) - JUnit Jupiter (Aggregator) (from https://junit.org/junit5/) - Logback Classic Module (from ) - Logback Core Module (from ) @@ -600,7 +605,6 @@ The following software have components provided under the terms of this license: - LatencyUtils (from http://latencyutils.github.io/LatencyUtils/) - Plexus Common Utilities (from http://plexus.codehaus.org/plexus-utils) - Spongy Castle (from http://rtyley.github.io/spongycastle/) -- xml-apis (from ) ======================================================================== SISSL-1.2 @@ -648,8 +652,10 @@ The following software have components provided under the terms of this license: - Project Lombok (from https://projectlombok.org) - Project Lombok (from https://projectlombok.org) - Spring Web (from https://github.com/spring-projects/spring-framework) +- StAX API (from http://stax.codehaus.org/) - msal4j (from https://github.com/AzureAD/microsoft-authentication-library-for-java) - reactive-streams (from http://www.reactive-streams.org/) +- xml-apis (from ) ======================================================================== unknown @@ -657,6 +663,8 @@ unknown The following software have components provided under the terms of this license: - Byte Buddy (without dependencies) (from ) +- JUnit (from http://junit.org) +- JUnit (from http://junit.org) - JUnit Jupiter (Aggregator) (from https://junit.org/junit5/) - JavaBeans Activation Framework API jar (from ) - JavaMail API (from ) diff --git a/pom.xml b/pom.xml index bffbca15c89ccea52d4713d304697b047c221cad..2501b81beeff5d2bb455d5250b9b34404e628e0a 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,7 @@ <module>partition-core</module> <module>provider/partition-azure</module> <module>provider/partition-aws</module> + <module>provider/partition-ibm</module> </modules> <profiles> @@ -133,6 +134,18 @@ <modules> <module>provider/partition-aws</module> </modules> + </profile> + <profile> + <id>partition-ibm</id> + <activation> + <property> + <name>env</name> + <value>partition-ibm</value> + </property> + </activation> + <modules> + <module>provider/partition-ibm</module> + </modules> </profile> <profile> <id>Default</id> diff --git a/provider/partition-ibm/pom.xml b/provider/partition-ibm/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..65be31fae77cd265997b26ea665b4a7c05dcab2e --- /dev/null +++ b/provider/partition-ibm/pom.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + + --> + +<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>partition</artifactId> + <groupId>org.opengroup.osdu</groupId> + <version>1.0.0</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <artifactId>partition-ibm</artifactId> + <version>1.0.0</version> + <description>Partition service on IBM</description> + <packaging>jar</packaging> + + <properties> + <aws.version>1.11.637</aws.version> + </properties> + + <dependencies> + <!-- Internal packages --> + <!-- <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-common</artifactId> + <version>${os-core-common.version}</version> + </dependency> --> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-lib-ibm</artifactId> + <version>0.3.8-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>partition-core</artifactId> + <version>1.0.0</version> + </dependency> + + + <!-- Third party Apache 2.0 license packages --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-security</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-client</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-oauth2-jose</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.data</groupId> + <artifactId>spring-data-commons</artifactId> + <version>2.1.10.RELEASE</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>javax.inject</groupId> + <artifactId>javax.inject</artifactId> + <version>1</version> + </dependency> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + + <!-- Testing packages --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>2.25.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>2.0.2</version> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito2</artifactId> + <version>2.0.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + </exclusion> + <exclusion> + <groupId>org.junit.vintage</groupId> + <artifactId>junit-vintage-engine</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + <configuration> + <classifier>spring-boot</classifier> + <mainClass>org.opengroup.osdu.partition.provider.ibm.PartitionApplication</mainClass> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/PartitionApplication.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/PartitionApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..aed280e4445b745237a240d673d28eee35c16bdd --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/PartitionApplication.java @@ -0,0 +1,18 @@ + +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@ComponentScan({"org.opengroup.osdu"}) +@SpringBootApplication +public class PartitionApplication { + + public static void main(String[] args) { + SpringApplication.run(PartitionApplication.class, args); + } +} \ No newline at end of file diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/GroupCache.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/GroupCache.java new file mode 100644 index 0000000000000000000000000000000000000000..e08ac65029167f5d200c997547ad5e8db52f1c0a --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/GroupCache.java @@ -0,0 +1,17 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.cache; + +import org.opengroup.osdu.core.common.model.entitlements.Groups; +import org.opengroup.osdu.core.common.cache.VmCache; +import org.springframework.stereotype.Component; + +@Component +public class GroupCache extends VmCache<String, Groups> { + + public GroupCache() { + super(30, 1000); + } +} + diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/PartitionListCacheImpl.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/PartitionListCacheImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..a1dc123275a4dbc9799979fe8a6a559d8c70e721 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/PartitionListCacheImpl.java @@ -0,0 +1,45 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.cache; + +import java.util.List; + +import javax.annotation.Resource; + +import org.opengroup.osdu.core.common.cache.ICache; +import org.opengroup.osdu.partition.provider.interfaces.IPartitionServiceCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +@Service +@Qualifier("partitionListCache") +public class PartitionListCacheImpl implements IPartitionServiceCache<String, List<String>> { + + + @Autowired + @Qualifier("partitionListCache") + private ICache<String, List<String>> cache; + + @Override + public void put(String s, List<String> o) { + this.cache.put(s, o); + } + + @Override + public List<String> get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } + +} diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/PartitionServiceCacheImpl.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/PartitionServiceCacheImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4afd7cd1f50890924930fb762b179603ec017274 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/PartitionServiceCacheImpl.java @@ -0,0 +1,42 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.cache; + +import org.opengroup.osdu.core.common.cache.ICache; +import org.opengroup.osdu.partition.model.PartitionInfo; +import org.opengroup.osdu.partition.provider.interfaces.IPartitionServiceCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +@Service +@Qualifier("partitionServiceCache") +public class PartitionServiceCacheImpl implements IPartitionServiceCache<String, PartitionInfo> { + + @Autowired + @Qualifier("partitionServiceCache") + private ICache<String, PartitionInfo> cache; + + @Override + public void put(String s, PartitionInfo o) { + this.cache.put(s, o); + } + + @Override + public PartitionInfo get(String s) { + return this.cache.get(s); + } + + @Override + public void delete(String s) { + this.cache.delete(s); + } + + @Override + public void clearAll() { + this.cache.clearAll(); + } +} diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/VmCacheConfiguration.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/VmCacheConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..892b6236c9f1dc06c24e79812b200042c9e10868 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/cache/VmCacheConfiguration.java @@ -0,0 +1,28 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.cache; + +import java.util.List; + +import org.opengroup.osdu.core.common.cache.VmCache; +import org.opengroup.osdu.partition.model.PartitionInfo; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class VmCacheConfiguration { + + @Bean(name = "partitionListCache") + public VmCache<String, List<String>> partitionListCache(@Value("${cache.expiration}") final int expiration, + @Value("${cache.maxSize}") final int maxSize) { + return new VmCache<>(expiration * 60, maxSize); + } + + @Bean(name ="partitionServiceCache") + public VmCache<String, PartitionInfo> partitionServiceCache(@Value("${cache.expiration}") final int expiration, + @Value("${cache.maxSize}") final int maxSize) { + return new VmCache<>(expiration * 60, maxSize); + } +} diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/model/PartitionDoc.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/model/PartitionDoc.java new file mode 100644 index 0000000000000000000000000000000000000000..ab25a7444c1f758653dec81f8dddad43d993037c --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/model/PartitionDoc.java @@ -0,0 +1,28 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.model; + +import org.opengroup.osdu.partition.model.PartitionInfo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PartitionDoc { + private String _id; + private String _rev; + PartitionInfo partitionInfo; + + public PartitionDoc(String partitionId, PartitionInfo partitionInfo) { + this._id = partitionId; + this.partitionInfo = partitionInfo; + } + + +} diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/AuthorizationService.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/AuthorizationService.java new file mode 100644 index 0000000000000000000000000000000000000000..ce059f118aa3eb1cf0e46e9b9ea13c130611e904 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/AuthorizationService.java @@ -0,0 +1,49 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.security; + +import org.opengroup.osdu.core.common.entitlements.IEntitlementsAndCacheService; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.partition.provider.interfaces.IAuthorizationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +@Component +@RequestScope +public class AuthorizationService implements IAuthorizationService { + + public static final String PARTITION_ADMIN_ROLE = "service.partition.admin"; + + @Autowired + private IEntitlementsAndCacheService entitlementsAndCacheService; + + @Autowired + private DpsHeaders headers; + + @Override + public boolean isDomainAdminServiceAccount() { + try { + return hasRole(PARTITION_ADMIN_ROLE); + } + catch (AppException e) { + throw e; + } + catch (Exception e) { + throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Authentication Failure", e.getMessage(), e); + } + + } + + private boolean hasRole(String requiredRole) { + //headers.put(DpsHeaders.DATA_PARTITION_ID, PARTITION_NAME); + String user = entitlementsAndCacheService.authorize(headers, requiredRole); + headers.put(DpsHeaders.USER_EMAIL, user); + return true; + } + +} + diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/EntitlementsAndCacheServiceImpl.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/EntitlementsAndCacheServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1e341267f21910c8d86794c7e71c72cedefa1cc9 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/EntitlementsAndCacheServiceImpl.java @@ -0,0 +1,153 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.security; + +import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.model.entitlements.Acl; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.core.common.cache.ICache; +import org.opengroup.osdu.core.common.model.storage.RecordMetadata; +import org.opengroup.osdu.core.common.util.Crc32c; +import org.opengroup.osdu.core.common.model.entitlements.EntitlementsException; +import org.opengroup.osdu.core.common.model.entitlements.Groups; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.http.HttpResponse; +import org.opengroup.osdu.core.common.entitlements.IEntitlementsFactory; +import org.opengroup.osdu.core.common.entitlements.IEntitlementsService; +import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; +import org.opengroup.osdu.core.common.entitlements.IEntitlementsAndCacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +public class EntitlementsAndCacheServiceImpl implements IEntitlementsAndCacheService { + + private static final String ERROR_REASON = "Access denied"; + private static final String ERROR_MSG = "The user is not authorized to perform this action"; + + @Autowired + private IEntitlementsFactory factory; + + @Autowired + private ICache<String, Groups> cache; + + @Autowired + private JaxRsDpsLog logger; + + @Override + public String authorize(DpsHeaders headers, String... roles) { + Groups groups = this.getGroups(headers); + if (groups.any(roles)) { + return groups.getDesId(); + } else { + throw new AppException(HttpStatus.SC_UNAUTHORIZED, ERROR_REASON, ERROR_MSG); + } + } + + @Override + public boolean isValidAcl(DpsHeaders headers, Set<String> acls) { + Groups groups = this.getGroups(headers); + if (groups.getGroups() == null || groups.getGroups().isEmpty()) { + this.logger.error("Error on getting groups for user: " + headers.getUserEmail()); + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Unknown error", + "Unknown error happened when validating ACL"); + } + String email = groups.getGroups().get(0).getEmail(); + if (!email.matches("^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$")) { + this.logger.error("Email address is invalid for this group: " + groups.getGroups().get(0)); + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Unknown error", + "Unknown error happened when validating ACL"); + } + String domain = email.split("@")[1]; + for (String acl : acls) { + if (!acl.split("@")[1].equalsIgnoreCase(domain)) { + return false; + } + } + return true; + } + + @Override + public boolean hasOwnerAccess(DpsHeaders headers, String[] ownerList) { + Groups groups = this.getGroups(headers); + Set<String> aclList = new HashSet<>(); + + for (String owner : ownerList) { + aclList.add(owner.split("@")[0]); + } + + String[] acls = new String[aclList.size()]; + return groups.any(aclList.toArray(acls)); + } + + @Override + public List<RecordMetadata> hasValidAccess(List<RecordMetadata> recordsMetadata, DpsHeaders headers) { + Groups groups = this.getGroups(headers); + List<RecordMetadata> result = new ArrayList<>(); + + for (RecordMetadata recordMetadata : recordsMetadata) { + Acl storageAcl = recordMetadata.getAcl(); + if (hasAccess(storageAcl, groups)) { + result.add(recordMetadata); + } else { + this.logger.warning("Post ACL check fails: " + recordMetadata.getId()); + } + } + + return result; + } + + private boolean hasAccess(Acl storageAcl, Groups groups) { + String[] viewers = storageAcl.getViewers(); + String[] owners = storageAcl.getOwners(); + Set<String> aclList = new HashSet<>(); + + for (String viewer : viewers) { + aclList.add(viewer.split("@")[0]); + } + for (String owner : owners) { + aclList.add(owner.split("@")[0]); + } + + String[] acls = new String[aclList.size()]; + if (groups.any(aclList.toArray(acls))) { + return true; + } else { + return false; + } + } + + protected Groups getGroups(DpsHeaders headers) { + String cacheKey = this.getGroupCacheKey(headers); + Groups groups = this.cache.get(cacheKey); + + if (groups == null) { + IEntitlementsService service = this.factory.create(headers); + try { + groups = service.getGroups(); + this.cache.put(cacheKey, groups); + this.logger.info("Entitlements cache miss"); + + } catch (EntitlementsException e) { + e.printStackTrace(); + HttpResponse response = e.getHttpResponse(); + this.logger.error(String.format("Error requesting entitlements service %s", response)); + throw new AppException(e.getHttpResponse().getResponseCode(), ERROR_REASON, ERROR_MSG, e); + } + } + + return groups; + } + + protected static String getGroupCacheKey(DpsHeaders headers) { + String key = String.format("entitlement-groups:%s:%s", headers.getPartitionIdWithFallbackToAccountId(), + headers.getAuthorization()); + return Crc32c.hashToBase64EncodedString(key); + } +} diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/EntitlementsClientFactory.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/EntitlementsClientFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..6fa6377a7faffa3eb34d1f229ecaa756d7e51214 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/EntitlementsClientFactory.java @@ -0,0 +1,36 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.security; + +import org.opengroup.osdu.core.common.entitlements.EntitlementsAPIConfig; +import org.opengroup.osdu.core.common.entitlements.EntitlementsFactory; +import org.opengroup.osdu.core.common.entitlements.IEntitlementsFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.stereotype.Component; + +@Component +public class EntitlementsClientFactory extends AbstractFactoryBean<IEntitlementsFactory> { + + @Value("${AUTHORIZE_API}") + private String AUTHORIZE_API; + + @Value("${AUTHORIZE_API_KEY:}") + private String AUTHORIZE_API_KEY; + + @Override + protected IEntitlementsFactory createInstance() throws Exception { + + return new EntitlementsFactory(EntitlementsAPIConfig + .builder() + .rootUrl(AUTHORIZE_API) + .apiKey(AUTHORIZE_API_KEY) + .build()); + } + + @Override + public Class<?> getObjectType() { + return IEntitlementsFactory.class; + } +} \ No newline at end of file diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/SecurityConfigIBM.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/SecurityConfigIBM.java new file mode 100644 index 0000000000000000000000000000000000000000..8da96136277545f93e232d755c422b87249a4ad8 --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/security/SecurityConfigIBM.java @@ -0,0 +1,26 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.security; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@EnableWebSecurity +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class SecurityConfigIBM extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable().authorizeRequests() + .antMatchers("_ah/liveness_check", + "/_ah/readiness_check", + "/swagger-resources/**", + "/configuration/security", + "/webjars/**") + .permitAll().anyRequest().authenticated().and().oauth2ResourceServer().jwt(); + } +} \ No newline at end of file diff --git a/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/service/PartitionServiceImpl.java b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/service/PartitionServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..247f48402ae97a50d6e1c5379e0541d0c848d7bf --- /dev/null +++ b/provider/partition-ibm/src/main/java/org/opengroup/osdu/partition/provider/ibm/service/PartitionServiceImpl.java @@ -0,0 +1,168 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.provider.ibm.service; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.http.HttpStatus; +import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; +import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.ibm.auth.ServiceCredentials; +import org.opengroup.osdu.core.ibm.cloudant.IBMCloudantClientFactory; +import org.opengroup.osdu.partition.model.PartitionInfo; +import org.opengroup.osdu.partition.provider.ibm.model.PartitionDoc; +import org.opengroup.osdu.partition.provider.interfaces.IPartitionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import com.cloudant.client.api.Database; +import com.cloudant.client.api.model.Response; +import com.cloudant.client.org.lightcouch.DocumentConflictException; +import com.cloudant.client.org.lightcouch.NoDocumentException; + +import lombok.extern.slf4j.Slf4j; + +@Service +@Slf4j +public class PartitionServiceImpl implements IPartitionService { + + private static final String PARTITION_DATABASE = "partition"; + + @Autowired + private JaxRsDpsLog logger; + + Database db; + + private IBMCloudantClientFactory cloudantFactory; + + @Value("${ibm.db.url}") + private String dbUrl; + @Value("${ibm.db.apikey:#{null}}") + private String apiKey; + @Value("${ibm.db.user:#{null}}") + private String dbUser; + @Value("${ibm.db.password:#{null}}") + private String dbPassword; + @Value("${ibm.env.prefix:local-dev}") + private String dbNamePrefix; + + public PartitionServiceImpl() { + + } + + @PostConstruct + public void init() { + cloudantFactory = new IBMCloudantClientFactory(new ServiceCredentials(dbUrl, dbUser, dbPassword)); + try { + db = cloudantFactory.getDatabase(dbNamePrefix, PARTITION_DATABASE); + } catch (MalformedURLException e) { + log.error("malformed URL has occurred.", e); + e.printStackTrace(); + } + } + + @Override + public PartitionInfo createPartition(String partitionId, PartitionInfo partitionInfo) { + PartitionDoc partitionDoc = new PartitionDoc(partitionId, partitionInfo); + try { + db.save(partitionDoc); + return partitionInfo; + } catch (DocumentConflictException e) { + log.error("Partition already exists"); + throw new AppException(e.getStatusCode(), "Conflict", "partition already exists", e); + } catch (Exception e) { + log.info("Partition creation failed "); + e.printStackTrace(); + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage(), "Partition creation failed", e); + } + + } + + @Override + public PartitionInfo updatePartition(String partitionId, PartitionInfo partitionInfo) { + if (partitionInfo.getProperties().containsKey("id")) { + throw new AppException(HttpStatus.SC_BAD_REQUEST, "can not update id", "the field id can not be updated"); + } + try { + PartitionDoc partitionDoc = db.find(PartitionDoc.class, partitionId); + partitionDoc.getPartitionInfo().getProperties().putAll(partitionInfo.getProperties()); + Response update = db.update(partitionDoc); + return partitionDoc.getPartitionInfo(); + } catch (NoDocumentException e) { + log.error(String.format("%s partition does not exists", partitionId)); + e.printStackTrace(); + throw new AppException(e.getStatusCode(), "Partition not found", + String.format("%s Update failed. Create partition first. partition does not exists", partitionId), + e); + } catch (DocumentConflictException e) { + log.error("Partition update failed. conflict is detected during the update"); + e.printStackTrace(); + throw new AppException(e.getStatusCode(), e.getReason(), e.getMessage(), e); + } catch (Exception e) { + log.error("Partition update failed"); + e.printStackTrace(); + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Partition update failed", e.getMessage(), e); + } + + } + + @Override + public PartitionInfo getPartition(String partitionId) { + PartitionDoc partitionDoc = null; + try { + partitionDoc = db.find(PartitionDoc.class, partitionId); + } catch (NoDocumentException e) { + log.error(String.format("%s partition does not exists", partitionId)); + e.printStackTrace(); + throw new AppException(e.getStatusCode(), e.getReason(), String.format("%s partition does not exists", partitionId), e); + } catch (Exception e) { + log.error("Partition could not found"); + e.printStackTrace(); + throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "unknown error", "Partition could not found", e ); + } + return partitionDoc.getPartitionInfo(); + } + + @Override + public boolean deletePartition(String partitionId) { + Response deleteStatus = null; + try { + PartitionDoc partitionDoc = db.find(PartitionDoc.class, partitionId); + deleteStatus = db.remove(partitionDoc); + } catch (NoDocumentException e) { + log.error(String.format("Deletion failed. Could not find partition ", partitionId)); + e.printStackTrace(); + throw new AppException(e.getStatusCode(), e.getReason(), String.format("Deletion failed. Could not find partition %s", partitionId), e); + } catch (Exception e) { + log.error("Deletion Failed. Unexpected error"); + e.printStackTrace(); + } + if(deleteStatus.getStatusCode() == 200) { + return true; + } + return false; + + } + + @Override + public List<String> getAllPartitions() { + List<String> partitionList = null; + try { + partitionList = db.getAllDocsRequestBuilder().includeDocs(true).build().getResponse().getDocIds(); + } catch (IOException e) { + log.error("Partitions could not found. IOException occurred", e); + e.printStackTrace(); + } catch (Exception e) { + log.error("Partition could not found.", e); + e.printStackTrace(); + } + return partitionList; + } + +} diff --git a/provider/partition-ibm/src/main/resources/application.properties b/provider/partition-ibm/src/main/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..82948a6526214f97e364a15ec10de00049d51ff2 --- /dev/null +++ b/provider/partition-ibm/src/main/resources/application.properties @@ -0,0 +1,28 @@ +LOG_PREFIX=partition +server.servlet.contextPath=/api/partition/v1 +server.port=8090 +springfox.documentation.swagger.v2.path=/api-docs +AUTHORIZE_API=TODO +#ACCEPT_HTTP=true + +#logging configuration +logging.level.org.springframework.web=DEBUG +logging.level.org.opengroup.osdu=debug +#logging.transaction.enabled=true +#logging.slf4jlogger.enabled=true + +cache.provider=vm +cache.expiration=1 +cache.maxSize=1000 + +ibm.health-check-uri=/api/partition/v1/_ah/liveness_check,/api/partition/v1/_ah/readiness_check +excluded.uris.for.partition-filter=/api/partition/v1/partitions + +#Database Configuration +ibm.env.prefix=TODO +ibm.db.url=TODO +ibm.db.user=TODO +ibm.db.password=TODO + +spring.security.oauth2.resourceserver.jwt.jwk-set-uri=TODO + diff --git a/provider/partition-ibm/src/test/java/org/opengroup/osdu/partition/provider/ibm/service/PartitionServiceImplTest.java b/provider/partition-ibm/src/test/java/org/opengroup/osdu/partition/provider/ibm/service/PartitionServiceImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2d851b56a285cdb414227d4c2a442c2c88c5c0a3 --- /dev/null +++ b/provider/partition-ibm/src/test/java/org/opengroup/osdu/partition/provider/ibm/service/PartitionServiceImplTest.java @@ -0,0 +1,5 @@ +package org.opengroup.osdu.partition.provider.ibm.service; + +public class PartitionServiceImplTest { + //TODO : Unit Test +} diff --git a/provider/partition-ibm/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/provider/partition-ibm/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000000000000000000000000000000000..ca6ee9cea8ec189a088d50559325d4e84ff8ad09 --- /dev/null +++ b/provider/partition-ibm/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file diff --git a/testing/partition-test-ibm/README.md b/testing/partition-test-ibm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..08f9e48c17092b6ed1fe309009679f2f54274276 --- /dev/null +++ b/testing/partition-test-ibm/README.md @@ -0,0 +1,23 @@ + +# Partition service integration tests + +Partition integration tests are refactored so that the business logic for integration tests resides in the `partition-test-core` module and provider specific logic and execution steps reside in provider module (e.g. `partition-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 +* Values for the following environment variables in Config.java + + ``` + ENVIRONMENT ('local' for local testing or 'dev' for dev testing) + PARTITION_BASE_URL(service base URL ) + CLIENT_TENANT (common tenant) + MY_TENANT (OSDU Tenant) + OAUTH2 credentials (User, Password etc) + ``` + + 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 `partition-test-core` directory and run `mvn clean install`. This will build the core module +* Next, to execute the integration tests, navigate to the provider module and execute `mvn test` diff --git a/testing/partition-test-ibm/pom.xml b/testing/partition-test-ibm/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..7979e3fc61177ae109cdc43d403d3907c2f997e9 --- /dev/null +++ b/testing/partition-test-ibm/pom.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-partition-testing</artifactId> + <version>0.0.5-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <groupId>org.opengroup.osdu.partition</groupId> + <artifactId>partition-test-ibm</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>jar</packaging> + + <properties> + <java.version>1.8</java.version> + <maven.compiler.target>1.8</maven.compiler.target> + <maven.compiler.source>1.8</maven.compiler.source> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <os-core-lib-ibm.version>0.3.8-SNAPSHOT</os-core-lib-ibm.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.opengroup.osdu</groupId> + <artifactId>os-core-lib-ibm</artifactId> + <version>${os-core-lib-ibm.version}</version> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>1.18.8</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.19.4</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + </dependency> + + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.8.5</version> + </dependency> + + <dependency> + <groupId>org.opengroup.osdu.partition</groupId> + <artifactId>partition-test-core</artifactId> + <version>0.0.1-SNAPSHOT</version> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>27.1-jre</version> + </dependency> + + </dependencies> +</project> diff --git a/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestCreatePartition.java b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestCreatePartition.java new file mode 100644 index 0000000000000000000000000000000000000000..b21b39328cf43289208458d2126a119d709b36d2 --- /dev/null +++ b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestCreatePartition.java @@ -0,0 +1,25 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.api; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.partition.util.IBMTestUtils; + +public class TestCreatePartition extends CreatePartitionTest { + + @Before + @Override + public void setup() { + this.testUtils = new IBMTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + +} diff --git a/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestDeletePartition.java b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestDeletePartition.java new file mode 100644 index 0000000000000000000000000000000000000000..9c2d7cc0da04ba0d4165b88fa8ea5f358d1a315f --- /dev/null +++ b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestDeletePartition.java @@ -0,0 +1,25 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.api; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.partition.util.IBMTestUtils; + +public class TestDeletePartition extends DeletePartitionTest { + + @Before + @Override + public void setup() { + this.testUtils = new IBMTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + +} diff --git a/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestGetPartitionById.java b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestGetPartitionById.java new file mode 100644 index 0000000000000000000000000000000000000000..0c5589183c737841a8713765527cee1ea0d155be --- /dev/null +++ b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestGetPartitionById.java @@ -0,0 +1,25 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.api; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.partition.util.IBMTestUtils; + +public class TestGetPartitionById extends GetPartitionByIdApitTest { + + @Before + @Override + public void setup() { + this.testUtils = new IBMTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + +} diff --git a/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestListPartitions.java b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestListPartitions.java new file mode 100644 index 0000000000000000000000000000000000000000..6a1e5e66a5226d0eb4360078ef7b56528ce4281f --- /dev/null +++ b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestListPartitions.java @@ -0,0 +1,25 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.api; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.partition.util.IBMTestUtils; + +public class TestListPartitions extends ListPartitionsApitTest { + + @Before + @Override + public void setup() { + this.testUtils = new IBMTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + +} diff --git a/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestUpdatePartition.java b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestUpdatePartition.java new file mode 100644 index 0000000000000000000000000000000000000000..564659dc2bd3d662bac5182a358a1442d21f8ff1 --- /dev/null +++ b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/api/TestUpdatePartition.java @@ -0,0 +1,25 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.api; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opengroup.osdu.partition.util.IBMTestUtils; + +public class TestUpdatePartition extends UpdatePartitionTest { + + @Before + @Override + public void setup() { + this.testUtils = new IBMTestUtils(); + } + + @After + @Override + public void tearDown() { + this.testUtils = null; + } + +} diff --git a/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/util/IBMTestUtils.java b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/util/IBMTestUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..0f29eca9b918f4f32ba60624139e3fc979f8ed7d --- /dev/null +++ b/testing/partition-test-ibm/src/test/java/org/opengroup/osdu/partition/util/IBMTestUtils.java @@ -0,0 +1,28 @@ +/* Licensed Materials - Property of IBM */ +/* (c) Copyright IBM Corp. 2020. All Rights Reserved.*/ + +package org.opengroup.osdu.partition.util; + +import org.opengroup.osdu.core.ibm.util.IdentityClient; + +import com.google.common.base.Strings; + +public class IBMTestUtils extends TestUtils { + + @Override + public String getAccessToken() throws Exception { + if(token == null || token.isEmpty()) { + token=IdentityClient.getTokenForUserWithAccess(); + } + return "Bearer " + token; + } + + @Override + public String getNoAccessToken() throws Exception { + if(noAccessToken == null || noAccessToken.isEmpty()) { + noAccessToken=IdentityClient.getTokenForUserWithNoAccess(); + } + return "Bearer " + noAccessToken; + } + +} diff --git a/testing/pom.xml b/testing/pom.xml index 21f1a5c40fabd23e23c807be8e659af29ed87173..a0600508d8f528ac6c956cd2b6fe2d6d0c1271a7 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -35,6 +35,7 @@ <module>partition-test-core</module> <module>partition-test-azure</module> <module>partition-test-aws</module> + <module>partition-test-ibm</module> </modules> <repositories>