From 063440fdfd80b5db2023bdeb5d281efb02dd20ad Mon Sep 17 00:00:00 2001 From: Rulin Xing Date: Mon, 16 Jun 2025 03:17:33 -0700 Subject: [PATCH 1/2] Add SigV4 related DPOs --- .../AuthenticationParametersDpo.java | 23 ++- .../core/connection/AuthenticationType.java | 3 +- .../BearerAuthenticationParametersDpo.java | 2 +- .../connection/ConnectionConfigInfoDpo.java | 33 ++++- .../OAuthClientCredentialsParametersDpo.java | 4 +- .../SigV4AuthenticationParametersDpo.java | 131 ++++++++++++++++++ .../hadoop/HadoopConnectionConfigInfoDpo.java | 18 ++- .../IcebergRestConnectionConfigInfoDpo.java | 19 ++- .../polaris/core/entity/CatalogEntity.java | 8 ++ .../core/identity/ServiceIdentityType.java | 83 +++++++++++ .../dpo/AwsIamServiceIdentityInfoDpo.java | 66 +++++++++ .../identity/dpo/ServiceIdentityInfoDpo.java | 83 +++++++++++ .../core/secrets/ServiceSecretReference.java | 53 +++++++ .../ConnectionConfigInfoDpoTest.java | 61 ++++++++ 14 files changed, 578 insertions(+), 9 deletions(-) create mode 100644 polaris-core/src/main/java/org/apache/polaris/core/connection/SigV4AuthenticationParametersDpo.java create mode 100644 polaris-core/src/main/java/org/apache/polaris/core/identity/ServiceIdentityType.java create mode 100644 polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java create mode 100644 polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java create mode 100644 polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java index 73523cadd1..20ddee0247 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java @@ -18,13 +18,16 @@ */ package org.apache.polaris.core.connection; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import jakarta.annotation.Nonnull; import java.util.Map; import org.apache.polaris.core.admin.model.AuthenticationParameters; import org.apache.polaris.core.admin.model.BearerAuthenticationParameters; import org.apache.polaris.core.admin.model.OAuthClientCredentialsParameters; +import org.apache.polaris.core.admin.model.SigV4AuthenticationParameters; import org.apache.polaris.core.connection.iceberg.IcebergCatalogPropertiesProvider; import org.apache.polaris.core.secrets.UserSecretReference; @@ -40,6 +43,7 @@ @JsonSubTypes.Type(value = OAuthClientCredentialsParametersDpo.class, name = "1"), @JsonSubTypes.Type(value = BearerAuthenticationParametersDpo.class, name = "2"), @JsonSubTypes.Type(value = ImplicitAuthenticationParametersDpo.class, name = "3"), + @JsonSubTypes.Type(value = SigV4AuthenticationParametersDpo.class, name = "4"), }) public abstract class AuthenticationParametersDpo implements IcebergCatalogPropertiesProvider { @@ -58,7 +62,12 @@ public int getAuthenticationTypeCode() { return authenticationTypeCode; } - public abstract AuthenticationParameters asAuthenticationParametersModel(); + @JsonIgnore + public AuthenticationType getAuthenticationType() { + return AuthenticationType.fromCode(authenticationTypeCode); + } + + public abstract @Nonnull AuthenticationParameters asAuthenticationParametersModel(); public static AuthenticationParametersDpo fromAuthenticationParametersModelWithSecrets( AuthenticationParameters authenticationParameters, @@ -85,6 +94,18 @@ public static AuthenticationParametersDpo fromAuthenticationParametersModelWithS case IMPLICIT: config = new ImplicitAuthenticationParametersDpo(); break; + case SIGV4: + // SigV4 authentication is not secret-based + SigV4AuthenticationParameters sigV4AuthenticationParametersModel = + (SigV4AuthenticationParameters) authenticationParameters; + config = + new SigV4AuthenticationParametersDpo( + sigV4AuthenticationParametersModel.getRoleArn(), + sigV4AuthenticationParametersModel.getRoleSessionName(), + sigV4AuthenticationParametersModel.getExternalId(), + sigV4AuthenticationParametersModel.getSigningRegion(), + sigV4AuthenticationParametersModel.getSigningName()); + break; default: throw new IllegalStateException( "Unsupported authentication type: " + authenticationParameters.getAuthenticationType()); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationType.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationType.java index 02e89c0597..334c4c1476 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationType.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationType.java @@ -34,6 +34,7 @@ public enum AuthenticationType { OAUTH(1), BEARER(2), IMPLICIT(3), + SIGV4(4), ; private static final AuthenticationType[] REVERSE_MAPPING_ARRAY; @@ -66,7 +67,7 @@ public enum AuthenticationType { * NULL_TYPE if not found * * @param authTypeCode code associated to the authentication type - * @return ConnectionType corresponding to that code or null if mapping not found + * @return AuthenticationType corresponding to that code or null if mapping not found */ public static @Nonnull AuthenticationType fromCode(int authTypeCode) { // ensure it is within bounds diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java index bf80c7c4cb..49e6953752 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java @@ -56,7 +56,7 @@ public BearerAuthenticationParametersDpo( } @Override - public AuthenticationParameters asAuthenticationParametersModel() { + public @Nonnull AuthenticationParameters asAuthenticationParametersModel() { return BearerAuthenticationParameters.builder() .setAuthenticationType(AuthenticationParameters.AuthenticationTypeEnum.BEARER) .build(); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java index fe4d183303..2fed570a3b 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java @@ -18,6 +18,7 @@ */ package org.apache.polaris.core.connection; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; @@ -36,6 +37,7 @@ import org.apache.polaris.core.connection.hadoop.HadoopConnectionConfigInfoDpo; import org.apache.polaris.core.connection.iceberg.IcebergCatalogPropertiesProvider; import org.apache.polaris.core.connection.iceberg.IcebergRestConnectionConfigInfoDpo; +import org.apache.polaris.core.identity.dpo.ServiceIdentityInfoDpo; import org.apache.polaris.core.secrets.UserSecretReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -64,22 +66,29 @@ public abstract class ConnectionConfigInfoDpo implements IcebergCatalogPropertie // The authentication parameters for the connection private final AuthenticationParametersDpo authenticationParameters; + // The Polaris service identity info of the connection + private final ServiceIdentityInfoDpo serviceIdentity; + public ConnectionConfigInfoDpo( @JsonProperty(value = "connectionTypeCode", required = true) int connectionTypeCode, @JsonProperty(value = "uri", required = true) @Nonnull String uri, @JsonProperty(value = "authenticationParameters", required = true) @Nonnull - AuthenticationParametersDpo authenticationParameters) { - this(connectionTypeCode, uri, authenticationParameters, true); + AuthenticationParametersDpo authenticationParameters, + @JsonProperty(value = "serviceIdentity", required = false) @Nonnull + ServiceIdentityInfoDpo serviceIdentity) { + this(connectionTypeCode, uri, authenticationParameters, serviceIdentity, true); } protected ConnectionConfigInfoDpo( int connectionTypeCode, @Nonnull String uri, @Nonnull AuthenticationParametersDpo authenticationParameters, + @Nonnull ServiceIdentityInfoDpo serviceIdentity, boolean validateUri) { this.connectionTypeCode = connectionTypeCode; this.uri = uri; this.authenticationParameters = authenticationParameters; + this.serviceIdentity = serviceIdentity; if (validateUri) { validateUri(uri); } @@ -89,6 +98,11 @@ public int getConnectionTypeCode() { return connectionTypeCode; } + @JsonIgnore + public ConnectionType getConnectionType() { + return ConnectionType.fromCode(connectionTypeCode); + } + public String getUri() { return uri; } @@ -97,6 +111,10 @@ public AuthenticationParametersDpo getAuthenticationParameters() { return authenticationParameters; } + public @Nonnull ServiceIdentityInfoDpo getServiceIdentity() { + return serviceIdentity; + } + private static final ObjectMapper DEFAULT_MAPPER; static { @@ -152,6 +170,7 @@ public static ConnectionConfigInfoDpo fromConnectionConfigInfoModelWithSecrets( new IcebergRestConnectionConfigInfoDpo( icebergRestConfigModel.getUri(), authenticationParameters, + null /*Service Identity Info*/, icebergRestConfigModel.getRemoteCatalogName()); break; case HADOOP: @@ -164,6 +183,7 @@ public static ConnectionConfigInfoDpo fromConnectionConfigInfoModelWithSecrets( new HadoopConnectionConfigInfoDpo( hadoopConfigModel.getUri(), authenticationParameters, + null /*Service Identity Info*/, hadoopConfigModel.getWarehouse()); break; default: @@ -173,6 +193,15 @@ public static ConnectionConfigInfoDpo fromConnectionConfigInfoModelWithSecrets( return config; } + /** + * Creates a new copy of the ConnectionConfigInfoDpo with the given service identity info. + * + * @param serviceIdentityInfo The service identity info to set. + * @return A new copy of the ConnectionConfigInfoDpo with the given service identity info. + */ + public abstract ConnectionConfigInfoDpo withServiceIdentity( + @Nonnull ServiceIdentityInfoDpo serviceIdentityInfo); + /** * Produces the correponding API-model ConnectionConfigInfo for this persistence object; many * fields are one-to-one direct mappings, but some fields, such as secretReferences, might only be diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java index 9a955de4fd..1b89de23bd 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java @@ -86,7 +86,7 @@ public OAuthClientCredentialsParametersDpo( return clientSecretReference; } - public @Nonnull List getScopes() { + public @Nullable List getScopes() { return scopes; } @@ -115,7 +115,7 @@ public OAuthClientCredentialsParametersDpo( } @Override - public AuthenticationParameters asAuthenticationParametersModel() { + public @Nonnull AuthenticationParameters asAuthenticationParametersModel() { return OAuthClientCredentialsParameters.builder() .setAuthenticationType(AuthenticationParameters.AuthenticationTypeEnum.OAUTH) .setTokenUri(getTokenUri()) diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/SigV4AuthenticationParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/SigV4AuthenticationParametersDpo.java new file mode 100644 index 0000000000..1d5ca8561f --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/SigV4AuthenticationParametersDpo.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.polaris.core.connection; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableMap; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.util.Map; +import org.apache.iceberg.aws.AwsProperties; +import org.apache.iceberg.rest.auth.AuthProperties; +import org.apache.polaris.core.admin.model.AuthenticationParameters; +import org.apache.polaris.core.admin.model.SigV4AuthenticationParameters; +import org.apache.polaris.core.secrets.UserSecretsManager; + +/** + * The internal persistence-object counterpart to SigV4AuthenticationParameters defined in the API + * model. + */ +public class SigV4AuthenticationParametersDpo extends AuthenticationParametersDpo { + + // The aws IAM role arn assumed by polaris userArn when signing requests + @JsonProperty(value = "roleArn") + private final String roleArn; + + // The session name used when assuming the role + @JsonProperty(value = "roleSessionName") + private final String roleSessionName; + + // An optional external id used to establish a trust relationship with AWS in the trust policy + @JsonProperty(value = "externalId") + private final String externalId; + + // Region to be used by the SigV4 protocol for signing requests + @JsonProperty(value = "signingRegion") + private final String signingRegion; + + // The service name to be used by the SigV4 protocol for signing requests, the default signing + // name is "execute-api" is if not provided + @JsonProperty(value = "signingName") + private final String signingName; + + public SigV4AuthenticationParametersDpo( + @JsonProperty(value = "roleArn", required = true) String roleArn, + @JsonProperty(value = "roleSessionName", required = false) String roleSessionName, + @JsonProperty(value = "externalId", required = false) String externalId, + @JsonProperty(value = "signingRegion", required = true) String signingRegion, + @JsonProperty(value = "signingName", required = false) String signingName) { + super(AuthenticationType.SIGV4.getCode()); + this.roleArn = roleArn; + this.roleSessionName = roleSessionName; + this.externalId = externalId; + this.signingRegion = signingRegion; + this.signingName = signingName; + } + + public @Nonnull String getRoleArn() { + return roleArn; + } + + public @Nullable String getRoleSessionName() { + return roleSessionName; + } + + public @Nullable String getExternalId() { + return externalId; + } + + public @Nonnull String getSigningRegion() { + return signingRegion; + } + + public @Nullable String getSigningName() { + return signingName; + } + + @Nonnull + @Override + public Map asIcebergCatalogProperties(UserSecretsManager secretsManager) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.put(AuthProperties.AUTH_TYPE, AuthProperties.AUTH_TYPE_SIGV4); + builder.put(AwsProperties.REST_SIGNER_REGION, getSigningRegion()); + if (getSigningName() != null) { + builder.put(AwsProperties.REST_SIGNING_NAME, getSigningName()); + } + + // TODO: Add a credential manager to assume the role and get the aws session credentials + return builder.build(); + } + + @Override + public @Nonnull AuthenticationParameters asAuthenticationParametersModel() { + return SigV4AuthenticationParameters.builder() + .setAuthenticationType(AuthenticationParameters.AuthenticationTypeEnum.SIGV4) + .setRoleArn(getRoleArn()) + .setRoleSessionName(getRoleSessionName()) + .setExternalId(getExternalId()) + .setSigningRegion(getSigningRegion()) + .setSigningName(getSigningName()) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("authenticationTypeCode", getAuthenticationTypeCode()) + .add("roleArn", getRoleArn()) + .add("roleSessionName", getRoleSessionName()) + .add("externalId", getExternalId()) + .add("signingRegion", getSigningRegion()) + .add("signingName", getSigningName()) + .toString(); + } +} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/hadoop/HadoopConnectionConfigInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/hadoop/HadoopConnectionConfigInfoDpo.java index 5f29482c15..05104dd208 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/hadoop/HadoopConnectionConfigInfoDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/hadoop/HadoopConnectionConfigInfoDpo.java @@ -24,12 +24,14 @@ import jakarta.annotation.Nullable; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.apache.iceberg.CatalogProperties; import org.apache.polaris.core.admin.model.ConnectionConfigInfo; import org.apache.polaris.core.admin.model.HadoopConnectionConfigInfo; import org.apache.polaris.core.connection.AuthenticationParametersDpo; import org.apache.polaris.core.connection.ConnectionConfigInfoDpo; import org.apache.polaris.core.connection.ConnectionType; +import org.apache.polaris.core.identity.dpo.ServiceIdentityInfoDpo; import org.apache.polaris.core.secrets.UserSecretsManager; /** @@ -44,8 +46,10 @@ public HadoopConnectionConfigInfoDpo( @JsonProperty(value = "uri", required = true) @Nonnull String uri, @JsonProperty(value = "authenticationParameters", required = true) @Nonnull AuthenticationParametersDpo authenticationParameters, + @JsonProperty(value = "serviceIdentity", required = false) @Nullable + ServiceIdentityInfoDpo serviceIdentityInfo, @JsonProperty(value = "warehouse", required = false) @Nullable String remoteCatalogName) { - super(ConnectionType.HADOOP.getCode(), uri, authenticationParameters); + super(ConnectionType.HADOOP.getCode(), uri, authenticationParameters, serviceIdentityInfo); this.warehouse = remoteCatalogName; } @@ -60,6 +64,7 @@ public String toString() { .add("uri", getUri()) .add("warehouse", getWarehouse()) .add("authenticationParameters", getAuthenticationParameters().toString()) + .add("serviceIdentity", getServiceIdentity()) .toString(); } @@ -75,6 +80,13 @@ public String toString() { return properties; } + @Override + public ConnectionConfigInfoDpo withServiceIdentity( + @Nonnull ServiceIdentityInfoDpo serviceIdentityInfo) { + return new HadoopConnectionConfigInfoDpo( + getUri(), getAuthenticationParameters(), serviceIdentityInfo, getWarehouse()); + } + @Override public ConnectionConfigInfo asConnectionConfigInfoModel() { return HadoopConnectionConfigInfo.builder() @@ -83,6 +95,10 @@ public ConnectionConfigInfo asConnectionConfigInfoModel() { .setWarehouse(getWarehouse()) .setAuthenticationParameters( getAuthenticationParameters().asAuthenticationParametersModel()) + .setServiceIdentity( + Optional.ofNullable(getServiceIdentity()) + .map(ServiceIdentityInfoDpo::asServiceIdentityInfoModel) + .orElse(null)) .build(); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/iceberg/IcebergRestConnectionConfigInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/iceberg/IcebergRestConnectionConfigInfoDpo.java index 236dcee293..0a6e870b7f 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/iceberg/IcebergRestConnectionConfigInfoDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/iceberg/IcebergRestConnectionConfigInfoDpo.java @@ -24,12 +24,14 @@ import jakarta.annotation.Nullable; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.apache.iceberg.CatalogProperties; import org.apache.polaris.core.admin.model.ConnectionConfigInfo; import org.apache.polaris.core.admin.model.IcebergRestConnectionConfigInfo; import org.apache.polaris.core.connection.AuthenticationParametersDpo; import org.apache.polaris.core.connection.ConnectionConfigInfoDpo; import org.apache.polaris.core.connection.ConnectionType; +import org.apache.polaris.core.identity.dpo.ServiceIdentityInfoDpo; import org.apache.polaris.core.secrets.UserSecretsManager; /** @@ -45,9 +47,12 @@ public IcebergRestConnectionConfigInfoDpo( @JsonProperty(value = "uri", required = true) @Nonnull String uri, @JsonProperty(value = "authenticationParameters", required = true) @Nonnull AuthenticationParametersDpo authenticationParameters, + @JsonProperty(value = "serviceIdentity", required = false) @Nullable + ServiceIdentityInfoDpo serviceIdentityInfo, @JsonProperty(value = "remoteCatalogName", required = false) @Nullable String remoteCatalogName) { - super(ConnectionType.ICEBERG_REST.getCode(), uri, authenticationParameters); + super( + ConnectionType.ICEBERG_REST.getCode(), uri, authenticationParameters, serviceIdentityInfo); this.remoteCatalogName = remoteCatalogName; } @@ -67,6 +72,13 @@ public String getRemoteCatalogName() { return properties; } + @Override + public ConnectionConfigInfoDpo withServiceIdentity( + @Nonnull ServiceIdentityInfoDpo serviceIdentityInfo) { + return new IcebergRestConnectionConfigInfoDpo( + getUri(), getAuthenticationParameters(), serviceIdentityInfo, getRemoteCatalogName()); + } + @Override public ConnectionConfigInfo asConnectionConfigInfoModel() { return IcebergRestConnectionConfigInfo.builder() @@ -75,6 +87,10 @@ public ConnectionConfigInfo asConnectionConfigInfoModel() { .setRemoteCatalogName(getRemoteCatalogName()) .setAuthenticationParameters( getAuthenticationParameters().asAuthenticationParametersModel()) + .setServiceIdentity( + Optional.ofNullable(getServiceIdentity()) + .map(ServiceIdentityInfoDpo::asServiceIdentityInfoModel) + .orElse(null)) .build(); } @@ -85,6 +101,7 @@ public String toString() { .add("uri", getUri()) .add("remoteCatalogName", getRemoteCatalogName()) .add("authenticationParameters", getAuthenticationParameters().toString()) + .add("serviceIdentity", getServiceIdentity()) .toString(); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java index 0de3c3daa4..623afb7c21 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java @@ -338,6 +338,14 @@ public Builder setConnectionConfigInfoDpoWithSecrets( return this; } + public Builder setConnectionConfigInfoDpo( + @Nonnull ConnectionConfigInfoDpo connectionConfigInfoDpo) { + internalProperties.put( + PolarisEntityConstants.getConnectionConfigInfoPropertyName(), + connectionConfigInfoDpo.serialize()); + return this; + } + @Override public CatalogEntity build() { return new CatalogEntity(buildBase()); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/identity/ServiceIdentityType.java b/polaris-core/src/main/java/org/apache/polaris/core/identity/ServiceIdentityType.java new file mode 100644 index 0000000000..c05e4573e8 --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/identity/ServiceIdentityType.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.polaris.core.identity; + +import jakarta.annotation.Nonnull; +import java.util.Arrays; +import org.apache.polaris.core.identity.dpo.ServiceIdentityInfoDpo; + +/** + * The internal persistence-object counterpart to ServiceIdentityTypeInfo.ServiceIdentityTypeEnum + * defined in the API model. We define integer type codes in this enum for better compatibility + * within persisted data in case the names of enum types are ever changed in place. + * + *

Important: Codes must be kept in-sync with JsonSubTypes annotated within {@link + * ServiceIdentityInfoDpo}. + */ +public enum ServiceIdentityType { + NULL_TYPE(0), + AWS_IAM(1), + ; + + private static final ServiceIdentityType[] REVERSE_MAPPING_ARRAY; + + static { + // find max array size + int maxCode = + Arrays.stream(ServiceIdentityType.values()) + .mapToInt(ServiceIdentityType::getCode) + .max() + .orElse(0); + + // allocate mapping array + REVERSE_MAPPING_ARRAY = new ServiceIdentityType[maxCode + 1]; + + // populate mapping array + for (ServiceIdentityType authType : ServiceIdentityType.values()) { + REVERSE_MAPPING_ARRAY[authType.code] = authType; + } + } + + private final int code; + + ServiceIdentityType(int code) { + this.code = code; + } + + /** + * Given the code associated with the type, return the associated ServiceIdentityType. Return + * NULL_TYPE if not found + * + * @param identityTypeCode code associated to the service identity type + * @return ServiceIdentityType corresponding to that code or null if mapping not found + */ + public static @Nonnull ServiceIdentityType fromCode(int identityTypeCode) { + // ensure it is within bounds + if (identityTypeCode < 0 || identityTypeCode >= REVERSE_MAPPING_ARRAY.length) { + return NULL_TYPE; + } + + // get value + return REVERSE_MAPPING_ARRAY[identityTypeCode]; + } + + public int getCode() { + return this.code; + } +} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java new file mode 100644 index 0000000000..467b366295 --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.polaris.core.identity.dpo; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.MoreObjects; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.apache.polaris.core.admin.model.AwsIamServiceIdentityInfo; +import org.apache.polaris.core.admin.model.ServiceIdentityInfo; +import org.apache.polaris.core.identity.ServiceIdentityType; +import org.apache.polaris.core.secrets.ServiceSecretReference; + +/** + * Persistence-layer representation of an AWS IAM service identity used by Polaris. + * + *

This class models an AWS IAM identity (either a user or role) and extends {@link + * ServiceIdentityInfoDpo}. It is typically used internally to store both the identity metadata + * (such as the IAM ARN) and a reference to the actual credential (e.g., via {@link + * ServiceSecretReference}). + * + *

Instances of this class are convertible to the public API model {@link + * AwsIamServiceIdentityInfo}. + */ +public class AwsIamServiceIdentityInfoDpo extends ServiceIdentityInfoDpo { + + @JsonCreator + public AwsIamServiceIdentityInfoDpo( + @JsonProperty(value = "identityInfoReference", required = false) @Nullable + ServiceSecretReference identityInfoReference) { + super(ServiceIdentityType.AWS_IAM.getCode(), identityInfoReference); + } + + @Override + public @Nonnull ServiceIdentityInfo asServiceIdentityInfoModel() { + return AwsIamServiceIdentityInfo.builder() + .setIdentityType(ServiceIdentityInfo.IdentityTypeEnum.AWS_IAM) + // TODO: inject service identity info + .setIamArn("") + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("identityTypeCode", getIdentityTypeCode()) + .toString(); + } +} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java new file mode 100644 index 0000000000..ff8da6166d --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.polaris.core.identity.dpo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.google.common.base.MoreObjects; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.apache.polaris.core.admin.model.ServiceIdentityInfo; +import org.apache.polaris.core.identity.ServiceIdentityType; +import org.apache.polaris.core.secrets.ServiceSecretReference; + +/** + * The internal persistence-object counterpart to ServiceIdentityInfo defined in the API model. + * Important: JsonSubTypes must be kept in sync with {@link ServiceIdentityType}. + */ +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "identityTypeCode") +@JsonSubTypes({@JsonSubTypes.Type(value = AwsIamServiceIdentityInfoDpo.class, name = "1")}) +public abstract class ServiceIdentityInfoDpo { + + @JsonProperty(value = "identityTypeCode") + private final int identityTypeCode; + + @JsonProperty(value = "identityInfoReference") + private final ServiceSecretReference identityInfoReference; + + public ServiceIdentityInfoDpo( + @JsonProperty(value = "identityTypeCode", required = true) int identityTypeCode, + @JsonProperty(value = "identityInfoReference", required = false) @Nullable + ServiceSecretReference identityInfoReference) { + this.identityTypeCode = identityTypeCode; + this.identityInfoReference = identityInfoReference; + } + + public int getIdentityTypeCode() { + return identityTypeCode; + } + + @JsonIgnore + public ServiceIdentityType getIdentityType() { + return ServiceIdentityType.fromCode(identityTypeCode); + } + + @JsonProperty + public ServiceSecretReference getIdentityInfoReference() { + return identityInfoReference; + } + + /** + * Converts this persistence object to the corresponding API model. During the conversion, some + * fields will be dropped, e.g. the reference to the service identity's credential + */ + public abstract @Nonnull ServiceIdentityInfo asServiceIdentityInfoModel(); + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("identityTypeCode", getIdentityTypeCode()) + .toString(); + } +} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java b/polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java new file mode 100644 index 0000000000..3edc99aa7a --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.polaris.core.secrets; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.util.Map; + +/** + * Represents a "wrapped reference" to a service-owned secret that holds an identifier to retrieve + * possibly remotely-stored secret material, along with an open-ended "referencePayload" that is + * specific to an implementation of the secret storage and which is needed "unwrap" the actual + * secret in combination with whatever is stored in the remote secrets storage. + */ +public class ServiceSecretReference extends UserSecretReference { + /** + * @param urn A string which should be self-sufficient to retrieve whatever secret material that + * is stored in the remote secret store. e.g., + * 'urn:polaris-service-secret:<service-manager-type>:<type-specific-identifier> + * @param referencePayload Optionally, any additional information that is necessary to fully + * reconstitute the original secret based on what is retrieved by the {@code urn}; this + * payload may include hashes/checksums, encryption key ids, OTP encryption keys, additional + * protocol/version specifiers, etc., which are implementation-specific. + */ + public ServiceSecretReference( + @JsonProperty(value = "urn", required = true) @Nonnull String urn, + @JsonProperty(value = "referencePayload") @Nullable Map referencePayload) { + super(urn, referencePayload); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ServiceSecretReference && super.equals(obj); + } +} diff --git a/polaris-core/src/test/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpoTest.java b/polaris-core/src/test/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpoTest.java index da1f4b3e66..727fac0d0f 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpoTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpoTest.java @@ -163,4 +163,65 @@ void testImplicitAuthenticationParameters() throws JsonProcessingException { objectMapper.readValue(expectedApiModelJson, ConnectionConfigInfo.class), connectionConfigInfoApiModel); } + + @Test + void testSigV4AuthenticationParameters() throws JsonProcessingException { + // Test deserialization and reserialization of the persistence JSON. + String json = + "" + + "{" + + " \"connectionTypeCode\": 1," + + " \"uri\": \"https://glue.us-west-2.amazonaws.com/iceberg\"," + + " \"remoteCatalogName\": \"123456789012\"," + + " \"authenticationParameters\": {" + + " \"authenticationTypeCode\": 4," + + " \"roleArn\": \"arn:aws:iam::123456789012:role/glue-catalog-role\"," + + " \"roleSessionName\": \"polaris-catalog-federation\"," + + " \"externalId\": \"external-id\"," + + " \"signingRegion\": \"us-west-2\"," + + " \"signingName\": \"glue\"" + + " }," + + " \"serviceIdentity\": {" + + " \"identityTypeCode\": 1," + + " \"identityInfoReference\": {" + + " \"urn\": \"urn:polaris-secret:default-identity-registry:my-realm:AWS_IAM\"," + + " \"referencePayload\": {" + + " \"key\": \"value\"" + + " }" + + " }" + + " }" + + "}"; + + ConnectionConfigInfoDpo connectionConfigInfoDpo = ConnectionConfigInfoDpo.deserialize(json); + Assertions.assertNotNull(connectionConfigInfoDpo); + JsonNode tree1 = objectMapper.readTree(json); + JsonNode tree2 = objectMapper.readTree(connectionConfigInfoDpo.serialize()); + Assertions.assertEquals(tree1, tree2); + + // Test conversion into API model JSON. + ConnectionConfigInfo connectionConfigInfoApiModel = + connectionConfigInfoDpo.asConnectionConfigInfoModel(); + String expectedApiModelJson = + "" + + "{" + + " \"connectionType\": \"ICEBERG_REST\"," + + " \"uri\": \"https://glue.us-west-2.amazonaws.com/iceberg\"," + + " \"remoteCatalogName\": \"123456789012\"," + + " \"authenticationParameters\": {" + + " \"authenticationType\": \"SIGV4\"," + + " \"roleArn\": \"arn:aws:iam::123456789012:role/glue-catalog-role\"," + + " \"roleSessionName\": \"polaris-catalog-federation\"," + + " \"externalId\": \"external-id\"," + + " \"signingRegion\": \"us-west-2\"," + + " \"signingName\": \"glue\"" + + " }," + + " \"serviceIdentity\": {" + + " \"identityType\": \"AWS_IAM\"," + + " \"iamArn\": \"\"" + + " }" + + "}"; + Assertions.assertEquals( + objectMapper.readValue(expectedApiModelJson, ConnectionConfigInfo.class), + connectionConfigInfoApiModel); + } } From fef363fe2bfa70d16fd61221a757e44ef8a4213c Mon Sep 17 00:00:00 2001 From: Rulin Xing Date: Thu, 14 Aug 2025 10:58:33 -0700 Subject: [PATCH 2/2] Rename UserSecretReference to SecretReference and fix some small issues --- .../AuthenticationParametersDpo.java | 4 +- .../BearerAuthenticationParametersDpo.java | 8 +-- .../connection/ConnectionConfigInfoDpo.java | 11 ++-- .../OAuthClientCredentialsParametersDpo.java | 8 +-- .../polaris/core/entity/CatalogEntity.java | 4 +- .../dpo/AwsIamServiceIdentityInfoDpo.java | 13 +++-- .../identity/dpo/ServiceIdentityInfoDpo.java | 11 ++-- ...retReference.java => SecretReference.java} | 21 ++++---- .../core/secrets/ServiceSecretReference.java | 53 ------------------- .../secrets/UnsafeInMemorySecretsManager.java | 11 ++-- .../core/secrets/UserSecretsManager.java | 19 ++++--- ...enceTest.java => SecretReferenceTest.java} | 17 +++--- .../secrets/UserSecretsManagerBaseTest.java | 20 +++---- .../service/admin/PolarisAdminService.java | 16 +++--- 14 files changed, 83 insertions(+), 133 deletions(-) rename polaris-core/src/main/java/org/apache/polaris/core/secrets/{UserSecretReference.java => SecretReference.java} (94%) delete mode 100644 polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java rename polaris-core/src/test/java/org/apache/polaris/core/secrets/{UserSecretReferenceTest.java => SecretReferenceTest.java} (80%) diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java index 20ddee0247..f7d08c3725 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/AuthenticationParametersDpo.java @@ -29,7 +29,7 @@ import org.apache.polaris.core.admin.model.OAuthClientCredentialsParameters; import org.apache.polaris.core.admin.model.SigV4AuthenticationParameters; import org.apache.polaris.core.connection.iceberg.IcebergCatalogPropertiesProvider; -import org.apache.polaris.core.secrets.UserSecretReference; +import org.apache.polaris.core.secrets.SecretReference; /** * The internal persistence-object counterpart to AuthenticationParameters defined in the API model. @@ -71,7 +71,7 @@ public AuthenticationType getAuthenticationType() { public static AuthenticationParametersDpo fromAuthenticationParametersModelWithSecrets( AuthenticationParameters authenticationParameters, - Map secretReferences) { + Map secretReferences) { final AuthenticationParametersDpo config; switch (authenticationParameters.getAuthenticationType()) { case OAUTH: diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java index 49e6953752..2da854a59a 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/BearerAuthenticationParametersDpo.java @@ -25,7 +25,7 @@ import org.apache.iceberg.rest.auth.OAuth2Properties; import org.apache.polaris.core.admin.model.AuthenticationParameters; import org.apache.polaris.core.admin.model.BearerAuthenticationParameters; -import org.apache.polaris.core.secrets.UserSecretReference; +import org.apache.polaris.core.secrets.SecretReference; import org.apache.polaris.core.secrets.UserSecretsManager; /** @@ -35,16 +35,16 @@ public class BearerAuthenticationParametersDpo extends AuthenticationParametersDpo { @JsonProperty(value = "bearerTokenReference") - private final UserSecretReference bearerTokenReference; + private final SecretReference bearerTokenReference; public BearerAuthenticationParametersDpo( @JsonProperty(value = "bearerTokenReference", required = true) @Nonnull - UserSecretReference bearerTokenReference) { + SecretReference bearerTokenReference) { super(AuthenticationType.BEARER.getCode()); this.bearerTokenReference = bearerTokenReference; } - public @Nonnull UserSecretReference getBearerTokenReference() { + public @Nonnull SecretReference getBearerTokenReference() { return bearerTokenReference; } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java index 2fed570a3b..367c190817 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/ConnectionConfigInfoDpo.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -38,7 +39,7 @@ import org.apache.polaris.core.connection.iceberg.IcebergCatalogPropertiesProvider; import org.apache.polaris.core.connection.iceberg.IcebergRestConnectionConfigInfoDpo; import org.apache.polaris.core.identity.dpo.ServiceIdentityInfoDpo; -import org.apache.polaris.core.secrets.UserSecretReference; +import org.apache.polaris.core.secrets.SecretReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,7 +75,7 @@ public ConnectionConfigInfoDpo( @JsonProperty(value = "uri", required = true) @Nonnull String uri, @JsonProperty(value = "authenticationParameters", required = true) @Nonnull AuthenticationParametersDpo authenticationParameters, - @JsonProperty(value = "serviceIdentity", required = false) @Nonnull + @JsonProperty(value = "serviceIdentity", required = false) @Nullable ServiceIdentityInfoDpo serviceIdentity) { this(connectionTypeCode, uri, authenticationParameters, serviceIdentity, true); } @@ -83,7 +84,7 @@ protected ConnectionConfigInfoDpo( int connectionTypeCode, @Nonnull String uri, @Nonnull AuthenticationParametersDpo authenticationParameters, - @Nonnull ServiceIdentityInfoDpo serviceIdentity, + @Nullable ServiceIdentityInfoDpo serviceIdentity, boolean validateUri) { this.connectionTypeCode = connectionTypeCode; this.uri = uri; @@ -111,7 +112,7 @@ public AuthenticationParametersDpo getAuthenticationParameters() { return authenticationParameters; } - public @Nonnull ServiceIdentityInfoDpo getServiceIdentity() { + public @Nullable ServiceIdentityInfoDpo getServiceIdentity() { return serviceIdentity; } @@ -156,7 +157,7 @@ protected void validateUri(String uri) { */ public static ConnectionConfigInfoDpo fromConnectionConfigInfoModelWithSecrets( ConnectionConfigInfo connectionConfigurationModel, - Map secretReferences) { + Map secretReferences) { ConnectionConfigInfoDpo config = null; final AuthenticationParametersDpo authenticationParameters; switch (connectionConfigurationModel.getConnectionType()) { diff --git a/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java index 1b89de23bd..270560b505 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/connection/OAuthClientCredentialsParametersDpo.java @@ -35,7 +35,7 @@ import org.apache.iceberg.rest.auth.OAuth2Util; import org.apache.polaris.core.admin.model.AuthenticationParameters; import org.apache.polaris.core.admin.model.OAuthClientCredentialsParameters; -import org.apache.polaris.core.secrets.UserSecretReference; +import org.apache.polaris.core.secrets.SecretReference; import org.apache.polaris.core.secrets.UserSecretsManager; /** @@ -53,7 +53,7 @@ public class OAuthClientCredentialsParametersDpo extends AuthenticationParameter private final String clientId; @JsonProperty(value = "clientSecretReference") - private final UserSecretReference clientSecretReference; + private final SecretReference clientSecretReference; @JsonProperty(value = "scopes") private final List scopes; @@ -62,7 +62,7 @@ public OAuthClientCredentialsParametersDpo( @JsonProperty(value = "tokenUri", required = false) @Nullable String tokenUri, @JsonProperty(value = "clientId", required = true) @Nonnull String clientId, @JsonProperty(value = "clientSecretReference", required = true) @Nonnull - UserSecretReference clientSecretReference, + SecretReference clientSecretReference, @JsonProperty(value = "scopes", required = false) @Nullable List scopes) { super(AuthenticationType.OAUTH.getCode()); @@ -82,7 +82,7 @@ public OAuthClientCredentialsParametersDpo( return clientId; } - public @Nonnull UserSecretReference getClientSecretReference() { + public @Nonnull SecretReference getClientSecretReference() { return clientSecretReference; } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java index 623afb7c21..5747bd094e 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java @@ -43,7 +43,7 @@ import org.apache.polaris.core.config.BehaviorChangeConfiguration; import org.apache.polaris.core.connection.ConnectionConfigInfoDpo; import org.apache.polaris.core.context.CallContext; -import org.apache.polaris.core.secrets.UserSecretReference; +import org.apache.polaris.core.secrets.SecretReference; import org.apache.polaris.core.storage.FileStorageConfigurationInfo; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo; @@ -327,7 +327,7 @@ private void validateMaxAllowedLocations( public Builder setConnectionConfigInfoDpoWithSecrets( ConnectionConfigInfo connectionConfigurationModel, - Map secretReferences) { + Map secretReferences) { if (connectionConfigurationModel != null) { ConnectionConfigInfoDpo config = ConnectionConfigInfoDpo.fromConnectionConfigInfoModelWithSecrets( diff --git a/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java index 467b366295..cddad3a422 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/AwsIamServiceIdentityInfoDpo.java @@ -26,15 +26,18 @@ import org.apache.polaris.core.admin.model.AwsIamServiceIdentityInfo; import org.apache.polaris.core.admin.model.ServiceIdentityInfo; import org.apache.polaris.core.identity.ServiceIdentityType; -import org.apache.polaris.core.secrets.ServiceSecretReference; +import org.apache.polaris.core.secrets.SecretReference; /** * Persistence-layer representation of an AWS IAM service identity used by Polaris. * *

This class models an AWS IAM identity (either a user or role) and extends {@link - * ServiceIdentityInfoDpo}. It is typically used internally to store both the identity metadata - * (such as the IAM ARN) and a reference to the actual credential (e.g., via {@link - * ServiceSecretReference}). + * ServiceIdentityInfoDpo}. It is typically used internally to store a reference to the actual + * credential (e.g., via {@link SecretReference}). + * + *

During the runtime, it will be resolved to an actual ResolvedAwsIamServiceIdentityInfo object + * which contains the actual service identity info (e.g., the IAM user arn) and the corresponding + * credential. * *

Instances of this class are convertible to the public API model {@link * AwsIamServiceIdentityInfo}. @@ -44,7 +47,7 @@ public class AwsIamServiceIdentityInfoDpo extends ServiceIdentityInfoDpo { @JsonCreator public AwsIamServiceIdentityInfoDpo( @JsonProperty(value = "identityInfoReference", required = false) @Nullable - ServiceSecretReference identityInfoReference) { + SecretReference identityInfoReference) { super(ServiceIdentityType.AWS_IAM.getCode(), identityInfoReference); } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java index ff8da6166d..22f25dd70d 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/identity/dpo/ServiceIdentityInfoDpo.java @@ -27,11 +27,14 @@ import jakarta.annotation.Nullable; import org.apache.polaris.core.admin.model.ServiceIdentityInfo; import org.apache.polaris.core.identity.ServiceIdentityType; -import org.apache.polaris.core.secrets.ServiceSecretReference; +import org.apache.polaris.core.secrets.SecretReference; /** * The internal persistence-object counterpart to ServiceIdentityInfo defined in the API model. * Important: JsonSubTypes must be kept in sync with {@link ServiceIdentityType}. + * + *

During the runtime, it will be resolved to an actual ResolvedServiceIdentityInfo object which + * contains the actual service identity info and the corresponding credential. */ @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -44,12 +47,12 @@ public abstract class ServiceIdentityInfoDpo { private final int identityTypeCode; @JsonProperty(value = "identityInfoReference") - private final ServiceSecretReference identityInfoReference; + private final SecretReference identityInfoReference; public ServiceIdentityInfoDpo( @JsonProperty(value = "identityTypeCode", required = true) int identityTypeCode, @JsonProperty(value = "identityInfoReference", required = false) @Nullable - ServiceSecretReference identityInfoReference) { + SecretReference identityInfoReference) { this.identityTypeCode = identityTypeCode; this.identityInfoReference = identityInfoReference; } @@ -64,7 +67,7 @@ public ServiceIdentityType getIdentityType() { } @JsonProperty - public ServiceSecretReference getIdentityInfoReference() { + public SecretReference getIdentityInfoReference() { return identityInfoReference; } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretReference.java b/polaris-core/src/main/java/org/apache/polaris/core/secrets/SecretReference.java similarity index 94% rename from polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretReference.java rename to polaris-core/src/main/java/org/apache/polaris/core/secrets/SecretReference.java index 77a69dc6f2..6b9c6d25da 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretReference.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/secrets/SecretReference.java @@ -51,7 +51,7 @@ * the stored "secret material" as well as the referencePayload and any associated keys used for * encryption. */ -public class UserSecretReference { +public class SecretReference { @JsonProperty(value = "urn") private final String urn; @@ -74,7 +74,7 @@ public class UserSecretReference { Pattern.compile("^" + TYPE_SPECIFIC_IDENTIFIER_REGEX + "$"); /** - * Precompiled regex pattern for validating and parsing UserSecretReference URNs. Expected format: + * Precompiled regex pattern for validating and parsing SecretReference URNs. Expected format: * urn:polaris-secret::(::...). * *

Groups: @@ -98,15 +98,15 @@ public class UserSecretReference { /** * @param urn A string which should be self-sufficient to retrieve whatever secret material that * is stored in the remote secret store and also to identify an implementation of the - * UserSecretsManager which is capable of interpreting this concrete UserSecretReference. - * Should be of the form: + * UserSecretsManager which is capable of interpreting this concrete SecretReference. Should + * be of the form: * 'urn:polaris-secret:<secret-manager-type>:<type-specific-identifier> * @param referencePayload Optionally, any additional information that is necessary to fully * reconstitute the original secret based on what is retrieved by the {@code urn}; this * payload may include hashes/checksums, encryption key ids, OTP encryption keys, additional * protocol/version specifiers, etc., which are implementation-specific. */ - public UserSecretReference( + public SecretReference( @JsonProperty(value = "urn", required = true) @Nonnull String urn, @JsonProperty(value = "referencePayload") @Nullable Map referencePayload) { Preconditions.checkArgument( @@ -117,8 +117,7 @@ public UserSecretReference( } /** - * Validates whether the given URN string matches the expected format for UserSecretReference - * URNs. + * Validates whether the given URN string matches the expected format for SecretReference URNs. * * @param urn The URN string to validate. * @return true if the URN is valid, false otherwise. @@ -164,13 +163,13 @@ public static String buildUrnString( } /** - * Since UserSecretReference objects are specific to UserSecretManager implementations, the + * Since SecretReference objects are specific to UserSecretManager implementations, the * "secret-manager-type" portion of the URN should be used to validate that a URN is valid for a * given implementation and to dispatch to the correct implementation at runtime if multiple * concurrent implementations are possible in a given runtime environment. */ @JsonIgnore - public String getUserSecretManagerType() { + public String getSecretManagerType() { Matcher matcher = URN_PATTERN.matcher(urn); Preconditions.checkState(matcher.matches(), "Invalid secret URN: " + urn); return matcher.group(1); @@ -203,10 +202,10 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (obj == null || !(obj instanceof UserSecretReference)) { + if (obj == null || !(obj instanceof SecretReference)) { return false; } - UserSecretReference that = (UserSecretReference) obj; + SecretReference that = (SecretReference) obj; return Objects.equals(this.getUrn(), that.getUrn()) && Objects.equals(this.getReferencePayload(), that.getReferencePayload()); } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java b/polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java deleted file mode 100644 index 3edc99aa7a..0000000000 --- a/polaris-core/src/main/java/org/apache/polaris/core/secrets/ServiceSecretReference.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.polaris.core.secrets; - -import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; -import java.util.Map; - -/** - * Represents a "wrapped reference" to a service-owned secret that holds an identifier to retrieve - * possibly remotely-stored secret material, along with an open-ended "referencePayload" that is - * specific to an implementation of the secret storage and which is needed "unwrap" the actual - * secret in combination with whatever is stored in the remote secrets storage. - */ -public class ServiceSecretReference extends UserSecretReference { - /** - * @param urn A string which should be self-sufficient to retrieve whatever secret material that - * is stored in the remote secret store. e.g., - * 'urn:polaris-service-secret:<service-manager-type>:<type-specific-identifier> - * @param referencePayload Optionally, any additional information that is necessary to fully - * reconstitute the original secret based on what is retrieved by the {@code urn}; this - * payload may include hashes/checksums, encryption key ids, OTP encryption keys, additional - * protocol/version specifiers, etc., which are implementation-specific. - */ - public ServiceSecretReference( - @JsonProperty(value = "urn", required = true) @Nonnull String urn, - @JsonProperty(value = "referencePayload") @Nullable Map referencePayload) { - super(urn, referencePayload); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof ServiceSecretReference && super.equals(obj); - } -} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/secrets/UnsafeInMemorySecretsManager.java b/polaris-core/src/main/java/org/apache/polaris/core/secrets/UnsafeInMemorySecretsManager.java index bb690a1588..7b402dd233 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/secrets/UnsafeInMemorySecretsManager.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/secrets/UnsafeInMemorySecretsManager.java @@ -48,8 +48,7 @@ public class UnsafeInMemorySecretsManager implements UserSecretsManager { /** {@inheritDoc} */ @Override @Nonnull - public UserSecretReference writeSecret( - @Nonnull String secret, @Nonnull PolarisEntityCore forEntity) { + public SecretReference writeSecret(@Nonnull String secret, @Nonnull PolarisEntityCore forEntity) { // For illustrative purposes and to exercise the control flow of requiring both the stored // secret as well as the secretReferencePayload to recover the original secret, we'll use // basic XOR encryption and store the randomly generated key in the reference payload. @@ -97,15 +96,15 @@ public UserSecretReference writeSecret( // key is ever shared and/or the key isn't a one-time-pad of the same length as the source // secret. referencePayload.put(ENCRYPTION_KEY, encryptedSecretKeyBase64); - UserSecretReference secretReference = new UserSecretReference(secretUrn, referencePayload); + SecretReference secretReference = new SecretReference(secretUrn, referencePayload); return secretReference; } /** {@inheritDoc} */ @Override @Nonnull - public String readSecret(@Nonnull UserSecretReference secretReference) { - String secretManagerType = secretReference.getUserSecretManagerType(); + public String readSecret(@Nonnull SecretReference secretReference) { + String secretManagerType = secretReference.getSecretManagerType(); Preconditions.checkState( secretManagerType.equals(SECRET_MANAGER_TYPE), "Invalid secret manager type, expected: " @@ -148,7 +147,7 @@ public String readSecret(@Nonnull UserSecretReference secretReference) { /** {@inheritDoc} */ @Override - public void deleteSecret(@Nonnull UserSecretReference secretReference) { + public void deleteSecret(@Nonnull SecretReference secretReference) { rawSecretStore.remove(secretReference.getUrn()); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretsManager.java b/polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretsManager.java index 194223053e..41126f2b87 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretsManager.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretsManager.java @@ -31,8 +31,8 @@ public interface UserSecretsManager { /** * Persist the {@code secret} under a new URN {@code secretUrn} and return a {@code - * UserSecretReference} that can subsequently be used by this same UserSecretsManager to retrieve - * the original secret. The {@code forEntity} is provided for an implementation to extract other + * SecretReference} that can subsequently be used by this same UserSecretsManager to retrieve the + * original secret. The {@code forEntity} is provided for an implementation to extract other * identifying metadata such as entity type, id, name, etc., to store alongside the remotely * stored secret to facilitate operational management of the secrets outside of the core Polaris * service (for example, to perform garbage-collection if the Polaris service fails to delete @@ -44,25 +44,24 @@ public interface UserSecretsManager { * its entirety within a persisted PolarisEntity */ @Nonnull - UserSecretReference writeSecret(@Nonnull String secret, @Nonnull PolarisEntityCore forEntity); + SecretReference writeSecret(@Nonnull String secret, @Nonnull PolarisEntityCore forEntity); /** - * Retrieve a secret using the {@code secretReference}. See {@link UserSecretReference} for - * details about identifiers and payloads. + * Retrieve a secret using the {@code secretReference}. See {@link SecretReference} for details + * about identifiers and payloads. * * @param secretReference Reference object for retrieving the original secret * @return The stored secret, or null if it no longer exists */ @Nonnull - String readSecret(@Nonnull UserSecretReference secretReference); + String readSecret(@Nonnull SecretReference secretReference); /** - * Delete a stored secret. See {@link UserSecretReference} for details about identifiers and - * payloads. + * Delete a stored secret. See {@link SecretReference} for details about identifiers and payloads. * * @param secretReference Reference object for retrieving the original secret */ - void deleteSecret(@Nonnull UserSecretReference secretReference); + void deleteSecret(@Nonnull SecretReference secretReference); /** * Builds a URN string from the given secret manager type and type-specific identifier. @@ -74,6 +73,6 @@ public interface UserSecretsManager { @Nonnull default String buildUrn( @Nonnull String secretManagerType, @Nonnull String typeSpecificIdentifier) { - return UserSecretReference.buildUrnString(secretManagerType, typeSpecificIdentifier); + return SecretReference.buildUrnString(secretManagerType, typeSpecificIdentifier); } } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretReferenceTest.java b/polaris-core/src/test/java/org/apache/polaris/core/secrets/SecretReferenceTest.java similarity index 80% rename from polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretReferenceTest.java rename to polaris-core/src/test/java/org/apache/polaris/core/secrets/SecretReferenceTest.java index e05bd947bb..09d0fe8844 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretReferenceTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/secrets/SecretReferenceTest.java @@ -25,7 +25,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -public class UserSecretReferenceTest { +public class SecretReferenceTest { @ParameterizedTest @ValueSource( @@ -36,7 +36,7 @@ public class UserSecretReferenceTest { "urn:polaris-secret:vault:project:env:service:key" }) public void testValidUrns(String validUrn) { - assertThat(new UserSecretReference(validUrn, null)).isNotNull(); + assertThat(new SecretReference(validUrn, null)).isNotNull(); } @ParameterizedTest @@ -54,7 +54,7 @@ public void testValidUrns(String validUrn) { "urn:polaris-secret:unsafe-in-memory:key::" }) public void testInvalidUrns(String invalidUrn) { - assertThatThrownBy(() -> new UserSecretReference(invalidUrn, null)) + assertThatThrownBy(() -> new SecretReference(invalidUrn, null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Invalid secret URN: " + invalidUrn); } @@ -62,22 +62,21 @@ public void testInvalidUrns(String invalidUrn) { @Test public void tesGetUrnComponents() { String urn = "urn:polaris-secret:unsafe-in-memory:key1:value1"; - UserSecretReference reference = new UserSecretReference(urn, null); + SecretReference reference = new SecretReference(urn, null); - assertThat(reference.getUserSecretManagerType()).isEqualTo("unsafe-in-memory"); + assertThat(reference.getSecretManagerType()).isEqualTo("unsafe-in-memory"); assertThat(reference.getTypeSpecificIdentifier()).isEqualTo("key1:value1"); } @Test public void testBuildUrn() { - String urn = UserSecretReference.buildUrnString("aws-secrets", "my-key"); + String urn = SecretReference.buildUrnString("aws-secrets", "my-key"); assertThat(urn).isEqualTo("urn:polaris-secret:aws-secrets:my-key"); - String urnWithMultipleIdentifiers = - UserSecretReference.buildUrnString("vault", "project:service"); + String urnWithMultipleIdentifiers = SecretReference.buildUrnString("vault", "project:service"); assertThat(urnWithMultipleIdentifiers).isEqualTo("urn:polaris-secret:vault:project:service"); - String urnWithNumbers = UserSecretReference.buildUrnString("type_123", "456:789"); + String urnWithNumbers = SecretReference.buildUrnString("type_123", "456:789"); assertThat(urnWithNumbers).isEqualTo("urn:polaris-secret:type_123:456:789"); } } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretsManagerBaseTest.java b/polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretsManagerBaseTest.java index 09e45b185a..287a885b40 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretsManagerBaseTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/secrets/UserSecretsManagerBaseTest.java @@ -47,17 +47,17 @@ public void testBasicSecretStorageAndRetrieval() throws JsonProcessingException String secret1 = "sensitivesecret1"; String secret2 = "sensitivesecret2"; - UserSecretReference reference1 = secretsManager.writeSecret(secret1, entity1); - UserSecretReference reference2 = secretsManager.writeSecret(secret2, entity2); + SecretReference reference1 = secretsManager.writeSecret(secret1, entity1); + SecretReference reference2 = secretsManager.writeSecret(secret2, entity2); - // Make sure we can JSON-serialize and deserialize the UserSecretReference objects. + // Make sure we can JSON-serialize and deserialize the SecretReference objects. String serializedReference1 = DEFAULT_MAPPER.writeValueAsString(reference1); String serializedReference2 = DEFAULT_MAPPER.writeValueAsString(reference2); - UserSecretReference reassembledReference1 = - DEFAULT_MAPPER.readValue(serializedReference1, UserSecretReference.class); - UserSecretReference reassembledReference2 = - DEFAULT_MAPPER.readValue(serializedReference2, UserSecretReference.class); + SecretReference reassembledReference1 = + DEFAULT_MAPPER.readValue(serializedReference1, SecretReference.class); + SecretReference reassembledReference2 = + DEFAULT_MAPPER.readValue(serializedReference2, SecretReference.class); Assertions.assertThat(reassembledReference1).isEqualTo(reference1); Assertions.assertThat(reassembledReference2).isEqualTo(reference2); @@ -74,8 +74,8 @@ public void testMultipleSecretsForSameEntity() { String secret1 = "sensitivesecret1"; String secret2 = "sensitivesecret2"; - UserSecretReference reference1 = secretsManager.writeSecret(secret1, entity1); - UserSecretReference reference2 = secretsManager.writeSecret(secret2, entity1); + SecretReference reference1 = secretsManager.writeSecret(secret1, entity1); + SecretReference reference2 = secretsManager.writeSecret(secret2, entity1); Assertions.assertThat(secretsManager.readSecret(reference1)).isEqualTo(secret1); Assertions.assertThat(secretsManager.readSecret(reference2)).isEqualTo(secret2); @@ -89,7 +89,7 @@ public void testDeleteSecret() { String secret1 = "sensitivesecret1"; - UserSecretReference reference1 = secretsManager.writeSecret(secret1, entity1); + SecretReference reference1 = secretsManager.writeSecret(secret1, entity1); Assertions.assertThat(secretsManager.readSecret(reference1)).isEqualTo(secret1); diff --git a/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java b/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java index 9ef57701d4..8761492d5b 100644 --- a/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java +++ b/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java @@ -106,7 +106,7 @@ import org.apache.polaris.core.persistence.resolver.ResolverStatus; import org.apache.polaris.core.policy.PolicyEntity; import org.apache.polaris.core.policy.exceptions.NoSuchPolicyException; -import org.apache.polaris.core.secrets.UserSecretReference; +import org.apache.polaris.core.secrets.SecretReference; import org.apache.polaris.core.secrets.UserSecretsManager; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; import org.apache.polaris.core.storage.StorageLocation; @@ -636,8 +636,8 @@ private boolean catalogOverlapsWithExistingCatalog(CatalogEntity catalogEntity) /** * Secrets embedded *or* simply referenced through the API model will require separate processing - * for normalizing into resolved/verified/offloaded UserSecretReference objects which are then - * placed appropriately into persistence objects. + * for normalizing into resolved/verified/offloaded SecretReference objects which are then placed + * appropriately into persistence objects. * *

If secrets are already direct URIs/URNs to an external secret store, we may need to validate * the URI/URN and/or transform into a polaris-internal URN format along with type-information or @@ -650,9 +650,9 @@ private boolean catalogOverlapsWithExistingCatalog(CatalogEntity catalogEntity) * secrets into a Polaris service-level secrets manager and return the associated internal * references to the stored secret. */ - private Map extractSecretReferences( + private Map extractSecretReferences( CreateCatalogRequest catalogRequest, PolarisEntity forEntity) { - Map secretReferences = new HashMap<>(); + Map secretReferences = new HashMap<>(); Catalog catalog = catalogRequest.getCatalog(); UserSecretsManager secretsManager = getUserSecretsManager(); if (catalog instanceof ExternalCatalog externalCatalog) { @@ -667,7 +667,7 @@ private Map extractSecretReferences( OAuthClientCredentialsParameters oauthClientCredentialsModel = (OAuthClientCredentialsParameters) authenticationParameters; String inlineClientSecret = oauthClientCredentialsModel.getClientSecret(); - UserSecretReference secretReference = + SecretReference secretReference = secretsManager.writeSecret(inlineClientSecret, forEntity); secretReferences.put( AuthenticationParametersDpo.INLINE_CLIENT_SECRET_REFERENCE_KEY, secretReference); @@ -678,7 +678,7 @@ private Map extractSecretReferences( BearerAuthenticationParameters bearerAuthenticationParametersModel = (BearerAuthenticationParameters) authenticationParameters; String inlineBearerToken = bearerAuthenticationParametersModel.getBearerToken(); - UserSecretReference secretReference = + SecretReference secretReference = secretsManager.writeSecret(inlineBearerToken, forEntity); secretReferences.put( AuthenticationParametersDpo.INLINE_BEARER_TOKEN_REFERENCE_KEY, secretReference); @@ -736,7 +736,7 @@ public PolarisEntity createCatalog(CreateCatalogRequest catalogRequest) { .log("Creating a federated catalog"); FeatureConfiguration.enforceFeatureEnabledOrThrow( callContext, FeatureConfiguration.ENABLE_CATALOG_FEDERATION); - Map processedSecretReferences = Map.of(); + Map processedSecretReferences = Map.of(); List supportedAuthenticationTypes = callContext .getRealmConfig()