Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
16d33fd
Initial prototype of catalog federation just passing special properti…
dennishuo Feb 7, 2025
3b1aa16
Defined internal representation classes for connection config
XJDKC Mar 3, 2025
1d1650b
Construct and initialize federated iceberg catalog based on connectio…
XJDKC Mar 3, 2025
205ef1e
Apply the same spec renames to the internal ConnectionConfiguration r…
dennishuo Mar 19, 2025
c8484e0
Manually pick @XJDKC fixes for integration tests and omittign secrets…
dennishuo Mar 19, 2025
acb5bec
Fix internal connection structs with updated naming from spec PR
dennishuo Mar 20, 2025
300553f
Push CreateCatalogRequest down to PolarisAdminService::createCatalog …
dennishuo Mar 27, 2025
4ad235e
Add new interface UserSecretsManager along with a default implementation
dennishuo Mar 30, 2025
cc17ae5
Wire in UserSecretsManager to createCatalog and federated Iceberg API…
dennishuo Mar 30, 2025
6700fb2
Since we already use commons-codec DigestUtils.sha256Hex, use that fo…
dennishuo Apr 3, 2025
10b2966
Rename the persistence-objects corresponding to API model objects wit…
dennishuo Apr 4, 2025
fd7ae59
Use UserSecretsManagerFactory to Produce the UserSecretsManager (#1)
XJDKC Apr 4, 2025
cbe362d
Gate all federation logic behind a new FeatureConfiguration - ENABLE_…
dennishuo Apr 4, 2025
3e85244
Also rename some variables and method names to be consistent with pri…
dennishuo Apr 4, 2025
9be6ee3
Merge branch 'main' of github.com:dennishuo/polaris into dhuo-catalog…
dennishuo Apr 4, 2025
7d5c9e1
Merge branch 'main' of github.com:dennishuo/polaris into dhuo-catalog…
dennishuo Apr 10, 2025
31a7333
Merge branch 'main' of github.com:dennishuo/polaris into dhuo-catalog…
dennishuo Apr 14, 2025
03af9f5
Change ConnectionType and AuthenticationType to be stored as int code…
dennishuo Apr 14, 2025
544ce82
Add javadoc comment to IcebergCatalogPropertiesProvider
dennishuo Apr 15, 2025
beaa34e
Add some constraints on the expected format of the URN in UserSecretR…
dennishuo Apr 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ public void testIcebergCreateTablesInExternalCatalog() throws IOException {
.withPartitionSpec(PartitionSpec.unpartitioned())
.create())
.isInstanceOf(BadRequestException.class)
.hasMessage("Malformed request: Cannot create table on external catalogs.");
.hasMessage("Malformed request: Cannot create table on static-facade external catalogs.");
}
}

Expand Down Expand Up @@ -515,7 +515,7 @@ public void testIcebergUpdateTableInExternalCatalog() throws IOException {
10L))
.commit())
.isInstanceOf(BadRequestException.class)
.hasMessage("Malformed request: Cannot update table on external catalogs.");
.hasMessage("Malformed request: Cannot update table on static-facade external catalogs.");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.List;
import java.util.Optional;
import org.apache.polaris.core.admin.model.StorageConfigInfo;
import org.apache.polaris.core.context.CallContext;
import org.apache.polaris.core.persistence.cache.EntityWeigher;

/**
Expand All @@ -36,6 +37,22 @@ protected FeatureConfiguration(
super(key, description, defaultValue, catalogConfig);
}

/**
* Helper for the common scenario of gating a feature with a boolean FeatureConfiguration, where
* we want to throw an UnsupportedOperationException if it's not enabled.
*/
public static void enforceFeatureEnabledOrThrow(
CallContext callContext, FeatureConfiguration<Boolean> featureConfig) {
boolean enabled =
callContext
.getPolarisCallContext()
.getConfigurationStore()
.getConfiguration(callContext.getPolarisCallContext(), featureConfig);
if (!enabled) {
throw new UnsupportedOperationException("Feature not enabled: " + featureConfig.key);
}
}

public static final FeatureConfiguration<Boolean>
ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING =
PolarisConfiguration.<Boolean>builder()
Expand Down Expand Up @@ -201,4 +218,13 @@ protected FeatureConfiguration(
+ " requires experimentation in the specific deployment environment")
.defaultValue(100 * EntityWeigher.WEIGHT_PER_MB)
.buildFeatureConfiguration();

public static final FeatureConfiguration<Boolean> ENABLE_CATALOG_FEDERATION =
PolarisConfiguration.<Boolean>builder()
.key("ENABLE_CATALOG_FEDERATION")
.description(
"If true, allows creating and using ExternalCatalogs containing ConnectionConfigInfos"
+ " to perform federation to remote catalogs.")
.defaultValue(false)
.buildFeatureConfiguration();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
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.secrets.UserSecretReference;

