diff --git a/pom.xml b/pom.xml index 0fb194209daed38bad3e324e09b36c36ddb7fb71..e495d6d5965f7d506a5fc16480a0337b0f7fc0f2 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ <maven.compiler.source>17</maven.compiler.source> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <openapi.version>2.6.0</openapi.version> - <os-core-common.version>3.0.0</os-core-common.version> + <os-core-common.version>3.1.1</os-core-common.version> <gson.version>2.8.9</gson.version> <json-smart.version>2.5.0</json-smart.version> <netty.version>4.1.97.Final</netty.version> diff --git a/provider/register-gc/docs/anthos/README.md b/provider/register-gc/docs/anthos/README.md index 9616f80232511173bb57a6823a4b24727fbf4cb7..10c57cb30a09ab638cb8939c21edc7eb7c24b3ba 100644 --- a/provider/register-gc/docs/anthos/README.md +++ b/provider/register-gc/docs/anthos/README.md @@ -18,16 +18,17 @@ Must have: Defined in default application property file but possible to override: -| name | value | description | sensitive? | source | -|-----------------------------|------------------------------------------|--------------------------------------------------------------------------------|------------|-------------------------------------| -| `LOG_PREFIX` | `service` | Logging prefix | no | - | -| `SERVER_SERVLET_CONTEXPATH` | `/api/register/v1` | Register context path | no | - | -| `ENTITLEMENTS_API` | ex `http://entitlements/entitlements/v1` | Entitlements API endpoint | no | output of infrastructure deployment | -| `STORAGE_API` | ex `http://storage/api/storage/v2` | Storage API endpoint | no | output of infrastructure deployment | -| `SUBSCRIBER_SECRET` | ex`7a786376626e` | HMAC_SECRET from notification int tests in HEX , pattern(^[a-zA-Z0-9]{8,30}+$) | yes | output of infrastructure deployment | -| `ENVIRONMENT` | ex `dev` | Service environment config | no | - | -| `PARTITION_API` | ex `http://partition/api/partition/v1` | Partition service API endpoint | no | output of infrastructure deployment | -| `SECRET_API` | ex `http://secret/api/secret/v1` | Secret service API endpoint | no | output of infrastructure deployment | +| name | value | description | sensitive? | source | +|-----------------------------|------------------------------------------|--------------------------------------------------------------------------------------------|------------|-------------------------------------| +| `LOG_PREFIX` | `service` | Logging prefix | no | - | +| `SERVER_SERVLET_CONTEXPATH` | `/api/register/v1` | Register context path | no | - | +| `ENTITLEMENTS_HOST` | ex `http://entitlements/entitlements/v1` | Entitlements API domain | no | output of infrastructure deployment | +| `STORAGE_HOST` | ex `http://storage/api/storage/v2` | Storage API domain | no | output of infrastructure deployment | +| `SUBSCRIBER_SECRET` | ex`7a786376626e` | HMAC_SECRET from notification int tests in HEX , pattern(^[a-zA-Z0-9]{8,30}+$) | yes | output of infrastructure deployment | +| `ENVIRONMENT` | ex `dev` | Service environment config | no | - | +| `PARTITION_HOST` | ex `http://partition/api/partition/v1` | Partition service API domain | no | output of infrastructure deployment | +| `SECRET_HOST` | ex `http://secret` | Secret service API domain | no | output of infrastructure deployment | +| `GROUP_ID` | ex `group` | The id of the groups is created. The default (and recommended for `jdbc`) value is `group` | no | - | These variables define service behavior, and are used to switch between `Reference` or `Google Cloud` environments, their overriding and usage in mixed mode was not tested. Usage of spring profiles is preferred. diff --git a/provider/register-gc/docs/gc/README.md b/provider/register-gc/docs/gc/README.md index 0263bf6d3dcc6c0eb1e9658dabd66cc809f76d54..8dfc93841626544558e8dd8a4735da2a05c13542 100644 --- a/provider/register-gc/docs/gc/README.md +++ b/provider/register-gc/docs/gc/README.md @@ -15,19 +15,20 @@ Must have: Defined in default application property file but possible to override: -| name | value | description | sensitive? | source | -|----------------------------------|------------------------------------------|--------------------------------------------------------------------------------|------------|-----------------------------------------------------| -| `LOG_PREFIX` | `service` | Logging prefix | no | - | -| `SERVER_SERVLET_CONTEXPATH` | `/api/register/v1` | Register context path | no | - | -| `ENTITLEMENTS_API` | ex `http://entitlements/entitlements/v1` | Entitlements API endpoint | no | output of infrastructure deployment | -| `STORAGE_API` | ex `http://storage/api/storage/v2` | Storage API endpoint | no | output of infrastructure deployment | -| `SUBSCRIBER_SECRET` | ex`7a786376626e` | HMAC_SECRET from notification int tests in HEX , pattern(^[a-zA-Z0-9]{8,30}+$) | yes | output of infrastructure deployment | -| `ENVIRONMENT` | ex `dev` | Service environment config | no | - | -| `PARTITION_API` | ex `http://partition/api/partition/v1` | Partition service endpoint | no | output of infrastructure deployment | -| `SERVICE_IDENTITY` | ex `osdu-gcp-sa` | Service account identity "**osdu-gcp-sa**@iam.gserviceaccount.com" | yes | <https://console.cloud.google.com/apis/credentials> | -| `SECRET_API` | ex `http://secret/api/secret/v1` | Secret service API endpoint | no | output of infrastructure deployment | -| `MANAGEMENT_ENDPOINTS_WEB_BASE` | ex `/` | Web base for Actuator | no | - | -| `MANAGEMENT_SERVER_PORT` | ex `8081` | Port for Actuator | no | - | +| name | value | description | sensitive? | source | +|---------------------------------|--------------------------|--------------------------------------------------------------------------------------------|------------|-----------------------------------------------------| +| `LOG_PREFIX` | `service` | Logging prefix | no | - | +| `SERVER_SERVLET_CONTEXPATH` | `/api/register/v1` | Register context path | no | - | +| `ENTITLEMENTS_HOST` | ex `http://entitlements` | Entitlements API domain | no | output of infrastructure deployment | +| `STORAGE_HOST` | ex `http://storage` | Storage API domain | no | output of infrastructure deployment | +| `SUBSCRIBER_SECRET` | ex`7a786376626e` | HMAC_SECRET from notification int tests in HEX , pattern(^[a-zA-Z0-9]{8,30}+$) | yes | output of infrastructure deployment | +| `ENVIRONMENT` | ex `dev` | Service environment config | no | - | +| `PARTITION_HOST` | ex `http://partition` | Partition service domain | no | output of infrastructure deployment | +| `SERVICE_IDENTITY` | ex `osdu-gcp-sa` | Service account identity "**osdu-gcp-sa**@iam.gserviceaccount.com" | yes | <https://console.cloud.google.com/apis/credentials> | +| `SECRET_HOST` | ex `http://secret` | Secret service domain | no | output of infrastructure deployment | +| `MANAGEMENT_ENDPOINTS_WEB_BASE` | ex `/` | Web base for Actuator | no | - | +| `MANAGEMENT_SERVER_PORT` | ex `8081` | Port for Actuator | no | - | +| `GROUP_ID` | ex `group` | The id of the groups is created. The default (and recommended for `jdbc`) value is `group` | no | - | These variables define service behavior, and are used to switch between `Reference` or `Google Cloud` environments, their overriding and usage in mixed mode was not tested. Usage of spring profiles is preferred. diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/config/RegisterConfigurationProperties.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/config/RegisterConfigurationProperties.java index 6afc52522ffc126f92bf08f19f8d05b1fb7db455..d356fe94fd4381e4d4f2aeba713f7a9efc06faaa 100644 --- a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/config/RegisterConfigurationProperties.java +++ b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/config/RegisterConfigurationProperties.java @@ -26,4 +26,5 @@ import org.springframework.context.annotation.Configuration; @ConfigurationProperties public class RegisterConfigurationProperties { private String secretApi; + private String domain; } diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/constant/RegisterConstants.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/constant/RegisterConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..4228c602c98143d9368d05c2c142e1b0f314af15 --- /dev/null +++ b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/constant/RegisterConstants.java @@ -0,0 +1,26 @@ +/* + Copyright 2020-2024 Google LLC + Copyright 2020-2024 EPAM Systems, Inc + + 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.gcp.constant; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public final class RegisterConstants { + public static final String REGISTER_SECRET_OWNERS_GROUP = "register.secret.owners"; + public static final String REGISTER_SECRET_VIEWERS_GROUP = "register.secret.viewers"; +} diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/gson/OffsetDateTimeAdapter.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/gson/OffsetDateTimeAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..354d680afeaf4f21744a0b3006c9f1093c55edb3 --- /dev/null +++ b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/gson/OffsetDateTimeAdapter.java @@ -0,0 +1,36 @@ +/* + Copyright 2020-2024 Google LLC + Copyright 2020-2024 EPAM Systems, Inc + + 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.gcp.gson; + +import com.google.gson.*; + +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; + +public class OffsetDateTimeAdapter implements JsonSerializer<OffsetDateTime>, JsonDeserializer<OffsetDateTime> { + + @Override + public JsonElement serialize(OffsetDateTime src, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + } + + @Override + public OffsetDateTime deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return OffsetDateTime.parse(json.getAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } +} \ No newline at end of file diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/model/SecretModel.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/model/SecretModel.java deleted file mode 100644 index e5cb1a8206176aaec982df87c5ba1558aa0b2991..0000000000000000000000000000000000000000 --- a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/model/SecretModel.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2020-2024 Google LLC - Copyright 2020-2024 EPAM Systems, Inc - - 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.gcp.model; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.google.gson.annotations.SerializedName; -import io.swagger.v3.oas.annotations.media.Schema; -import java.time.OffsetDateTime; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -//Todo: move it to core-common -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class SecretModel { - @Schema(type = "string") - private String id; - - @Schema(type = "string") - private String key; - - @Schema(type = "string") - private String value; - - @Schema(type = "string", format = "date-time") - private OffsetDateTime createdAt; - - @Schema(type = "boolean") - @SerializedName("enabled") - private boolean isEnabled; -} diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClient.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClient.java index 4f12e05cf4fc65138b537d9adcceab2559ef4e2c..f88799a131ffe0f28808c9cfed841aae99f05657 100644 --- a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClient.java +++ b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClient.java @@ -17,9 +17,8 @@ package org.opengroup.osdu.register.provider.gcp.secret; -import org.opengroup.osdu.register.provider.gcp.model.SecretModel; +import org.opengroup.osdu.core.common.model.secret.SecretModel; -//Todo: move it to core-common public interface SecretClient { SecretModel retrieveSecret(String secretKey); diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientImpl.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientImpl.java index b79798c652c29ead63fb3802abb8a2671f04b112..5478cbcb9763d5979b3bf510c7a98e031c901936 100644 --- a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientImpl.java +++ b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientImpl.java @@ -20,6 +20,7 @@ package org.opengroup.osdu.register.provider.gcp.secret; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.net.URI; +import java.time.OffsetDateTime; import java.util.HashMap; import java.util.Map; import lombok.RequiredArgsConstructor; @@ -28,16 +29,19 @@ import org.apache.http.HttpStatus; import org.opengroup.osdu.core.common.http.HttpRequest; import org.opengroup.osdu.core.common.http.HttpResponse; import org.opengroup.osdu.core.common.http.IHttpClient; +import org.opengroup.osdu.core.common.model.entitlements.Acl; import org.opengroup.osdu.core.common.model.http.AppException; import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.core.common.model.secret.SecretModel; import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient; import org.opengroup.osdu.register.provider.gcp.config.RegisterConfigurationProperties; -import org.opengroup.osdu.register.provider.gcp.model.SecretModel; +import org.opengroup.osdu.register.provider.gcp.constant.RegisterConstants; +import org.opengroup.osdu.register.provider.gcp.gson.OffsetDateTimeAdapter; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; -//Todo: move it to core-common +// Todo: move it to core-common @Slf4j @Service @RequiredArgsConstructor @@ -51,7 +55,10 @@ public class SecretClientImpl implements SecretClient { private final DpsHeaders dpsHeaders; private final IServiceAccountJwtClient serviceAccountJwtClient; - private final Gson gson = new GsonBuilder().create(); + private final Gson gson = + new GsonBuilder() + .registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter()) + .create(); @Override public SecretModel retrieveSecret(String secretKey) { @@ -73,10 +80,10 @@ public class SecretClientImpl implements SecretClient { String url = createUrl(); SecretModel secret = SecretModel.builder() - .key(secretKey) .id(secretKey) .value(secretValue) .isEnabled(true) + .secretAcls(buildAcl()) .build(); Map<String, String> headers = buildHttpHeaders(); HttpRequest request = HttpRequest.post(secret).url(url).headers(headers).build(); @@ -88,15 +95,30 @@ public class SecretClientImpl implements SecretClient { return getSecretModel(response); } + private Acl buildAcl() { + String partitionId = dpsHeaders.getPartitionId(); + String domain = configurationProperties.getDomain(); + + String ownersAcl = + String.format( + "%s@%s.%s", RegisterConstants.REGISTER_SECRET_OWNERS_GROUP, partitionId, domain); + + String viewersAcl = + String.format( + "%s@%s.%s", RegisterConstants.REGISTER_SECRET_VIEWERS_GROUP, partitionId, domain); + + return new Acl(new String[] {viewersAcl}, new String[] {ownersAcl}); + } + @Override public SecretModel updateSecret(String secretKey, String secretValue) { String url = createUrl(secretKey); Map<String, String> headers = buildHttpHeaders(); SecretModel secret = SecretModel.builder() - .key(secretKey) .id(secretKey) .value(secretValue) + .secretAcls(buildAcl()) .isEnabled(true) .build(); HttpRequest request = HttpRequest.patch(secret).url(url).headers(headers).build(); diff --git a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/subscriber/OsmAccess.java b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/subscriber/OsmAccess.java index e93ec65c2f03832e1984720a8395562d58a96001..e5b41e764643fa0bbace3b9734cbbd585bfe4bb2 100644 --- a/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/subscriber/OsmAccess.java +++ b/provider/register-gc/src/main/java/org/opengroup/osdu/register/provider/gcp/subscriber/OsmAccess.java @@ -22,7 +22,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_SING import com.google.common.base.Strings; import jakarta.annotation.PostConstruct; - import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import java.util.List; @@ -32,6 +31,7 @@ import java.util.concurrent.locks.ReentrantLock; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.opengroup.osdu.core.common.model.http.AppException; +import org.opengroup.osdu.core.common.model.secret.SecretModel; import org.opengroup.osdu.core.common.model.tenant.TenantInfo; import org.opengroup.osdu.core.gcp.oqm.driver.OqmDriverRuntimeException; import org.opengroup.osdu.core.gcp.oqm.model.OqmSubscription; @@ -43,7 +43,6 @@ import org.opengroup.osdu.core.gcp.osm.model.query.GetQuery; import org.opengroup.osdu.core.gcp.osm.service.Context; import org.opengroup.osdu.core.gcp.osm.service.Transaction; import org.opengroup.osdu.core.gcp.osm.translate.ExceptionClassifier; -import org.opengroup.osdu.register.provider.gcp.model.SecretModel; import org.opengroup.osdu.register.provider.gcp.secret.SecretClient; import org.opengroup.osdu.register.provider.gcp.subscriber.model.SubscriptionDto; import org.opengroup.osdu.register.subscriber.model.*; @@ -60,234 +59,253 @@ import org.springframework.stereotype.Repository; @Slf4j public class OsmAccess implements IDatastoreAccess { - private static final String ITEM_MISSING_ERROR_TEMPLATE = "Subscription item skipped: {}"; + private static final String ITEM_MISSING_ERROR_TEMPLATE = "Subscription item skipped: {}"; - private final Context context; - private final TenantInfo tenantInfo; - private final SecretClient secretClient; - private final SubscriptionEventPublisher publisher; + private final Context context; + private final TenantInfo tenantInfo; + private final SecretClient secretClient; + private final SubscriptionEventPublisher publisher; - ReentrantLock subscriptionDeleteLock = new ReentrantLock(); + ReentrantLock subscriptionDeleteLock = new ReentrantLock(); - private static final String SERVER_ERROR_REASON = "Server Error"; - private static final String CONFLICT_REASON = "Conflict"; - private static final String ABORTED_DUE_TO_CONFLICT_MSG = "ABORTED due to conflict"; + private static final String SERVER_ERROR_REASON = "Server Error"; + private static final String CONFLICT_REASON = "Conflict"; + private static final String ABORTED_DUE_TO_CONFLICT_MSG = "ABORTED due to conflict"; - private static final Kind KIND = new Kind("SUBSCRIPTION"); - private static final String SUBSCRIPTION_CREATED = "Subscription Created"; - private static final String SUBSCRIPTION_UPDATED = "Subscription Updated"; - private static final String SUBSCRIPTION_DELETED = "Subscription Deleted"; + private static final Kind KIND = new Kind("SUBSCRIPTION"); + private static final String SUBSCRIPTION_CREATED = "Subscription Created"; + private static final String SUBSCRIPTION_UPDATED = "Subscription Updated"; + private static final String SUBSCRIPTION_DELETED = "Subscription Deleted"; - private Destination getDestination() { - return Destination.builder() - .partitionId(tenantInfo.getDataPartitionId()) - .namespace(new Namespace(tenantInfo.getName())) - .kind(KIND) - .build(); - } + private Destination getDestination() { + return Destination.builder() + .partitionId(tenantInfo.getDataPartitionId()) + .namespace(new Namespace(tenantInfo.getName())) + .kind(KIND) + .build(); + } - @PostConstruct - void postConstruct() { - log.debug("OsmAccess bean constructed."); - } + @PostConstruct + void postConstruct() { + log.debug("OsmAccess bean constructed."); + } - @Override - public Subscription create(Subscription subscriptionRequest) { - return createSubscriptionInStorage(getDtoBySubscription(subscriptionRequest)); - } + @Override + public Subscription create(Subscription subscriptionRequest) { + return createSubscriptionInStorage(getDtoBySubscription(subscriptionRequest)); + } - @Override - public Subscription get(String id) { + @Override + public Subscription get(String id) { - GetQuery<SubscriptionDto> q = new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", id)); - SubscriptionDto existingSubscription = context.getOne(q); - if (existingSubscription == null) - throw new AppException(404, "Not found", String.format("Subscriber with id %s does not exist.", id)); + GetQuery<SubscriptionDto> q = + new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", id)); + SubscriptionDto existingSubscription = context.getOne(q); + if (existingSubscription == null) + throw new AppException( + 404, "Not found", String.format("Subscriber with id %s does not exist.", id)); - return getSubscriptionByDto(existingSubscription); - } + return getSubscriptionByDto(existingSubscription); + } - @Override - public List<Subscription> query(String notificationId) { - GetQuery<SubscriptionDto> q = new GetQuery<>( - SubscriptionDto.class, - getDestination(), - eq("notificationId", notificationId)); - return context.getResultsAsList(q).stream() - .map(this::getSubscriptionByDto) - .toList(); - } + @Override + public List<Subscription> query(String notificationId) { + GetQuery<SubscriptionDto> q = + new GetQuery<>( + SubscriptionDto.class, getDestination(), eq("notificationId", notificationId)); + return context.getResultsAsList(q).stream().map(this::getSubscriptionByDto).toList(); + } - @Override - public boolean delete(String id) { - return deleteFromStorage(id); - } + @Override + public boolean delete(String id) { + return deleteFromStorage(id); + } - @Override - public boolean patch(String id, Secret secret) { - return updateSubscriptionInStorage(id, secret); - } + @Override + public boolean patch(String id, Secret secret) { + return updateSubscriptionInStorage(id, secret); + } - @Override - public List<Subscription> getAll() { - return context.getResultsAsList(new GetQuery<>(SubscriptionDto.class, getDestination())) - .stream() - .map(item -> { - try { - return getSubscriptionByDto(item); - } catch (OqmDriverRuntimeException e) { - log.error(ITEM_MISSING_ERROR_TEMPLATE, e.getMessage()); - return null; - } + @Override + public List<Subscription> getAll() { + return context + .getResultsAsList(new GetQuery<>(SubscriptionDto.class, getDestination())) + .stream() + .map( + item -> { + try { + return getSubscriptionByDto(item); + } catch (OqmDriverRuntimeException e) { + log.error(ITEM_MISSING_ERROR_TEMPLATE, e.getMessage()); + return null; + } }) - .filter(Objects::nonNull) - .toList(); - } + .filter(Objects::nonNull) + .toList(); + } - private Subscription createSubscriptionInStorage(SubscriptionDto subscriptionRequest) { - - Transaction txn = context.beginTransaction(getDestination()); - try { - GetQuery<SubscriptionDto> q = new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", subscriptionRequest.getId())); - if (context.getOne(q) != null) - throw new AppException(409, CONFLICT_REASON, "A subscriber already exists with the same topic and endpoint combination"); - - subscriptionRequest.setCreatedOnEpoch(new Timestamp(System.currentTimeMillis())); - secretClient.createSecret(generateUUID(subscriptionRequest.getId()).toString(), subscriptionRequest.getSecretValue()); - Subscription createdSubscription = getSubscriptionByDto(context.createAndGet(subscriptionRequest, getDestination())); - publisher.sendSubscriptionLifecycleEvent( - SUBSCRIPTION_CREATED, - tenantInfo, - OqmSubscription.builder() - .name(createdSubscription.getNotificationId()) - .topic(OqmTopic.builder().name(createdSubscription.getTopic()).build()) - .build()); - txn.commitIfActive(); - return createdSubscription; - } catch (AppException e) { - throw e; - } catch (Exception e) { - ExceptionClassifier.ExceptionClassification classification = context.classifyException(e); - switch (classification) { - case ALREADY_EXISTS: - throw new AppException(409, CONFLICT_REASON, "A subscriber already exists with the same topic and endpoint combination"); - case CONFLICT: - throw new AppException(409, CONFLICT_REASON, ABORTED_DUE_TO_CONFLICT_MSG); - default: - throw new AppException(500, SERVER_ERROR_REASON, "Unexpected error creating subscription.", e); - } - } finally { - txn.rollbackIfActive(); - } + private Subscription createSubscriptionInStorage(SubscriptionDto subscriptionRequest) { + + Transaction txn = context.beginTransaction(getDestination()); + try { + GetQuery<SubscriptionDto> q = + new GetQuery<>( + SubscriptionDto.class, getDestination(), eq("id", subscriptionRequest.getId())); + if (context.getOne(q) != null) + throw new AppException( + 409, + CONFLICT_REASON, + "A subscriber already exists with the same topic and endpoint combination"); + + subscriptionRequest.setCreatedOnEpoch(new Timestamp(System.currentTimeMillis())); + secretClient.createSecret( + generateID(subscriptionRequest.getId()), subscriptionRequest.getSecretValue()); + Subscription createdSubscription = + getSubscriptionByDto(context.createAndGet(subscriptionRequest, getDestination())); + publisher.sendSubscriptionLifecycleEvent( + SUBSCRIPTION_CREATED, + tenantInfo, + OqmSubscription.builder() + .name(createdSubscription.getNotificationId()) + .topic(OqmTopic.builder().name(createdSubscription.getTopic()).build()) + .build()); + txn.commitIfActive(); + return createdSubscription; + } catch (AppException e) { + throw e; + } catch (Exception e) { + ExceptionClassifier.ExceptionClassification classification = context.classifyException(e); + switch (classification) { + case ALREADY_EXISTS: + throw new AppException( + 409, + CONFLICT_REASON, + "A subscriber already exists with the same topic and endpoint combination"); + case CONFLICT: + throw new AppException(409, CONFLICT_REASON, ABORTED_DUE_TO_CONFLICT_MSG); + default: + throw new AppException( + 500, SERVER_ERROR_REASON, "Unexpected error creating subscription.", e); + } + } finally { + txn.rollbackIfActive(); } + } - private boolean deleteFromStorage(String id) { - boolean output = false; - - Transaction txn = context.beginTransaction(getDestination()); - try { - subscriptionDeleteLock.lock(); - GetQuery<SubscriptionDto> q = new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", id)); - SubscriptionDto existingSubscription = context.getOne(q); - if (existingSubscription != null) { - secretClient.removeSecret(generateUUID(id).toString()); - context.deleteById(SubscriptionDto.class, getDestination(), id); - publisher.sendSubscriptionLifecycleEvent( - SUBSCRIPTION_DELETED, - tenantInfo, - OqmSubscription.builder() - .name(existingSubscription.getNotificationId()) - .topic(OqmTopic.builder().name(existingSubscription.getTopic()).build()) - .build()); - txn.commitIfActive(); - output = true; - } - } catch (Exception e) { - ExceptionClassifier.ExceptionClassification classification = context.classifyException(e); - switch (classification) { - case ALREADY_EXISTS: - throw new AppException(409, CONFLICT_REASON, "Another request is trying to delete the same DDMS."); - case CONFLICT: - throw new AppException(409, CONFLICT_REASON, ABORTED_DUE_TO_CONFLICT_MSG); - default: - throw new AppException(500, SERVER_ERROR_REASON, "Unexpected error deleting subscription.", e); - } - } finally { - subscriptionDeleteLock.unlock(); - txn.rollbackIfActive(); - } - return output; + private boolean deleteFromStorage(String id) { + boolean output = false; + + Transaction txn = context.beginTransaction(getDestination()); + try { + subscriptionDeleteLock.lock(); + GetQuery<SubscriptionDto> q = + new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", id)); + SubscriptionDto existingSubscription = context.getOne(q); + if (existingSubscription != null) { + secretClient.removeSecret(generateID(id)); + context.deleteById(SubscriptionDto.class, getDestination(), id); + publisher.sendSubscriptionLifecycleEvent( + SUBSCRIPTION_DELETED, + tenantInfo, + OqmSubscription.builder() + .name(existingSubscription.getNotificationId()) + .topic(OqmTopic.builder().name(existingSubscription.getTopic()).build()) + .build()); + txn.commitIfActive(); + output = true; + } + } catch (Exception e) { + ExceptionClassifier.ExceptionClassification classification = context.classifyException(e); + switch (classification) { + case ALREADY_EXISTS: + throw new AppException( + 409, CONFLICT_REASON, "Another request is trying to delete the same DDMS."); + case CONFLICT: + throw new AppException(409, CONFLICT_REASON, ABORTED_DUE_TO_CONFLICT_MSG); + default: + throw new AppException( + 500, SERVER_ERROR_REASON, "Unexpected error deleting subscription.", e); + } + } finally { + subscriptionDeleteLock.unlock(); + txn.rollbackIfActive(); } + return output; + } - private boolean updateSubscriptionInStorage(String id, Secret secret) { - boolean output = false; - - Transaction txn = context.beginTransaction(getDestination()); - try { - GetQuery<SubscriptionDto> q = new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", id)); - SubscriptionDto existingSubscription = context.getOne(q); - if (existingSubscription != null) { - Subscription updatedSubscriptionRequest = getSubscriptionByDto(existingSubscription); - updatedSubscriptionRequest.setSecret(secret); - secretClient.updateSecret(generateUUID(updatedSubscriptionRequest.getId()).toString(), secret.toString()); - context.upsert(getDtoBySubscription(updatedSubscriptionRequest), getDestination()); - publisher.sendSubscriptionLifecycleEvent( - SUBSCRIPTION_UPDATED, - tenantInfo, - OqmSubscription.builder() - .name(updatedSubscriptionRequest.getNotificationId()) - .topic(OqmTopic.builder().name(updatedSubscriptionRequest.getTopic()).build()) - .build()); - txn.commitIfActive(); - output = true; - } - } catch (Exception e) { - ExceptionClassifier.ExceptionClassification classification = context.classifyException(e); - switch (classification) { - case ALREADY_EXISTS: - throw new AppException(409, CONFLICT_REASON, "Another request is trying to update the same DDMS."); - case CONFLICT: - throw new AppException(409, CONFLICT_REASON, ABORTED_DUE_TO_CONFLICT_MSG); - default: - throw new AppException(500, SERVER_ERROR_REASON, "Unexpected error updating subscription.", e); - } - } finally { - txn.rollbackIfActive(); - } - return output; + private boolean updateSubscriptionInStorage(String id, Secret secret) { + boolean output = false; + + Transaction txn = context.beginTransaction(getDestination()); + try { + GetQuery<SubscriptionDto> q = + new GetQuery<>(SubscriptionDto.class, getDestination(), eq("id", id)); + SubscriptionDto existingSubscription = context.getOne(q); + if (existingSubscription != null) { + Subscription updatedSubscriptionRequest = getSubscriptionByDto(existingSubscription); + updatedSubscriptionRequest.setSecret(secret); + secretClient.updateSecret( + generateID(updatedSubscriptionRequest.getId()), secret.toString()); + context.upsert(getDtoBySubscription(updatedSubscriptionRequest), getDestination()); + publisher.sendSubscriptionLifecycleEvent( + SUBSCRIPTION_UPDATED, + tenantInfo, + OqmSubscription.builder() + .name(updatedSubscriptionRequest.getNotificationId()) + .topic(OqmTopic.builder().name(updatedSubscriptionRequest.getTopic()).build()) + .build()); + txn.commitIfActive(); + output = true; + } + } catch (Exception e) { + ExceptionClassifier.ExceptionClassification classification = context.classifyException(e); + switch (classification) { + case ALREADY_EXISTS: + throw new AppException( + 409, CONFLICT_REASON, "Another request is trying to update the same DDMS."); + case CONFLICT: + throw new AppException(409, CONFLICT_REASON, ABORTED_DUE_TO_CONFLICT_MSG); + default: + throw new AppException( + 500, SERVER_ERROR_REASON, "Unexpected error updating subscription.", e); + } + } finally { + txn.rollbackIfActive(); } + return output; + } - private SubscriptionDto getDtoBySubscription(Subscription subscription) { - SubscriptionDto subscriptionDto = new SubscriptionDto(); - BeanUtils.copyProperties(subscription, subscriptionDto); - subscriptionDto.setSecretValue(subscription.getSecret().toString()); - subscriptionDto.setSecretType(subscription.getSecret().getSecretType()); - return subscriptionDto; - } + private SubscriptionDto getDtoBySubscription(Subscription subscription) { + SubscriptionDto subscriptionDto = new SubscriptionDto(); + BeanUtils.copyProperties(subscription, subscriptionDto); + subscriptionDto.setSecretValue(subscription.getSecret().toString()); + subscriptionDto.setSecretType(subscription.getSecret().getSecretType()); + return subscriptionDto; + } - private Subscription getSubscriptionByDto(SubscriptionDto subscriptionDto) { - Subscription subscription = new Subscription(); - BeanUtils.copyProperties(subscriptionDto, subscription); - - if (!Strings.isNullOrEmpty(subscriptionDto.getSecretType())) { - - SecretModel secretModel = secretClient.retrieveSecret(generateUUID(subscriptionDto.getId()).toString()); - if (subscriptionDto.getSecretType().equals(Constants.GSASecret)) { - GsaSecret gsaSecret = new GsaSecret(); - String[] splitSecret = secretModel.getValue().split("`"); - gsaSecret.setValue(new GsaSecretValue(splitSecret[0], splitSecret[1])); - subscription.setSecret(gsaSecret); - } else { - HmacSecret hmacSecret = new HmacSecret(); - hmacSecret.setValue(secretModel.getValue()); - subscription.setSecret(hmacSecret); - } - } - return subscription; + private Subscription getSubscriptionByDto(SubscriptionDto subscriptionDto) { + Subscription subscription = new Subscription(); + BeanUtils.copyProperties(subscriptionDto, subscription); + + if (!Strings.isNullOrEmpty(subscriptionDto.getSecretType())) { + + SecretModel secretModel = secretClient.retrieveSecret(generateID(subscriptionDto.getId())); + if (subscriptionDto.getSecretType().equals(Constants.GSASecret)) { + GsaSecret gsaSecret = new GsaSecret(); + String[] splitSecret = secretModel.getValue().split("`"); + gsaSecret.setValue(new GsaSecretValue(splitSecret[0], splitSecret[1])); + subscription.setSecret(gsaSecret); + } else { + HmacSecret hmacSecret = new HmacSecret(); + hmacSecret.setValue(secretModel.getValue()); + subscription.setSecret(hmacSecret); + } } + return subscription; + } - private UUID generateUUID(String string) { + private String generateID(String string) { byte[] bytes = string.getBytes(StandardCharsets.UTF_8); - return UUID.nameUUIDFromBytes(bytes); + return UUID.nameUUIDFromBytes(bytes).toString().replace("-", ""); } } diff --git a/provider/register-gc/src/main/resources/application.properties b/provider/register-gc/src/main/resources/application.properties index 3479cee30ce28902f34012ab87317730114a3633..61dccf821986ddb1656d8f23018ebe7f1117716b 100644 --- a/provider/register-gc/src/main/resources/application.properties +++ b/provider/register-gc/src/main/resources/application.properties @@ -38,9 +38,11 @@ NOTIFICATION_RECORDS_CHANGED_PATH=/api/notification/v1/push-handlers/records-cha RECORDS_CHANGE_PUBSUB_ENDPOINT=${NOTIFICATION_HOST}${NOTIFICATION_RECORDS_CHANGED_PATH} SECRET_HOST=http://secret -SECRET_PATH=/api/secret/v1 +SECRET_PATH=/api/secret/v2 SECRET_API=${SECRET_HOST}${SECRET_PATH} +domain=${GROUP_ID:group} + propertyResolver.strategy=partition #Health checks diff --git a/provider/register-gc/src/test/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientTest.java b/provider/register-gc/src/test/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientTest.java index df66828e81ed42cdf1429369454d14993cf3f60d..a9a3a4898a1066e77c8cde117b011c7a7e326914 100644 --- a/provider/register-gc/src/test/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientTest.java +++ b/provider/register-gc/src/test/java/org/opengroup/osdu/register/provider/gcp/secret/SecretClientTest.java @@ -34,9 +34,9 @@ import org.opengroup.osdu.core.common.http.HttpResponse; import org.opengroup.osdu.core.common.http.IHttpClient; import org.opengroup.osdu.core.common.model.http.AppException; import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.opengroup.osdu.core.common.model.secret.SecretModel; import org.opengroup.osdu.core.common.util.IServiceAccountJwtClient; import org.opengroup.osdu.register.provider.gcp.config.RegisterConfigurationProperties; -import org.opengroup.osdu.register.provider.gcp.model.SecretModel; @RunWith(MockitoJUnitRunner.class) public class SecretClientTest { @@ -69,7 +69,7 @@ public class SecretClientTest { public void createSecretTest() { SecretModel expectedResult = SecretModel.builder() - .key(SECRET_KEY_1_VAL) + .id(SECRET_KEY_1_VAL) .value(SECRET_VALUE_1_VAL) .isEnabled(true) .build(); @@ -98,7 +98,7 @@ public class SecretClientTest { public void retrieveSecretTest() { SecretModel expectedResult = SecretModel.builder() - .key(SECRET_KEY_1_VAL) + .id(SECRET_KEY_1_VAL) .value(SECRET_VALUE_1_VAL) .isEnabled(true) .build(); @@ -125,7 +125,7 @@ public class SecretClientTest { public void updateSecretTest() { SecretModel expectedResult = SecretModel.builder() - .key(SECRET_KEY_1_VAL) + .id(SECRET_KEY_1_VAL) .value(SECRET_VALUE_2_VAL) .isEnabled(true) .build();