Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -248,12 +248,12 @@ public void catalogMaintenance() {
// 8 stale objects:
// - 1 namespace (catalog state)
// - 1 table (catalog state)
// - 1 grants (realm setup) - including 1 ACLs
// - 2 grants (realm setup) - including 2 ACLs
// - 1 principal
// - 1 principal role
// - 1 catalog role
// - 1 catalog
Optional.of(8L));
Optional.of(10L));

checkEntities("real state after grace", entities);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,89 +92,6 @@ NoSqlMetaStore ms(PolarisCallContext callContext) {

// Realms

@Nonnull
@Override
public BaseResult bootstrapPolarisService(@Nonnull PolarisCallContext callCtx) {
bootstrapPolarisServiceInternal(ms(callCtx));
return new BaseResult(BaseResult.ReturnStatus.SUCCESS);
}

Optional<CreatePrincipalResult> bootstrapPolarisServiceInternal(NoSqlMetaStore ms) {
// This function is idempotent, already existing entities will not be created again.

// Create the root-container, if not already present
var rootContainer =
ms.lookupRoot()
.orElseGet(
() -> {
var newRoot =
new PolarisBaseEntity(
PolarisEntityConstants.getNullId(),
PolarisEntityConstants.getRootEntityId(),
PolarisEntityType.ROOT,
PolarisEntitySubType.NULL_SUBTYPE,
PolarisEntityConstants.getRootEntityId(),
PolarisEntityConstants.getRootContainerName());
ms.createEntity(newRoot);
return newRoot;
});

// Create the root-principal, if not already present
var rootPrincipal =
ms.lookupEntityByName(
0L,
0L,
PolarisEntityType.PRINCIPAL.getCode(),
PolarisEntityConstants.getRootPrincipalName());
var createPrincipalResult = Optional.<CreatePrincipalResult>empty();
if (rootPrincipal == null) {
var rootPrincipalId = ms.generateNewId();
rootPrincipal =
new PolarisBaseEntity(
PolarisEntityConstants.getNullId(),
rootPrincipalId,
PolarisEntityType.PRINCIPAL,
PolarisEntitySubType.NULL_SUBTYPE,
PolarisEntityConstants.getRootEntityId(),
PolarisEntityConstants.getRootPrincipalName());

createPrincipalResult = Optional.of(ms.createPrincipal(rootPrincipal, rootCredentialsSet));
}

// Create the service-admin principal-role, if not already present
var serviceAdminPrincipalRole =
ms.lookupEntityByName(
0L,
0L,
PolarisEntityType.PRINCIPAL_ROLE.getCode(),
PolarisEntityConstants.getNameOfPrincipalServiceAdminRole());
if (serviceAdminPrincipalRole == null) {
// now create the account admin principal role
var serviceAdminPrincipalRoleId = ms.generateNewId();
serviceAdminPrincipalRole =
new PolarisBaseEntity(
PolarisEntityConstants.getNullId(),
serviceAdminPrincipalRoleId,
PolarisEntityType.PRINCIPAL_ROLE,
PolarisEntitySubType.NULL_SUBTYPE,
PolarisEntityConstants.getRootEntityId(),
PolarisEntityConstants.getNameOfPrincipalServiceAdminRole());
ms.createEntity(serviceAdminPrincipalRole);
}

// Persisting already existing grants is an idempotent operation
ms.persistGrantsOrRevokes(
true,
// we also need to grant usage on the account-admin principal to the principal
new SecurableGranteePrivilegeTuple(
serviceAdminPrincipalRole, rootPrincipal, PolarisPrivilege.PRINCIPAL_ROLE_USAGE),
// grant SERVICE_MANAGE_ACCESS on the rootContainer to the serviceAdminPrincipalRole
new SecurableGranteePrivilegeTuple(
rootContainer, serviceAdminPrincipalRole, PolarisPrivilege.SERVICE_MANAGE_ACCESS));

return createPrincipalResult;
}

@Nonnull
@Override
public BaseResult purge(@Nonnull PolarisCallContext callCtx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static org.apache.polaris.core.auth.AuthBootstrapUtil.createPolarisPrincipalForRealm;
import static org.apache.polaris.persistence.nosql.coretypes.refs.References.realmReferenceNames;
import static org.apache.polaris.persistence.nosql.realms.api.RealmDefinition.RealmStatus.ACTIVE;
import static org.apache.polaris.persistence.nosql.realms.api.RealmDefinition.RealmStatus.CREATED;
Expand All @@ -37,21 +38,16 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.polaris.core.PolarisCallContext;
import org.apache.polaris.core.PolarisDiagnostics;
import org.apache.polaris.core.config.RealmConfig;
import org.apache.polaris.core.context.RealmContext;
import org.apache.polaris.core.entity.PolarisBaseEntity;
import org.apache.polaris.core.entity.PolarisEntity;
import org.apache.polaris.core.entity.PolarisEntityConstants;
import org.apache.polaris.core.entity.PolarisEntityType;
import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
import org.apache.polaris.core.persistence.BasePersistence;
import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.persistence.bootstrap.RootCredentialsSet;
import org.apache.polaris.core.persistence.cache.EntityCache;
import org.apache.polaris.core.persistence.dao.entity.BaseResult;
import org.apache.polaris.core.persistence.dao.entity.CreatePrincipalResult;
import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult;
import org.apache.polaris.core.storage.PolarisStorageIntegrationProvider;
import org.apache.polaris.persistence.nosql.api.Persistence;
Expand Down Expand Up @@ -275,9 +271,8 @@ private PrincipalSecretsResult bootstrapRealm(
rootCredentialsSet,
clock);

var secretsResult =
bootstrapServiceAndCreatePolarisPrincipalForRealm(
realmId, metaStoreManager, metaStore, rootCredentialsSet);
PolarisCallContext ctx = new PolarisCallContext(() -> realmId, metaStore);
var secretsResult = createPolarisPrincipalForRealm(metaStoreManager, ctx);

realmManagement.update(
realmDesc, RealmDefinition.builder().from(realmDesc).status(ACTIVE).build());
Expand All @@ -286,70 +281,4 @@ private PrincipalSecretsResult bootstrapRealm(

return secretsResult;
}

/**
* This method bootstraps service for a given realm: i.e., creates all the required entities in
* the metastore and creates a root service principal. After that, we rotate the root principal
* credentials and print them to stdout
*/
private PrincipalSecretsResult bootstrapServiceAndCreatePolarisPrincipalForRealm(
String realmId,
NoSqlMetaStoreManager metaStoreManager,
NoSqlMetaStore metaStore,
RootCredentialsSet rootCredentialsSet) {
var createPrincipalResult = metaStoreManager.bootstrapPolarisServiceInternal(metaStore);

var rootPrincipal =
createPrincipalResult
.map(result -> (PolarisBaseEntity) result.getPrincipal())
.orElseGet(
() ->
metaStoreManager
.readEntityByName(
metaStore,
null,
PolarisEntityType.PRINCIPAL,
PolarisEntityConstants.getRootPrincipalName())
.getEntity());

var clientId =
PolarisEntity.of(rootPrincipal)
.getInternalPropertiesAsMap()
.get(PolarisEntityConstants.getClientIdPropertyName());
checkState(clientId != null, "Root principal has no client-ID");
var secrets = metaStore.loadPrincipalSecrets(clientId);

var principalSecrets = createPrincipalResult.map(CreatePrincipalResult::getPrincipalSecrets);
if (principalSecrets.isPresent()) {
LOGGER.debug(
"Root principal created for realm '{}', directly returning credentials for client-ID '{}'",
realmId,
principalSecrets.get().getPrincipalClientId());
return new PrincipalSecretsResult(principalSecrets.get());
}

var providedCredentials = rootCredentialsSet.credentials().get(realmId);
if (providedCredentials != null) {
LOGGER.debug(
"Root principal for realm '{}' already exists, credentials provided externally, returning credentials for client-ID '{}'",
realmId,
providedCredentials.clientId());
return new PrincipalSecretsResult(
new PolarisPrincipalSecrets(
rootPrincipal.getId(),
providedCredentials.clientId(),
providedCredentials.clientSecret(),
providedCredentials.clientSecret()));
}

// Have to rotate the secrets to retain the idempotency of this function
var result =
metaStoreManager.rotatePrincipalSecrets(
metaStore, secrets.getPrincipalId(), false, secrets.getMainSecretHash());
LOGGER.debug(
"Rotating credentials for root principal for realm '{}', client-ID is '{}'",
realmId,
result.getPrincipalSecrets().getPrincipalClientId());
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.apache.polaris.persistence.relational.jdbc;

import static org.apache.polaris.core.auth.AuthBootstrapUtil.createPolarisPrincipalForRealm;

import io.smallrye.common.annotation.Identifier;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
Expand Down Expand Up @@ -181,8 +183,14 @@ public synchronized Map<String, PrincipalSecretsResult> bootstrapRealms(
}
initializeForRealm(
datasourceOperations, realmContext, bootstrapOptions.rootCredentialsSet());

PolarisMetaStoreManager metaStoreManager =
metaStoreManagerMap.get(realmContext.getRealmIdentifier());
BasePersistence metaStore = sessionSupplierMap.get(realmContext.getRealmIdentifier()).get();
PolarisCallContext polarisContext = new PolarisCallContext(realmContext, metaStore);

PrincipalSecretsResult secretsResult =
bootstrapServiceAndCreatePolarisPrincipalForRealm(realmContext);
createPolarisPrincipalForRealm(metaStoreManager, polarisContext);
results.put(realm, secretsResult);
}
}
Expand Down Expand Up @@ -244,36 +252,6 @@ public synchronized EntityCache getOrCreateEntityCache(
return entityCacheMap.get(realmContext.getRealmIdentifier());
}

/**
* This method bootstraps service for a given realm: i.e. creates all the needed entities in the
* metastore and creates a root service principal.
*/
private PrincipalSecretsResult bootstrapServiceAndCreatePolarisPrincipalForRealm(
RealmContext realmContext) {
// While bootstrapping we need to act as a fake privileged context since the real
// CallContext may not have been resolved yet.
PolarisMetaStoreManager metaStoreManager =
metaStoreManagerMap.get(realmContext.getRealmIdentifier());
BasePersistence metaStore = sessionSupplierMap.get(realmContext.getRealmIdentifier()).get();
PolarisCallContext polarisContext = new PolarisCallContext(realmContext, metaStore);

Optional<PrincipalEntity> preliminaryRootPrincipal =
metaStoreManager.findRootPrincipal(polarisContext);
if (preliminaryRootPrincipal.isPresent()) {
String overrideMessage =
"It appears this metastore manager has already been bootstrapped. "
+ "To continue bootstrapping, please first purge the metastore with the `purge` command.";
LOGGER.error("\n\n {} \n\n", overrideMessage);
throw new IllegalArgumentException(overrideMessage);
}

metaStoreManager.bootstrapPolarisService(polarisContext);

PrincipalEntity rootPrincipal =
metaStoreManager.findRootPrincipal(polarisContext).orElseThrow();
return metaStoreManager.loadPrincipalSecrets(polarisContext, rootPrincipal.getClientId());
}

/**
* In this method we check if Service was bootstrapped for a given realm, i.e. that all the
* entities were created (root principal, root principal role, etc) If service was not
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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.auth;

import static com.google.common.base.Preconditions.checkState;

import com.google.common.base.Preconditions;
import java.util.Optional;
import org.apache.polaris.core.PolarisCallContext;
import org.apache.polaris.core.entity.PolarisBaseEntity;
import org.apache.polaris.core.entity.PolarisEntityConstants;
import org.apache.polaris.core.entity.PolarisEntitySubType;
import org.apache.polaris.core.entity.PolarisEntityType;
import org.apache.polaris.core.entity.PolarisPrivilege;
import org.apache.polaris.core.entity.PrincipalEntity;
import org.apache.polaris.core.entity.PrincipalRoleEntity;
import org.apache.polaris.core.persistence.PolarisMetaStoreManager;
import org.apache.polaris.core.persistence.dao.entity.CreatePrincipalResult;
import org.apache.polaris.core.persistence.dao.entity.GenerateEntityIdResult;
import org.apache.polaris.core.persistence.dao.entity.PrincipalSecretsResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Utility class for sharing root Polaris Principal setup code among all persistence
* implementations.
*
* <p>Note: this class is not meant to be reused outside of Polaris code.
*/
public class AuthBootstrapUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthBootstrapUtil.class);