/**
* The internal persistence-object counterpart to AuthenticationParameters defined in the API model.
* Important: JsonSubTypes must be kept in sync with {@link AuthenticationType}.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "authenticationTypeCode", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = OAuthClientCredentialsParametersDpo.class, name = "1"),
@JsonSubTypes.Type(value = BearerAuthenticationParametersDpo.class, name = "2"),
})
public abstract class AuthenticationParametersDpo implements IcebergCatalogPropertiesProvider {

public static final String INLINE_CLIENT_SECRET_REFERENCE_KEY = "inlineClientSecretReference";
public static final String INLINE_BEARER_TOKEN_REFERENCE_KEY = "inlineBearerTokenReference";

@JsonProperty(value = "authenticationTypeCode")
private final int authenticationTypeCode;

public AuthenticationParametersDpo(
@JsonProperty(value = "authenticationTypeCode", required = true) int authenticationTypeCode) {
this.authenticationTypeCode = authenticationTypeCode;
}

public int getAuthenticationTypeCode() {
return authenticationTypeCode;
}

public abstract AuthenticationParameters asAuthenticationParametersModel();

public static AuthenticationParametersDpo fromAuthenticationParametersModelWithSecrets(
AuthenticationParameters authenticationParameters,
Map<String, UserSecretReference> secretReferences) {
final AuthenticationParametersDpo config;
switch (authenticationParameters.getAuthenticationType()) {
case OAUTH:
OAuthClientCredentialsParameters oauthClientCredentialsModel =
(OAuthClientCredentialsParameters) authenticationParameters;
config =
new OAuthClientCredentialsParametersDpo(
AuthenticationType.OAUTH.getCode(),
oauthClientCredentialsModel.getTokenUri(),
oauthClientCredentialsModel.getClientId(),
secretReferences.get(INLINE_CLIENT_SECRET_REFERENCE_KEY),
oauthClientCredentialsModel.getScopes());
break;
case BEARER:
BearerAuthenticationParameters bearerAuthenticationParametersModel =
(BearerAuthenticationParameters) authenticationParameters;
config =
new BearerAuthenticationParametersDpo(
AuthenticationType.BEARER.getCode(),
secretReferences.get(INLINE_BEARER_TOKEN_REFERENCE_KEY));
break;
default:
throw new IllegalStateException(
"Unsupported authentication type: " + authenticationParameters.getAuthenticationType());
}
return config;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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;

/**
* The internal persistence-object counterpart to AuthenticationParameters.AuthenticationTypeEnum
* 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.
*
* <p>Important: Codes must be kept in-sync with JsonSubTypes annotated within {@link
* AuthenticationParametersDpo}.
*/
public enum AuthenticationType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of wire/serialized compatibility perhaps we should specify the int values here like we do for some other enums

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Note that in existing places where we use int codes, we explicitly store the codes first-class in the entity model's top-level fields, whereas here we're using the enum to match the pattern of JsonSubType hierarchy, so we also depend on annotation magic. This means the update to support the int values here also means AuthenticationParametersDpo will have kind of ugly annotations that need to be manually kept in sync:

+/**
+ * The internal persistence-object counterpart to AuthenticationParameters defined in the API
+ * model. Important: JsonSubTypes must be kept in sync with {@link AuthenticationType}.
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "authenticationTypeCode", visible = true)
 @JsonSubTypes({
-  @JsonSubTypes.Type(value = OAuthClientCredentialsParametersDpo.class, name = "OAUTH"),
-  @JsonSubTypes.Type(value = BearerAuthenticationParametersDpo.class, name = "BEARER"),
+  @JsonSubTypes.Type(value = OAuthClientCredentialsParametersDpo.class, name = "1"),
+  @JsonSubTypes.Type(value = BearerAuthenticationParametersDpo.class, name = "2"),
 })

Of course, we already had to manually keep it in sync before, it just happened to be with strings that were a little more accessible (OAUTH, BEARER) since they matched the primary enum name.

In any case, I agree int codes will be more future-proof for our persisted data, even if it means it'll be a bit harder to debug at a glance.

OAUTH(1),
BEARER(2);

private final int code;

AuthenticationType(int code) {
this.code = code;
}

public int getCode() {
return this.code;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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 jakarta.annotation.Nonnull;
import java.util.Map;
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.UserSecretsManager;

/**
* The internal persistence-object counterpart to BearerAuthenticationParameters defined in the API
* model.
*/
public class BearerAuthenticationParametersDpo extends AuthenticationParametersDpo {

@JsonProperty(value = "bearerTokenReference")
private final UserSecretReference bearerTokenReference;

public BearerAuthenticationParametersDpo(
@JsonProperty(value = "authenticationTypeCode", required = true) int authenticationTypeCode,
@JsonProperty(value = "bearerTokenReference", required = true) @Nonnull
UserSecretReference bearerTokenReference) {
super(authenticationTypeCode);
this.bearerTokenReference = bearerTokenReference;
}

public @Nonnull UserSecretReference getBearerTokenReference() {
return bearerTokenReference;
}

@Override
public @Nonnull Map<String, String> asIcebergCatalogProperties(
UserSecretsManager secretsManager) {
String bearerToken = secretsManager.readSecret(getBearerTokenReference());
return Map.of(OAuth2Properties.TOKEN, bearerToken);
}

@Override
public AuthenticationParameters asAuthenticationParametersModel() {
return BearerAuthenticationParameters.builder()
.setAuthenticationType(AuthenticationParameters.AuthenticationTypeEnum.BEARER)
.build();
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("authenticationTypeCode", getAuthenticationTypeCode())
.add("bearerTokenReference", getBearerTokenReference())
.toString();
}
}
Loading