private AuthBootstrapUtil() {}

public static PrincipalSecretsResult createPolarisPrincipalForRealm(
PolarisMetaStoreManager metaStoreManager, PolarisCallContext ctx) {

Optional<PrincipalEntity> preliminaryRootPrincipal = metaStoreManager.findRootPrincipal(ctx);
if (preliminaryRootPrincipal.isPresent()) {
String overrideMessage =
"It appears this metastore manager has already been bootstrapped. "
+ "To continue bootstrapping, please first purge the metastore with the `purge` command.";
LOGGER.error("\n\n {} \n\n", overrideMessage);
throw new IllegalArgumentException(overrideMessage);
}

// Create a root container entity that can represent the securable for any top-level grants.
PolarisBaseEntity rootContainer =
new PolarisBaseEntity(
PolarisEntityConstants.getNullId(),
PolarisEntityConstants.getRootEntityId(),
PolarisEntityType.ROOT,
PolarisEntitySubType.NULL_SUBTYPE,
PolarisEntityConstants.getRootEntityId(),
PolarisEntityConstants.getRootContainerName());
metaStoreManager.createEntityIfNotExists(ctx, null, rootContainer);

CreatePrincipalResult principalResult =
metaStoreManager.createPrincipal(
ctx,
new PrincipalEntity.Builder()
.setId(generateId(metaStoreManager, ctx))
.setName(PolarisEntityConstants.getRootPrincipalName())
.setCreateTimestamp(System.currentTimeMillis())
.build());
checkState(principalResult.isSuccess(), "Unable to create root principal");
PrincipalEntity rootPrincipal = principalResult.getPrincipal();

// now create the account admin principal role
PrincipalRoleEntity serviceAdminPrincipalRole =
new PrincipalRoleEntity.Builder()
.setId(generateId(metaStoreManager, ctx))
.setName(PolarisEntityConstants.getNameOfPrincipalServiceAdminRole())
.setCreateTimestamp(System.currentTimeMillis())
.build();
metaStoreManager.createEntityIfNotExists(ctx, null, serviceAdminPrincipalRole);

// we also need to grant usage on the account-admin principal to the principal
metaStoreManager.grantPrivilegeOnSecurableToRole(
ctx, rootPrincipal, null, serviceAdminPrincipalRole, PolarisPrivilege.PRINCIPAL_ROLE_USAGE);

// grant SERVICE_MANAGE_ACCESS on the rootContainer to the serviceAdminPrincipalRole
metaStoreManager.grantPrivilegeOnSecurableToRole(
ctx,
serviceAdminPrincipalRole,
null,
rootContainer,
PolarisPrivilege.SERVICE_MANAGE_ACCESS);

return metaStoreManager.loadPrincipalSecrets(ctx, rootPrincipal.getClientId());
}

private static long generateId(PolarisMetaStoreManager metaStoreManager, PolarisCallContext ctx) {
GenerateEntityIdResult res = metaStoreManager.generateNewEntityId(ctx);
Preconditions.checkState(res.isSuccess(), "Unable to generate id for polaris entity");
return res.getId();
}
}
Loading