http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java deleted file mode 100644 index 1c4bb37..0000000 --- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java +++ /dev/null @@ -1,4797 +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.sentry.provider.db.service.persistent; - -import static org.apache.sentry.core.common.utils.SentryConstants.AUTHORIZABLE_JOINER; -import static org.apache.sentry.core.common.utils.SentryConstants.KV_JOINER; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import javax.jdo.FetchGroup; -import javax.jdo.JDODataStoreException; -import javax.jdo.JDOHelper; -import javax.jdo.PersistenceManager; -import javax.jdo.PersistenceManagerFactory; -import javax.jdo.Query; - -import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.conf.Configuration; -import org.apache.sentry.core.common.exception.SentryAccessDeniedException; -import org.apache.sentry.core.common.exception.SentryAlreadyExistsException; -import org.apache.sentry.core.common.exception.SentryGrantDeniedException; -import org.apache.sentry.core.common.exception.SentryInvalidInputException; -import org.apache.sentry.core.common.exception.SentryNoSuchObjectException; -import org.apache.sentry.core.common.exception.SentrySiteConfigurationException; -import org.apache.sentry.core.common.exception.SentryUserException; -import org.apache.sentry.core.common.utils.PathUtils; -import org.apache.sentry.core.common.utils.SentryConstants; -import org.apache.sentry.core.model.db.AccessConstants; -import org.apache.sentry.core.model.db.DBModelAuthorizable.AuthorizableType; -import org.apache.sentry.hdfs.PathsUpdate; -import org.apache.sentry.hdfs.UniquePathsUpdate; -import org.apache.sentry.hdfs.UpdateableAuthzPaths; -import org.apache.sentry.hdfs.service.thrift.TPrivilegeEntityType; -import org.apache.sentry.provider.db.service.model.MAuthzPathsMapping; -import org.apache.sentry.provider.db.service.model.MAuthzPathsSnapshotId; -import org.apache.sentry.provider.db.service.model.MSentryChange; -import org.apache.sentry.provider.db.service.model.MSentryGroup; -import org.apache.sentry.provider.db.service.model.MSentryHmsNotification; -import org.apache.sentry.provider.db.service.model.MSentryPathChange; -import org.apache.sentry.provider.db.service.model.MSentryPermChange; -import org.apache.sentry.provider.db.service.model.MSentryPrivilege; -import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; -import org.apache.sentry.provider.db.service.model.MSentryRole; -import org.apache.sentry.provider.db.service.model.MSentryUser; -import org.apache.sentry.provider.db.service.model.MSentryVersion; -import org.apache.sentry.provider.db.service.model.MSentryUtil; -import org.apache.sentry.provider.db.service.model.MPath; -import org.apache.sentry.hdfs.service.thrift.TPrivilegeEntity; -import org.apache.sentry.api.common.ApiConstants.PrivilegeScope; -import org.apache.sentry.api.service.thrift.SentryPolicyStoreProcessor; -import org.apache.sentry.api.service.thrift.TSentryActiveRoleSet; -import org.apache.sentry.api.service.thrift.TSentryAuthorizable; -import org.apache.sentry.api.service.thrift.TSentryGrantOption; -import org.apache.sentry.api.service.thrift.TSentryGroup; -import org.apache.sentry.api.service.thrift.TSentryMappingData; -import org.apache.sentry.api.service.thrift.TSentryPrivilege; -import org.apache.sentry.api.service.thrift.TSentryPrivilegeMap; -import org.apache.sentry.api.service.thrift.TSentryRole; -import org.apache.sentry.service.common.ServiceConstants.SentryEntityType; -import org.apache.sentry.service.common.ServiceConstants.ServerConfig; -import org.datanucleus.store.rdbms.exceptions.MissingTableException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.codahale.metrics.Gauge; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import static org.apache.sentry.hdfs.Updateable.Update; -import static org.apache.sentry.provider.db.service.persistent.QueryParamBuilder.newQueryParamBuilder; - -/** - * SentryStore is the data access object for Sentry data. Strings - * such as role and group names will be normalized to lowercase - * in addition to starting and ending whitespace. - * <p> - * We have several places where we rely on transactions to support - * read/modify/write semantics for incrementing IDs. - * This works but using DB support is rather expensive and we can - * user in-core serializations to help with this a least within a - * single node and rely on DB for multi-node synchronization. - * <p> - * This isn't much of a problem for path updates since they are - * driven by HMSFollower which usually runs on a single leader - * node, but permission updates originate from clients - * directly and may be highly concurrent. - * <p> - * We are internally serializing all permissions update anyway, so doing - * partial serialization on every node helps. For this reason all - * SentryStore calls that affect permission deltas are serialized. - * <p> - * See <a href="https://issues.apache.org/jira/browse/SENTRY-1824">SENTRY-1824</a> - * for more detail. - */ -public class SentryStore { - private static final Logger LOGGER = LoggerFactory - .getLogger(SentryStore.class); - - public static final String NULL_COL = "__NULL__"; - public static final int INDEX_GROUP_ROLES_MAP = 0; - public static final int INDEX_USER_ROLES_MAP = 1; - - // String constants for field names - public static final String SERVER_NAME = "serverName"; - public static final String DB_NAME = "dbName"; - public static final String TABLE_NAME = "tableName"; - public static final String COLUMN_NAME = "columnName"; - public static final String ACTION = "action"; - public static final String URI = "URI"; - public static final String GRANT_OPTION = "grantOption"; - public static final String ROLE_NAME = "roleName"; - - // Initial change ID for permission/path change. Auto increment - // is starting from 1. - public static final long INIT_CHANGE_ID = 1L; - - private static final long EMPTY_CHANGE_ID = 0L; - - public static final long EMPTY_NOTIFICATION_ID = 0L; - - // Representation for empty HMS snapshots not found on MAuthzPathsSnapshotId - public static final long EMPTY_PATHS_SNAPSHOT_ID = 0L; - - // For counters, representation of the "unknown value" - private static final long COUNT_VALUE_UNKNOWN = -1L; - - // Representation for unknown HMS notification ID - private static final long NOTIFICATION_UNKNOWN = -1L; - - private static final String EMPTY_GRANTOR_PRINCIPAL = "--"; - - - private static final Set<String> ALL_ACTIONS = Sets.newHashSet( - AccessConstants.ALL, AccessConstants.ACTION_ALL, - AccessConstants.SELECT, AccessConstants.INSERT, AccessConstants.ALTER, - AccessConstants.CREATE, AccessConstants.DROP, AccessConstants.INDEX, - AccessConstants.LOCK); - - // Now partial revoke just support action with SELECT,INSERT and ALL. - // Now partial revoke just support action with SELECT,INSERT, and ALL. - // e.g. If we REVOKE SELECT from a privilege with action ALL, it will leads to INSERT - // e.g. If we REVOKE SELECT from a privilege with action ALL, it will leads to others individual - // Otherwise, if we revoke other privilege(e.g. ALTER,DROP...), we will remove it from a role directly. - private static final Set<String> PARTIAL_REVOKE_ACTIONS = Sets.newHashSet(AccessConstants.ALL, - AccessConstants.ACTION_ALL.toLowerCase(), AccessConstants.SELECT, AccessConstants.INSERT); - - // Datanucleus property controlling whether query results are loaded at commit time - // to make query usable post-commit - private static final String LOAD_RESULTS_AT_COMMIT = "datanucleus.query.loadResultsAtCommit"; - - private final PersistenceManagerFactory pmf; - private Configuration conf; - private final TransactionManager tm; - - // When it is true, execute DeltaTransactionBlock to persist delta changes. - // When it is false, do not execute DeltaTransactionBlock - private boolean persistUpdateDeltas; - - /** - * counterWait is used to synchronize notifications between Thrift and HMSFollower. - * Technically it doesn't belong here, but the only thing that connects HMSFollower - * and Thrift API is SentryStore. An alternative could be a singleton CounterWait or - * some factory that returns CounterWait instances keyed by name, but this complicates - * things unnecessary. - * <p> - * Keeping it here isn't ideal but serves the purpose until we find a better home. - */ - private final CounterWait counterWait; - - public static Properties getDataNucleusProperties(Configuration conf) - throws SentrySiteConfigurationException, IOException { - Properties prop = new Properties(); - prop.putAll(ServerConfig.SENTRY_STORE_DEFAULTS); - String jdbcUrl = conf.get(ServerConfig.SENTRY_STORE_JDBC_URL, "").trim(); - Preconditions.checkArgument(!jdbcUrl.isEmpty(), "Required parameter " + - ServerConfig.SENTRY_STORE_JDBC_URL + " is missed"); - String user = conf.get(ServerConfig.SENTRY_STORE_JDBC_USER, ServerConfig. - SENTRY_STORE_JDBC_USER_DEFAULT).trim(); - //Password will be read from Credential provider specified using property - // CREDENTIAL_PROVIDER_PATH("hadoop.security.credential.provider.path" in sentry-site.xml - // it falls back to reading directly from sentry-site.xml - char[] passTmp = conf.getPassword(ServerConfig.SENTRY_STORE_JDBC_PASS); - if (passTmp == null) { - throw new SentrySiteConfigurationException("Error reading " + - ServerConfig.SENTRY_STORE_JDBC_PASS); - } - String pass = new String(passTmp); - - String driverName = conf.get(ServerConfig.SENTRY_STORE_JDBC_DRIVER, - ServerConfig.SENTRY_STORE_JDBC_DRIVER_DEFAULT); - prop.setProperty(ServerConfig.JAVAX_JDO_URL, jdbcUrl); - prop.setProperty(ServerConfig.JAVAX_JDO_USER, user); - prop.setProperty(ServerConfig.JAVAX_JDO_PASS, pass); - prop.setProperty(ServerConfig.JAVAX_JDO_DRIVER_NAME, driverName); - - /* - * Oracle doesn't support "repeatable-read" isolation level and testing - * showed issues with "serializable" isolation level for Oracle 12, - * so we use "read-committed" instead. - * - * JDBC URL always looks like jdbc:oracle:<drivertype>:@<database> - * we look at the second component. - * - * The isolation property can be overwritten via configuration property. - */ - final String oracleDb = "oracle"; - if (prop.getProperty(ServerConfig.DATANUCLEUS_ISOLATION_LEVEL, ""). - equals(ServerConfig.DATANUCLEUS_REPEATABLE_READ) && - jdbcUrl.contains(oracleDb)) { - String[] parts = jdbcUrl.split(":"); - if ((parts.length > 1) && parts[1].equals(oracleDb)) { - // For Oracle JDBC driver, replace "repeatable-read" with "read-committed" - prop.setProperty(ServerConfig.DATANUCLEUS_ISOLATION_LEVEL, - "read-committed"); - } - } - - for (Map.Entry<String, String> entry : conf) { - String key = entry.getKey(); - if (key.startsWith(ServerConfig.SENTRY_JAVAX_JDO_PROPERTY_PREFIX) || - key.startsWith(ServerConfig.SENTRY_DATANUCLEUS_PROPERTY_PREFIX)) { - key = StringUtils.removeStart(key, ServerConfig.SENTRY_DB_PROPERTY_PREFIX); - prop.setProperty(key, entry.getValue()); - } - } - // Disallow operations outside of transactions - prop.setProperty("datanucleus.NontransactionalRead", "false"); - prop.setProperty("datanucleus.NontransactionalWrite", "false"); - return prop; - } - - public SentryStore(Configuration conf) throws Exception { - this.conf = conf; - Properties prop = getDataNucleusProperties(conf); - boolean checkSchemaVersion = conf.get( - ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, - ServerConfig.SENTRY_VERIFY_SCHEM_VERSION_DEFAULT).equalsIgnoreCase( - "true"); - - // Schema verification should be set to false only for testing. - // If it is set to false, appropriate datanucleus properties will be set so that - // database schema is automatically created. This is desirable only for running tests. - // Sentry uses <code>SentrySchemaTool</code> to create schema with the help of sql scripts. - - if (!checkSchemaVersion) { - prop.setProperty("datanucleus.schema.autoCreateAll", "true"); - } - pmf = JDOHelper.getPersistenceManagerFactory(prop); - tm = new TransactionManager(pmf, conf); - verifySentryStoreSchema(checkSchemaVersion); - long notificationTimeout = conf.getInt(ServerConfig.SENTRY_NOTIFICATION_SYNC_TIMEOUT_MS, - ServerConfig.SENTRY_NOTIFICATION_SYNC_TIMEOUT_DEFAULT); - counterWait = new CounterWait(notificationTimeout, TimeUnit.MILLISECONDS); - } - - public void setPersistUpdateDeltas(boolean persistUpdateDeltas) { - this.persistUpdateDeltas = persistUpdateDeltas; - } - - - public TransactionManager getTransactionManager() { - return tm; - } - - public CounterWait getCounterWait() { - return counterWait; - } - - // ensure that the backend DB schema is set - void verifySentryStoreSchema(boolean checkVersion) throws Exception { - if (!checkVersion) { - setSentryVersion(SentryStoreSchemaInfo.getSentryVersion(), - "Schema version set implicitly"); - } else { - String currentVersion = getSentryVersion(); - if (!SentryStoreSchemaInfo.getSentryVersion().equals(currentVersion)) { - throw new SentryAccessDeniedException( - "The Sentry store schema version " + currentVersion - + " is different from distribution version " - + SentryStoreSchemaInfo.getSentryVersion()); - } - } - } - - public synchronized void stop() { - if (pmf != null) { - pmf.close(); - } - } - - /** - * Get a single role with the given name inside a transaction - * @param pm Persistence Manager instance - * @param roleName Role name (should not be null) - * @return single role with the given name - */ - public MSentryRole getRole(PersistenceManager pm, String roleName) { - Query query = pm.newQuery(MSentryRole.class); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - query.setFilter("this.roleName == :roleName"); - query.setUnique(true); - return (MSentryRole) query.execute(roleName); - } - - /** - * Get list of all roles. Should be called inside transaction. - * @param pm Persistence manager instance - * @return List of all roles - */ - @SuppressWarnings("unchecked") - private List<MSentryRole> getAllRoles(PersistenceManager pm) { - Query query = pm.newQuery(MSentryRole.class); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - return (List<MSentryRole>) query.execute(); - } - - /** - * Get a single user with the given name inside a transaction - * @param pm Persistence Manager instance - * @param userName User name (should not be null) - * @return single user with the given name - */ - public MSentryUser getUser(PersistenceManager pm, String userName) { - Query query = pm.newQuery(MSentryUser.class); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - query.setFilter("this.userName == :userName"); - query.setUnique(true); - return (MSentryUser) query.execute(userName); - } - - /** - * Create a sentry user and persist it. User name is the primary key for the - * user, so an attempt to create a user which exists fails with JDO exception. - * - * @param userName: Name of the user being persisted. - * The name is normalized. - * @throws Exception - */ - public void createSentryUser(final String userName) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedUserName = trimAndLower(userName); - if (getUser(pm, trimmedUserName) != null) { - throw new SentryAlreadyExistsException("User: " + trimmedUserName); - } - pm.makePersistent( - new MSentryUser(trimmedUserName, System.currentTimeMillis(), Sets.newHashSet())); - return null; - }); - } - - /** - * Normalize the string values - remove leading and trailing whitespaces and - * convert to lower case - * @return normalized input - */ - private String trimAndLower(String input) { - return input.trim().toLowerCase(); - } - - /** - * Create a sentry role and persist it. Role name is the primary key for the - * role, so an attempt to create a role which exists fails with JDO exception. - * - * @param roleName: Name of the role being persisted. - * The name is normalized. - * @throws Exception - */ - public void createSentryRole(final String roleName) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = trimAndLower(roleName); - if (getRole(pm, trimmedRoleName) != null) { - throw new SentryAlreadyExistsException("Role: " + trimmedRoleName); - } - pm.makePersistent(new MSentryRole(trimmedRoleName)); - return null; - }); - } - - /** - * Get count of object of the given class - * @param tClass Class to count - * @param <T> Class type - * @return count of objects or -1 in case of error - */ - private <T> Long getCount(final Class<T> tClass) { - try { - return tm.executeTransaction( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - Query query = pm.newQuery(); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - query.setClass(tClass); - query.setResult("count(this)"); - Long result = (Long)query.execute(); - return result; - }); - } catch (Exception e) { - return COUNT_VALUE_UNKNOWN; - } - } - - /** - * @return number of roles - */ - public Gauge<Long> getRoleCountGauge() { - return () -> getCount(MSentryRole.class); - } - - /** - * @return Number of privileges - */ - public Gauge<Long> getPrivilegeCountGauge() { - return () -> getCount(MSentryPrivilege.class); - } - - /** - * @return number of groups - */ - public Gauge<Long> getGroupCountGauge() { - return () -> getCount(MSentryGroup.class); - } - - /** - * @return Number of users - */ - Gauge<Long> getUserCountGauge() { - return () -> getCount(MSentryUser.class); - } - - /** - * @return number of threads waiting for HMS notifications to be processed - */ - public Gauge<Integer> getHMSWaitersCountGauge() { - return () -> counterWait.waitersCount(); - } - - /** - * @return current value of last processed notification ID - */ - public Gauge<Long> getLastNotificationIdGauge() { - return () -> { - try { - return getLastProcessedNotificationID(); - } catch (Exception e) { - LOGGER.error("Can not read current notificationId", e); - return NOTIFICATION_UNKNOWN; - } - }; - } - - /** - * @return ID of the path snapshot - */ - public Gauge<Long> getLastPathsSnapshotIdGauge() { - return () -> { - try { - return getCurrentAuthzPathsSnapshotID(); - } catch (Exception e) { - LOGGER.error("Can not read current paths snapshot ID", e); - return NOTIFICATION_UNKNOWN; - } - }; - } - - /** - * @return Permissions change ID - */ - public Gauge<Long> getPermChangeIdGauge() { - return new Gauge<Long>() { - @Override - public Long getValue() { - try { - return tm.executeTransaction( - pm -> getLastProcessedChangeIDCore(pm, MSentryPermChange.class) - ); - } catch (Exception e) { - LOGGER.error("Can not read current permissions change ID", e); - return NOTIFICATION_UNKNOWN; - } - } - }; - } - - /** - * @return Path change id - */ - public Gauge<Long> getPathChangeIdGauge() { - return () -> { - try { - return tm.executeTransaction( - pm -> getLastProcessedChangeIDCore(pm, MSentryPathChange.class) - ); - } catch (Exception e) { - LOGGER.error("Can not read current path change ID", e); - return NOTIFICATION_UNKNOWN; - } - }; - } - - /** - * Lets the test code know how many privs are in the db, so that we know - * if they are in fact being cleaned up when not being referenced any more. - * @return The number of rows in the db priv table. - */ - @VisibleForTesting - long countMSentryPrivileges() { - return getCount(MSentryPrivilege.class); - } - - @VisibleForTesting - void clearAllTables() { - try { - tm.executeTransaction( - pm -> { - pm.newQuery(MSentryRole.class).deletePersistentAll(); - pm.newQuery(MSentryGroup.class).deletePersistentAll(); - pm.newQuery(MSentryUser.class).deletePersistentAll(); - pm.newQuery(MSentryPrivilege.class).deletePersistentAll(); - pm.newQuery(MSentryPermChange.class).deletePersistentAll(); - pm.newQuery(MSentryPathChange.class).deletePersistentAll(); - pm.newQuery(MAuthzPathsMapping.class).deletePersistentAll(); - pm.newQuery(MPath.class).deletePersistentAll(); - pm.newQuery(MSentryHmsNotification.class).deletePersistentAll(); - pm.newQuery(MAuthzPathsSnapshotId.class).deletePersistentAll(); - return null; - }); - } catch (Exception e) { - // the method only for test, log the error and ignore the exception - LOGGER.error(e.getMessage(), e); - } - } - - /** - * Removes all the information related to HMS Objects from sentry store. - */ - @VisibleForTesting - public void clearHmsPathInformation() throws Exception { - tm.executeTransactionWithRetry( - pm -> { - // Data in MAuthzPathsSnapshotId.class is not cleared intentionally. - // This data will help sentry retain the history of snapshots taken before - // and help in picking appropriate ID even when hdfs sync is enabled/disabled. - pm.newQuery(MSentryPathChange.class).deletePersistentAll(); - pm.newQuery(MAuthzPathsMapping.class).deletePersistentAll(); - pm.newQuery(MPath.class).deletePersistentAll(); - return null; - }); - } - - /** - * Purge a given delta change table, with a specified number of changes to be kept. - * - * @param cls the class of a perm/path delta change {@link MSentryPermChange} or - * {@link MSentryPathChange}. - * @param pm a {@link PersistenceManager} instance. - * @param changesToKeep the number of changes the caller want to keep. - * @param <T> the type of delta change class. - */ - @VisibleForTesting - <T extends MSentryChange> void purgeDeltaChangeTableCore( - Class<T> cls, PersistenceManager pm, long changesToKeep) { - Preconditions.checkArgument(changesToKeep >= 0, - "changes to keep must be a non-negative number"); - long lastChangedID = getLastProcessedChangeIDCore(pm, cls); - long maxIDDeleted = lastChangedID - changesToKeep; - - Query query = pm.newQuery(cls); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - - // It is an approximation of "SELECT ... LIMIT CHANGE_TO_KEEP" in SQL, because JDO w/ derby - // does not support "LIMIT". - // See: http://www.datanucleus.org/products/datanucleus/jdo/jdoql_declarative.html - query.setFilter("changeID <= maxChangedIdDeleted"); - query.declareParameters("long maxChangedIdDeleted"); - long numDeleted = query.deletePersistentAll(maxIDDeleted); - if (numDeleted > 0) { - LOGGER.info(String.format("Purged %d of %s to changeID=%d", - numDeleted, cls.getSimpleName(), maxIDDeleted)); - } - } - - /** - * Purge notification id table, keeping a specified number of entries. - * @param pm a {@link PersistenceManager} instance. - * @param changesToKeep the number of changes the caller want to keep. - */ - @VisibleForTesting - protected void purgeNotificationIdTableCore(PersistenceManager pm, - long changesToKeep) { - Preconditions.checkArgument(changesToKeep > 0, - "You need to keep at least one entry in SENTRY_HMS_NOTIFICATION_ID table"); - long lastNotificationID = getLastProcessedNotificationIDCore(pm); - Query query = pm.newQuery(MSentryHmsNotification.class); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - - // It is an approximation of "SELECT ... LIMIT CHANGE_TO_KEEP" in SQL, because JDO w/ derby - // does not support "LIMIT". - // See: http://www.datanucleus.org/products/datanucleus/jdo/jdoql_declarative.html - query.setFilter("notificationId <= maxNotificationIdDeleted"); - query.declareParameters("long maxNotificationIdDeleted"); - long numDeleted = query.deletePersistentAll(lastNotificationID - changesToKeep); - if (numDeleted > 0) { - LOGGER.info("Purged {} of {}", numDeleted, MSentryHmsNotification.class.getSimpleName()); - } - } - - /** - * Purge delta change tables, {@link MSentryPermChange} and {@link MSentryPathChange}. - * The number of deltas to keep is configurable - */ - public void purgeDeltaChangeTables() { - final int changesToKeep = conf.getInt(ServerConfig.SENTRY_DELTA_KEEP_COUNT, - ServerConfig.SENTRY_DELTA_KEEP_COUNT_DEFAULT); - LOGGER.info("Purging MSentryPathUpdate and MSentyPermUpdate tables, leaving {} entries", - changesToKeep); - try { - tm.executeTransaction(pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - purgeDeltaChangeTableCore(MSentryPermChange.class, pm, changesToKeep); - LOGGER.info("MSentryPermChange table has been purged."); - purgeDeltaChangeTableCore(MSentryPathChange.class, pm, changesToKeep); - LOGGER.info("MSentryPathUpdate table has been purged."); - return null; - }); - } catch (Exception e) { - LOGGER.error("Delta change cleaning process encountered an error", e); - } - } - - /** - * Purge hms notification id table , {@link MSentryHmsNotification}. - * The number of notifications id's to be kept is based on configuration - * sentry.server.delta.keep.count - */ - public void purgeNotificationIdTable() { - final int changesToKeep = conf.getInt(ServerConfig.SENTRY_HMS_NOTIFICATION_ID_KEEP_COUNT, - ServerConfig.SENTRY_HMS_NOTIFICATION_ID_KEEP_COUNT_DEFAULT); - LOGGER.debug("Purging MSentryHmsNotification table, leaving {} entries", - changesToKeep); - try { - tm.executeTransaction(pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - purgeNotificationIdTableCore(pm, changesToKeep); - return null; - }); - } catch (Exception e) { - LOGGER.error("MSentryHmsNotification cleaning process encountered an error", e); - } - } - /** - * Alter a given sentry role to grant a privilege. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param privilege the given privilege - * @throws Exception - */ - void alterSentryRoleGrantPrivilege(final String grantorPrincipal, - final String roleName, final TSentryPrivilege privilege) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = trimAndLower(roleName); - // first do grant check - grantOptionCheck(pm, grantorPrincipal, privilege); - - // Alter sentry Role and grant Privilege. - MSentryPrivilege mPrivilege = alterSentryRoleGrantPrivilegeCore( - pm, trimmedRoleName, privilege); - - if (mPrivilege != null) { - // update the privilege to be the one actually updated. - convertToTSentryPrivilege(mPrivilege, privilege); - } - return null; - }); - } - - /** - * Alter a given sentry role to grant a set of privileges. - * Internally calls alterSentryRoleGrantPrivilege. - * - * @param grantorPrincipal User name - * @param roleName Role name - * @param privileges Set of privileges - * @throws Exception - */ - public void alterSentryRoleGrantPrivileges(final String grantorPrincipal, - final String roleName, final Set<TSentryPrivilege> privileges) throws Exception { - for (TSentryPrivilege privilege : privileges) { - alterSentryRoleGrantPrivilege(grantorPrincipal, roleName, privilege); - } - } - - /** - * Alter a given sentry role to grant a privilege, as well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param privilege the given privilege - * @param update the corresponding permission delta update. - * @throws Exception - * - */ - synchronized void alterSentryRoleGrantPrivilege(final String grantorPrincipal, - final String roleName, final TSentryPrivilege privilege, - final Update update) throws Exception { - - execute(update, pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = trimAndLower(roleName); - // first do grant check - grantOptionCheck(pm, grantorPrincipal, privilege); - - // Alter sentry Role and grant Privilege. - MSentryPrivilege mPrivilege = alterSentryRoleGrantPrivilegeCore(pm, - trimmedRoleName, privilege); - - if (mPrivilege != null) { - // update the privilege to be the one actually updated. - convertToTSentryPrivilege(mPrivilege, privilege); - } - return null; - }); - } - - /** - * Alter a given sentry role to grant a set of privileges, as well as persist the - * corresponding permission change to MSentryPermChange table in a single transaction. - * Internally calls alterSentryRoleGrantPrivilege. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param privileges a Set of privileges - * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map - * @throws Exception - * - */ - public void alterSentryRoleGrantPrivileges(final String grantorPrincipal, - final String roleName, final Set<TSentryPrivilege> privileges, - final Map<TSentryPrivilege, Update> privilegesUpdateMap) throws Exception { - - Preconditions.checkNotNull(privilegesUpdateMap); - for (TSentryPrivilege privilege : privileges) { - Update update = privilegesUpdateMap.get(privilege); - if (update != null) { - alterSentryRoleGrantPrivilege(grantorPrincipal, roleName, privilege, - update); - } else { - alterSentryRoleGrantPrivilege(grantorPrincipal, roleName, privilege); - } - } - } - - private MSentryPrivilege alterSentryRoleGrantPrivilegeCore(PersistenceManager pm, - String roleName, TSentryPrivilege privilege) - throws SentryNoSuchObjectException, SentryInvalidInputException { - MSentryPrivilege mPrivilege = null; - MSentryRole mRole = getRole(pm, roleName); - if (mRole == null) { - throw noSuchRole(roleName); - } - - if(privilege.getPrivilegeScope().equalsIgnoreCase(PrivilegeScope.URI.name()) - && StringUtils.isBlank(privilege.getURI())) { - throw new SentryInvalidInputException("cannot grant URI privileges to Null or EMPTY location"); - } - - if (!isNULL(privilege.getColumnName()) || !isNULL(privilege.getTableName()) - || !isNULL(privilege.getDbName())) { - // If Grant is for ALL and Either INSERT/SELECT already exists.. - // need to remove it and GRANT ALL.. - if (AccessConstants.ALL.equalsIgnoreCase(privilege.getAction()) - || AccessConstants.ACTION_ALL.equalsIgnoreCase(privilege.getAction())) { - TSentryPrivilege tNotAll = new TSentryPrivilege(privilege); - tNotAll.setAction(AccessConstants.SELECT); - MSentryPrivilege mSelect = getMSentryPrivilege(tNotAll, pm); - tNotAll.setAction(AccessConstants.INSERT); - MSentryPrivilege mInsert = getMSentryPrivilege(tNotAll, pm); - if ((mSelect != null) && mRole.getPrivileges().contains(mSelect)) { - mSelect.removeRole(mRole); - pm.makePersistent(mSelect); - } - if ((mInsert != null) && mRole.getPrivileges().contains(mInsert)) { - mInsert.removeRole(mRole); - pm.makePersistent(mInsert); - } - } else { - // If Grant is for Either INSERT/SELECT and ALL already exists.. - // do nothing.. - TSentryPrivilege tAll = new TSentryPrivilege(privilege); - tAll.setAction(AccessConstants.ALL); - MSentryPrivilege mAll1 = getMSentryPrivilege(tAll, pm); - tAll.setAction(AccessConstants.ACTION_ALL); - MSentryPrivilege mAll2 = getMSentryPrivilege(tAll, pm); - if (mAll1 != null && mRole.getPrivileges().contains(mAll1)) { - return null; - } - if (mAll2 != null && mRole.getPrivileges().contains(mAll2)) { - return null; - } - } - } - - mPrivilege = getMSentryPrivilege(privilege, pm); - if (mPrivilege == null) { - mPrivilege = convertToMSentryPrivilege(privilege); - } - mPrivilege.appendRole(mRole); - pm.makePersistent(mPrivilege); - return mPrivilege; - } - - /** - * Alter a given sentry user to grant a set of privileges. - * Internally calls alterSentryUserGrantPrivilege. - * - * @param grantorPrincipal User name - * @param userName User name - * @param privileges Set of privileges - * @throws Exception - */ - public void alterSentryUserGrantPrivileges(final String grantorPrincipal, - final String userName, final Set<TSentryPrivilege> privileges) throws Exception { - - try { - MSentryUser userEntry = getMSentryUserByName(userName, false); - if (userEntry == null) { - createSentryUser(userName); - } - } catch (SentryAlreadyExistsException e) { - // the user may be created by other thread, so swallow the exception and proceed - } - - for (TSentryPrivilege privilege : privileges) { - alterSentryUserGrantPrivilege(grantorPrincipal, userName, privilege); - } - } - - /** - * Alter a given sentry user to grant a privilege. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param privilege the given privilege - * @throws Exception - */ - void alterSentryUserGrantPrivilege(final String grantorPrincipal, - final String userName, final TSentryPrivilege privilege) throws Exception { - tm.executeTransactionWithRetry( - new TransactionBlock<Object>() { - public Object execute(PersistenceManager pm) throws Exception { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedUserName = trimAndLower(userName); - // first do grant check - grantOptionCheck(pm, grantorPrincipal, privilege); - - // Alter sentry User and grant Privilege. - MSentryPrivilege mPrivilege = alterSentryUserGrantPrivilegeCore( - pm, trimmedUserName, privilege); - - if (mPrivilege != null) { - // update the privilege to be the one actually updated. - convertToTSentryPrivilege(mPrivilege, privilege); - } - return null; - } - }); - } - - /** - * Alter a given sentry user to grant a privilege, as well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param privilege the given privilege - * @param update the corresponding permission delta update. - * @throws Exception - * - */ - synchronized void alterSentryUserGrantPrivilege(final String grantorPrincipal, - final String userName, final TSentryPrivilege privilege, - final Update update) throws Exception { - - execute(update, new TransactionBlock<Object>() { - public Object execute(PersistenceManager pm) throws Exception { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedUserName = trimAndLower(userName); - // first do grant check - grantOptionCheck(pm, grantorPrincipal, privilege); - - // Alter sentry User and grant Privilege. - MSentryPrivilege mPrivilege = alterSentryUserGrantPrivilegeCore(pm, - trimmedUserName, privilege); - - if (mPrivilege != null) { - // update the privilege to be the one actually updated. - convertToTSentryPrivilege(mPrivilege, privilege); - } - return null; - } - }); - } - - /** - * Alter a given sentry user to grant a set of privileges, as well as persist the - * corresponding permission change to MSentryPermChange table in a single transaction. - * Internally calls alterSentryUserGrantPrivilege. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param privileges a Set of privileges - * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map - * @throws Exception - * - */ - public void alterSentryUserGrantPrivileges(final String grantorPrincipal, - final String userName, final Set<TSentryPrivilege> privileges, - final Map<TSentryPrivilege, Update> privilegesUpdateMap) throws Exception { - - try { - MSentryUser userEntry = getMSentryUserByName(userName, false); - if (userEntry == null) { - createSentryUser(userName); - } - } catch (SentryAlreadyExistsException e) { - // the user may be created by other thread, so swallow the exception and proeed - } - - Preconditions.checkNotNull(privilegesUpdateMap); - for (TSentryPrivilege privilege : privileges) { - Update update = privilegesUpdateMap.get(privilege); - if (update != null) { - alterSentryUserGrantPrivilege(grantorPrincipal, userName, privilege, - update); - } else { - alterSentryUserGrantPrivilege(grantorPrincipal, userName, privilege); - } - } - } - - /** - * Get the user entry by user name - * @param userName the name of the user - * @return the user entry - * @throws Exception if the specified user does not exist - */ - @VisibleForTesting - public MSentryUser getMSentryUserByName(final String userName) throws Exception { - return getMSentryUserByName(userName, true); - } - - /** - * Get the user entry by user name - * @param userName the name of the user - * @param throwExceptionIfNotExist true: throw exception if user does not exist; false: return null - * @return the user entry or null - * @throws Exception if the specified user does not exist and throwExceptionIfNotExist is true - */ - MSentryUser getMSentryUserByName(final String userName, boolean throwExceptionIfNotExist) throws Exception { - return tm.executeTransaction( - new TransactionBlock<MSentryUser>() { - public MSentryUser execute(PersistenceManager pm) throws Exception { - String trimmedUserName = trimAndLower(userName); - MSentryUser sentryUser = getUser(pm, trimmedUserName); - if (sentryUser == null) { - if (throwExceptionIfNotExist) { - throw noSuchUser(trimmedUserName); - } - else { - return null; - } - } - return sentryUser; - } - }); - } - - private MSentryPrivilege alterSentryUserGrantPrivilegeCore(PersistenceManager pm, - String userName, TSentryPrivilege privilege) - throws SentryNoSuchObjectException, SentryInvalidInputException { - MSentryPrivilege mPrivilege = null; - MSentryUser mUser = getUser(pm, userName); - if (mUser == null) { - throw noSuchUser(userName); - } - - if(privilege.getPrivilegeScope().equalsIgnoreCase(PrivilegeScope.URI.name()) - && StringUtils.isBlank(privilege.getURI())) { - throw new SentryInvalidInputException("cannot grant URI privileges to Null or EMPTY location"); - } - - if (!isNULL(privilege.getColumnName()) || !isNULL(privilege.getTableName()) - || !isNULL(privilege.getDbName())) { - // If Grant is for ALL and Either INSERT/SELECT already exists.. - // need to remove it and GRANT ALL.. - if (AccessConstants.ALL.equalsIgnoreCase(privilege.getAction()) - || AccessConstants.ACTION_ALL.equalsIgnoreCase(privilege.getAction())) { - TSentryPrivilege tNotAll = new TSentryPrivilege(privilege); - tNotAll.setAction(AccessConstants.SELECT); - MSentryPrivilege mSelect = getMSentryPrivilege(tNotAll, pm); - tNotAll.setAction(AccessConstants.INSERT); - MSentryPrivilege mInsert = getMSentryPrivilege(tNotAll, pm); - if ((mSelect != null) && mUser.getPrivileges().contains(mSelect)) { - mSelect.removeUser(mUser); - pm.makePersistent(mSelect); - } - if ((mInsert != null) && mUser.getPrivileges().contains(mInsert)) { - mInsert.removeUser(mUser); - pm.makePersistent(mInsert); - } - } else { - // If Grant is for Either INSERT/SELECT and ALL already exists.. - // do nothing.. - TSentryPrivilege tAll = new TSentryPrivilege(privilege); - tAll.setAction(AccessConstants.ALL); - MSentryPrivilege mAll1 = getMSentryPrivilege(tAll, pm); - tAll.setAction(AccessConstants.ACTION_ALL); - MSentryPrivilege mAll2 = getMSentryPrivilege(tAll, pm); - if (mAll1 != null && mUser.getPrivileges().contains(mAll1)) { - return null; - } - if (mAll2 != null && mUser.getPrivileges().contains(mAll2)) { - return null; - } - } - } - - mPrivilege = getMSentryPrivilege(privilege, pm); - if (mPrivilege == null) { - mPrivilege = convertToMSentryPrivilege(privilege); - } - mPrivilege.appendUser(mUser); - pm.makePersistent(mPrivilege); - return mPrivilege; - } - - /** - * Alter a given sentry user to revoke a privilege. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param tPrivilege the given privilege - * @throws Exception - * - */ - void alterSentryUserRevokePrivilege(final String grantorPrincipal, - final String userName, final TSentryPrivilege tPrivilege) throws Exception { - - tm.executeTransactionWithRetry( - new TransactionBlock<Object>() { - public Object execute(PersistenceManager pm) throws Exception { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedUserName = safeTrimLower(userName); - // first do revoke check - grantOptionCheck(pm, grantorPrincipal, tPrivilege); - - alterSentryUserRevokePrivilegeCore(pm, trimmedUserName, tPrivilege); - return null; - } - }); - } - - /** - * Alter a given sentry user to revoke a set of privileges. - * Internally calls alterSentryUserRevokePrivilege. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param tPrivileges a Set of privileges - * @throws Exception - * - */ - public void alterSentryUserRevokePrivileges(final String grantorPrincipal, - final String userName, final Set<TSentryPrivilege> tPrivileges) throws Exception { - for (TSentryPrivilege tPrivilege : tPrivileges) { - alterSentryUserRevokePrivilege(grantorPrincipal, userName, tPrivilege); - } - } - - /** - * Alter a given sentry user to revoke a set of privileges, as well as persist the - * corresponding permission change to MSentryPermChange table in a single transaction. - * Internally calls alterSentryUserRevokePrivilege. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param tPrivileges a Set of privileges - * @param privilegesUpdateMap the corresponding <privilege, Update> map - * @throws Exception - * - */ - public void alterSentryUserRevokePrivileges(final String grantorPrincipal, - final String userName, final Set<TSentryPrivilege> tPrivileges, - final Map<TSentryPrivilege, Update> privilegesUpdateMap) - throws Exception { - - Preconditions.checkNotNull(privilegesUpdateMap); - for (TSentryPrivilege tPrivilege : tPrivileges) { - Update update = privilegesUpdateMap.get(tPrivilege); - if (update != null) { - alterSentryUserRevokePrivilege(grantorPrincipal, userName, - tPrivilege, update); - } else { - alterSentryUserRevokePrivilege(grantorPrincipal, userName, - tPrivilege); - } - } - } - - /** - * Alter a given sentry user to revoke a privilege, as well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param grantorPrincipal User name - * @param userName the given user name - * @param tPrivilege the given privilege - * @param update the corresponding permission delta update transaction block - * @throws Exception - * - */ - private synchronized void alterSentryUserRevokePrivilege(final String grantorPrincipal, - final String userName, final TSentryPrivilege tPrivilege, - final Update update) throws Exception { - execute(update, new TransactionBlock<Object>() { - public Object execute(PersistenceManager pm) throws Exception { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedUserName = safeTrimLower(userName); - // first do revoke check - grantOptionCheck(pm, grantorPrincipal, tPrivilege); - - alterSentryUserRevokePrivilegeCore(pm, trimmedUserName, tPrivilege); - return null; - } - }); - } - - private void alterSentryUserRevokePrivilegeCore(PersistenceManager pm, - String userName, TSentryPrivilege tPrivilege) - throws SentryNoSuchObjectException, SentryInvalidInputException { - MSentryUser mUser = getUser(pm, userName); - if (mUser == null) { - throw noSuchUser(userName); - } - if(tPrivilege.getPrivilegeScope().equalsIgnoreCase(PrivilegeScope.URI.name()) - && StringUtils.isBlank(tPrivilege.getURI())) { - throw new SentryInvalidInputException("cannot revoke URI privileges from Null or EMPTY location"); - } - - MSentryPrivilege mPrivilege = getMSentryPrivilege(tPrivilege, pm); - if (mPrivilege == null) { - mPrivilege = convertToMSentryPrivilege(tPrivilege); - } else { - mPrivilege = pm.detachCopy(mPrivilege); - } - - Set<MSentryPrivilege> privilegeGraph = new HashSet<>(); - if (mPrivilege.getGrantOption() != null) { - privilegeGraph.add(mPrivilege); - } else { - MSentryPrivilege mTure = new MSentryPrivilege(mPrivilege); - mTure.setGrantOption(true); - privilegeGraph.add(mTure); - MSentryPrivilege mFalse = new MSentryPrivilege(mPrivilege); - mFalse.setGrantOption(false); - privilegeGraph.add(mFalse); - } - // Get the privilege graph - populateChildren(pm, SentryEntityType.USER, Sets.newHashSet(userName), mPrivilege, privilegeGraph); - for (MSentryPrivilege childPriv : privilegeGraph) { - revokePrivilegeFromUser(pm, tPrivilege, mUser, childPriv); - } - pm.makePersistent(mUser); - } - - /** - * Alter a given sentry role to revoke a privilege. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param tPrivilege the given privilege - * @throws Exception - * - */ - void alterSentryRoleRevokePrivilege(final String grantorPrincipal, - final String roleName, final TSentryPrivilege tPrivilege) throws Exception { - - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = safeTrimLower(roleName); - // first do revoke check - grantOptionCheck(pm, grantorPrincipal, tPrivilege); - - alterSentryRoleRevokePrivilegeCore(pm, trimmedRoleName, tPrivilege); - return null; - }); - } - - /** - * Alter a given sentry role to revoke a set of privileges. - * Internally calls alterSentryRoleRevokePrivilege. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param tPrivileges a Set of privileges - * @throws Exception - * - */ - public void alterSentryRoleRevokePrivileges(final String grantorPrincipal, - final String roleName, final Set<TSentryPrivilege> tPrivileges) throws Exception { - for (TSentryPrivilege tPrivilege : tPrivileges) { - alterSentryRoleRevokePrivilege(grantorPrincipal, roleName, tPrivilege); - } - } - - /** - * Alter a given sentry role to revoke a privilege, as well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param tPrivilege the given privilege - * @param update the corresponding permission delta update transaction block - * @throws Exception - * - */ - private synchronized void alterSentryRoleRevokePrivilege(final String grantorPrincipal, - final String roleName, final TSentryPrivilege tPrivilege, - final Update update) throws Exception { - execute(update, pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = safeTrimLower(roleName); - // first do revoke check - grantOptionCheck(pm, grantorPrincipal, tPrivilege); - - alterSentryRoleRevokePrivilegeCore(pm, trimmedRoleName, tPrivilege); - return null; - }); - } - - /** - * Alter a given sentry role to revoke a set of privileges, as well as persist the - * corresponding permission change to MSentryPermChange table in a single transaction. - * Internally calls alterSentryRoleRevokePrivilege. - * - * @param grantorPrincipal User name - * @param roleName the given role name - * @param tPrivileges a Set of privileges - * @param privilegesUpdateMap the corresponding <privilege, Update> map - * @throws Exception - * - */ - public void alterSentryRoleRevokePrivileges(final String grantorPrincipal, - final String roleName, final Set<TSentryPrivilege> tPrivileges, - final Map<TSentryPrivilege, Update> privilegesUpdateMap) - throws Exception { - - Preconditions.checkNotNull(privilegesUpdateMap); - for (TSentryPrivilege tPrivilege : tPrivileges) { - Update update = privilegesUpdateMap.get(tPrivilege); - if (update != null) { - alterSentryRoleRevokePrivilege(grantorPrincipal, roleName, - tPrivilege, update); - } else { - alterSentryRoleRevokePrivilege(grantorPrincipal, roleName, - tPrivilege); - } - } - } - - private void alterSentryRoleRevokePrivilegeCore(PersistenceManager pm, - String roleName, TSentryPrivilege tPrivilege) - throws SentryNoSuchObjectException, SentryInvalidInputException { - MSentryRole mRole = getRole(pm, roleName); - if (mRole == null) { - throw noSuchRole(roleName); - } - if(tPrivilege.getPrivilegeScope().equalsIgnoreCase(PrivilegeScope.URI.name()) - && StringUtils.isBlank(tPrivilege.getURI())) { - throw new SentryInvalidInputException("cannot revoke URI privileges from Null or EMPTY location"); - } - - MSentryPrivilege mPrivilege = getMSentryPrivilege(tPrivilege, pm); - if (mPrivilege == null) { - mPrivilege = convertToMSentryPrivilege(tPrivilege); - } else { - mPrivilege = pm.detachCopy(mPrivilege); - } - - Set<MSentryPrivilege> privilegeGraph = new HashSet<>(); - if (mPrivilege.getGrantOption() != null) { - privilegeGraph.add(mPrivilege); - } else { - MSentryPrivilege mTure = new MSentryPrivilege(mPrivilege); - mTure.setGrantOption(true); - privilegeGraph.add(mTure); - MSentryPrivilege mFalse = new MSentryPrivilege(mPrivilege); - mFalse.setGrantOption(false); - privilegeGraph.add(mFalse); - } - // Get the privilege graph - populateChildren(pm, SentryEntityType.ROLE, Sets.newHashSet(roleName), mPrivilege, privilegeGraph); - for (MSentryPrivilege childPriv : privilegeGraph) { - revokePrivilegeFromRole(pm, tPrivilege, mRole, childPriv); - } - pm.makePersistent(mRole); - } - - /** - * Roles can be granted ALL, SELECT, and INSERT on tables. When - * a role has ALL and SELECT or INSERT are revoked, we need to remove the ALL - * privilege and add SELECT (INSERT was revoked) or INSERT (SELECT was revoked). - */ - private void revokePartial(PersistenceManager pm, - TSentryPrivilege requestedPrivToRevoke, - MSentryRole mRole, MSentryUser mUser, - MSentryPrivilege currentPrivilege) throws SentryInvalidInputException { - MSentryPrivilege persistedPriv = - getMSentryPrivilege(convertToTSentryPrivilege(currentPrivilege), pm); - if (persistedPriv == null) { - // The privilege corresponding to the currentPrivilege doesn't exist in the persistent - // store, so we create a fake one for the code below. The fake one is not associated with - // any role and shouldn't be stored in the persistent storage. - persistedPriv = convertToMSentryPrivilege(convertToTSentryPrivilege(currentPrivilege)); - } - - if (requestedPrivToRevoke.getAction().equalsIgnoreCase(AccessConstants.ALL) || - requestedPrivToRevoke.getAction().equalsIgnoreCase(AccessConstants.ACTION_ALL)) { - if (!persistedPriv.getRoles().isEmpty()) { - if (mRole != null) { - persistedPriv.removeRole(mRole); - } - if (mUser != null) { - persistedPriv.removeUser(mUser); - } - - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - } - } else { - - Set<String> addActions = new HashSet<String>(); - for (String actionToAdd : PARTIAL_REVOKE_ACTIONS) { - if( !requestedPrivToRevoke.getAction().equalsIgnoreCase(actionToAdd) && - !currentPrivilege.getAction().equalsIgnoreCase(actionToAdd) && - !AccessConstants.ALL.equalsIgnoreCase(actionToAdd) && - !AccessConstants.ACTION_ALL.equalsIgnoreCase(actionToAdd)) { - addActions.add(actionToAdd); - } - } - - if (mRole != null) { - revokeRolePartial(pm, mRole, currentPrivilege, persistedPriv, addActions); - } - - if (mUser != null) { - revokeUserPartial(pm, mUser, currentPrivilege, persistedPriv, addActions); - } - } - } - - private boolean isPrivilegeStall(MSentryPrivilege privilege) { - if (privilege.getUsers().isEmpty() && privilege.getRoles().isEmpty()) { - return true; - } - - return false; - } - - private boolean isPrivilegeStall(MSentryGMPrivilege privilege) { - if (privilege.getRoles().isEmpty()) { - return true; - } - - return false; - } - - private void revokeRolePartial(PersistenceManager pm, MSentryRole mRole, - MSentryPrivilege currentPrivilege, - MSentryPrivilege persistedPriv, - Set<String> addActions) throws SentryInvalidInputException { - // If table / URI, remove ALL - persistedPriv = getMSentryPrivilege(convertToTSentryPrivilege(persistedPriv), pm); - if (persistedPriv != null && !persistedPriv.getRoles().isEmpty()) { - persistedPriv.removeRole(mRole); - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - } - currentPrivilege.setAction(AccessConstants.ALL); - persistedPriv = getMSentryPrivilege(convertToTSentryPrivilege(currentPrivilege), pm); - if (persistedPriv != null && mRole.getPrivileges().contains(persistedPriv)) { - persistedPriv.removeRole(mRole); - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - - // add decomposted actions - for (String addAction : addActions) { - currentPrivilege.setAction(addAction); - TSentryPrivilege tSentryPrivilege = convertToTSentryPrivilege(currentPrivilege); - persistedPriv = getMSentryPrivilege(tSentryPrivilege, pm); - if (persistedPriv == null) { - persistedPriv = convertToMSentryPrivilege(tSentryPrivilege); - } - mRole.appendPrivilege(persistedPriv); - } - persistedPriv.appendRole(mRole); - pm.makePersistent(persistedPriv); - } - } - - private void revokeUserPartial(PersistenceManager pm, MSentryUser mUser, - MSentryPrivilege currentPrivilege, - MSentryPrivilege persistedPriv, - Set<String> addActions) throws SentryInvalidInputException { - // If table / URI, remove ALL - persistedPriv = getMSentryPrivilege(convertToTSentryPrivilege(persistedPriv), pm); - if (persistedPriv != null && !persistedPriv.getUsers().isEmpty()) { - persistedPriv.removeUser(mUser); - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - } - currentPrivilege.setAction(AccessConstants.ALL); - persistedPriv = getMSentryPrivilege(convertToTSentryPrivilege(currentPrivilege), pm); - if (persistedPriv != null && mUser.getPrivileges().contains(persistedPriv)) { - persistedPriv.removeUser(mUser); - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - - // add decomposted actions - for (String addAction : addActions) { - currentPrivilege.setAction(addAction); - TSentryPrivilege tSentryPrivilege = convertToTSentryPrivilege(currentPrivilege); - persistedPriv = getMSentryPrivilege(tSentryPrivilege, pm); - if (persistedPriv == null) { - persistedPriv = convertToMSentryPrivilege(tSentryPrivilege); - } - mUser.appendPrivilege(persistedPriv); - } - persistedPriv.appendUser(mUser); - pm.makePersistent(persistedPriv); - } - } - - /** - * Revoke privilege from role - */ - private void revokePrivilegeFromRole(PersistenceManager pm, TSentryPrivilege tPrivilege, - MSentryRole mRole, MSentryPrivilege mPrivilege) - throws SentryInvalidInputException { - if (PARTIAL_REVOKE_ACTIONS.contains(mPrivilege.getAction())) { - // if this privilege is in parital revoke actions - // we will do partial revoke - revokePartial(pm, tPrivilege, mRole, null, mPrivilege); - } else { - // otherwise, - // we will revoke it from role directly - MSentryPrivilege persistedPriv = getMSentryPrivilege(convertToTSentryPrivilege(mPrivilege), pm); - if (persistedPriv != null && !persistedPriv.getRoles().isEmpty()) { - persistedPriv.removeRole(mRole); - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - } - } - } - - /** - * Revoke privilege from user - */ - private void revokePrivilegeFromUser(PersistenceManager pm, TSentryPrivilege tPrivilege, - MSentryUser mUser, MSentryPrivilege mPrivilege) - throws SentryInvalidInputException { - if (PARTIAL_REVOKE_ACTIONS.contains(mPrivilege.getAction())) { - // if this privilege is in parital revoke actions - // we will do partial revoke - revokePartial(pm, tPrivilege, null, mUser, mPrivilege); - } else { - // otherwise, - // we will revoke it from user directly - MSentryPrivilege persistedPriv = getMSentryPrivilege(convertToTSentryPrivilege(mPrivilege), pm); - if (persistedPriv != null && !persistedPriv.getUsers().isEmpty()) { - persistedPriv.removeUser(mUser); - if (isPrivilegeStall(persistedPriv)) { - pm.deletePersistent(persistedPriv); - } else { - pm.makePersistent(persistedPriv); - } - } - } - } - - /** - * Explore Privilege graph and collect child privileges. - * The responsibility to commit/rollback the transaction should be handled by the caller. - */ - private void populateChildren(PersistenceManager pm, SentryEntityType entityType, Set<String> entityNames, MSentryPrivilege priv, - Collection<MSentryPrivilege> children) throws SentryInvalidInputException { - Preconditions.checkNotNull(pm); - if (!isNULL(priv.getServerName()) || !isNULL(priv.getDbName()) - || !isNULL(priv.getTableName())) { - // Get all TableLevel Privs - Set<MSentryPrivilege> childPrivs = getChildPrivileges(pm, entityType, entityNames, priv); - for (MSentryPrivilege childPriv : childPrivs) { - // Only recurse for table level privs.. - if (!isNULL(childPriv.getDbName()) && !isNULL(childPriv.getTableName()) - && !isNULL(childPriv.getColumnName())) { - populateChildren(pm, entityType, entityNames, childPriv, children); - } - // The method getChildPrivileges() didn't do filter on "action", - // if the action is not "All", it should judge the action of children privilege. - // For example: a user has a privilege âAll on Col1â, - // if the operation is âREVOKE INSERT on tableâ - // the privilege should be the child of table level privilege. - // but the privilege may still have other meaning, likes "SELECT, CREATE etc. on Col1". - // and the privileges like "SELECT, CREATE etc. on Col1" should not be revoke. - if (!priv.isActionALL()) { - if (childPriv.isActionALL()) { - // If the child privilege is All, we should convert it to the same - // privilege with parent - childPriv.setAction(priv.getAction()); - } - // Only include privilege that imply the parent privilege. - if (!priv.implies(childPriv)) { - continue; - } - } - children.add(childPriv); - } - } - } - - private Set<MSentryPrivilege> getChildPrivileges(PersistenceManager pm, SentryEntityType entityType, Set<String> entityNames, - MSentryPrivilege parent) throws SentryInvalidInputException { - // Column and URI do not have children - if (!isNULL(parent.getColumnName()) || !isNULL(parent.getURI())) { - return Collections.emptySet(); - } - - Query query = pm.newQuery(MSentryPrivilege.class); - QueryParamBuilder paramBuilder = null; - if (entityType == SentryEntityType.ROLE) { - paramBuilder = QueryParamBuilder.addRolesFilter(query, null, entityNames).add(SERVER_NAME, parent.getServerName()); - } else if (entityType == SentryEntityType.USER) { - paramBuilder = QueryParamBuilder.addUsersFilter(query, null, entityNames).add(SERVER_NAME, parent.getServerName()); - } else { - throw new SentryInvalidInputException("entityType" + entityType + " is not valid"); - } - - if (!isNULL(parent.getDbName())) { - paramBuilder.add(DB_NAME, parent.getDbName()); - if (!isNULL(parent.getTableName())) { - paramBuilder.add(TABLE_NAME, parent.getTableName()) - .addNotNull(COLUMN_NAME); - } else { - paramBuilder.addNotNull(TABLE_NAME); - } - } else { - // Add condition dbName != NULL || URI != NULL - paramBuilder.newChild() - .addNotNull(DB_NAME) - .addNotNull(URI); - } - - query.setFilter(paramBuilder.toString()); - query.setResult("privilegeScope, serverName, dbName, tableName, columnName," + - " URI, action, grantOption"); - List<Object[]> privObjects = - (List<Object[]>) query.executeWithMap(paramBuilder.getArguments()); - Set<MSentryPrivilege> privileges = new HashSet<>(privObjects.size()); - for (Object[] privObj : privObjects) { - String scope = (String)privObj[0]; - String serverName = (String)privObj[1]; - String dbName = (String)privObj[2]; - String tableName = (String) privObj[3]; - String columnName = (String) privObj[4]; - String URI = (String) privObj[5]; - String action = (String) privObj[6]; - Boolean grantOption = (Boolean) privObj[7]; - MSentryPrivilege priv = - new MSentryPrivilege(scope, serverName, dbName, tableName, - columnName, URI, action, grantOption); - privileges.add(priv); - } - return privileges; - } - - /** - * Drop a given sentry user. - * - * @param userName the given user name - * @throws Exception - */ - public void dropSentryUser(final String userName) throws Exception { - tm.executeTransactionWithRetry( - new TransactionBlock<Object>() { - public Object execute(PersistenceManager pm) throws Exception { - pm.setDetachAllOnCommit(false); // No need to detach objects - dropSentryUserCore(pm, userName); - return null; - } - }); - } - - /** - * Drop a given sentry user. As well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param userName the given user name - * @param update the corresponding permission delta update - * @throws Exception - */ - public synchronized void dropSentryUser(final String userName, - final Update update) throws Exception { - execute(update, new TransactionBlock<Object>() { - public Object execute(PersistenceManager pm) throws Exception { - pm.setDetachAllOnCommit(false); // No need to detach objects - dropSentryUserCore(pm, userName); - return null; - } - }); - } - - private void dropSentryUserCore(PersistenceManager pm, String userName) - throws SentryNoSuchObjectException { - String lUserName = trimAndLower(userName); - MSentryUser sentryUser = getUser(pm, lUserName); - if (sentryUser == null) { - throw noSuchUser(lUserName); - } - removePrivilegesForUser(pm, sentryUser); - pm.deletePersistent(sentryUser); - } - - /** - * Removes all the privileges associated with - * a particular user. After this dis-association if the - * privilege doesn't have any users associated it will be - * removed from the underlying persistence layer. - * @param pm Instance of PersistenceManager - * @param sentryUser User for which all the privileges are to be removed. - */ - private void removePrivilegesForUser(PersistenceManager pm, MSentryUser sentryUser) { - List<MSentryPrivilege> privilegesCopy = new ArrayList<>(sentryUser.getPrivileges()); - - sentryUser.removePrivileges(); - - removeStaledPrivileges(pm, privilegesCopy); - } - - @SuppressWarnings("unchecked") - private List<MSentryPrivilege> getMSentryPrivileges(TSentryPrivilege tPriv, PersistenceManager pm) { - Query query = pm.newQuery(MSentryPrivilege.class); - QueryParamBuilder paramBuilder = newQueryParamBuilder(); - paramBuilder - .add(SERVER_NAME, tPriv.getServerName()) - .add("action", tPriv.getAction()); - - if (!isNULL(tPriv.getDbName())) { - paramBuilder.add(DB_NAME, tPriv.getDbName()); - if (!isNULL(tPriv.getTableName())) { - paramBuilder.add(TABLE_NAME, tPriv.getTableName()); - if (!isNULL(tPriv.getColumnName())) { - paramBuilder.add(COLUMN_NAME, tPriv.getColumnName()); - } - } - } else if (!isNULL(tPriv.getURI())) { - // if db is null, uri is not null - paramBuilder.add(URI, tPriv.getURI(), true); - } - - query.setFilter(paramBuilder.toString()); - return (List<MSentryPrivilege>) query.executeWithMap(paramBuilder.getArguments()); - } - - private MSentryPrivilege getMSentryPrivilege(TSentryPrivilege tPriv, PersistenceManager pm) { - Boolean grantOption = null; - if (tPriv.getGrantOption().equals(TSentryGrantOption.TRUE)) { - grantOption = true; - } else if (tPriv.getGrantOption().equals(TSentryGrantOption.FALSE)) { - grantOption = false; - } - - QueryParamBuilder paramBuilder = newQueryParamBuilder(); - paramBuilder.add(SERVER_NAME, tPriv.getServerName()) - .add(DB_NAME, tPriv.getDbName()) - .add(TABLE_NAME, tPriv.getTableName()) - .add(COLUMN_NAME, tPriv.getColumnName()) - .add(URI, tPriv.getURI(), true) - .addObject(GRANT_OPTION, grantOption) - .add(ACTION, tPriv.getAction()); - - Query query = pm.newQuery(MSentryPrivilege.class); - query.setUnique(true); - query.setFilter(paramBuilder.toString()); - return (MSentryPrivilege)query.executeWithMap(paramBuilder.getArguments()); - } - - /** - * Drop a given sentry role. - * - * @param roleName the given role name - * @throws Exception - */ - public void dropSentryRole(final String roleName) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - dropSentryRoleCore(pm, roleName); - return null; - }); - } - - /** - * Drop a given sentry role. As well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param roleName the given role name - * @param update the corresponding permission delta update - * @throws Exception - */ - public synchronized void dropSentryRole(final String roleName, - final Update update) throws Exception { - execute(update, pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - dropSentryRoleCore(pm, roleName); - return null; - }); - } - - private void dropSentryRoleCore(PersistenceManager pm, String roleName) - throws SentryNoSuchObjectException { - String lRoleName = trimAndLower(roleName); - MSentryRole sentryRole = getRole(pm, lRoleName); - if (sentryRole == null) { - throw noSuchRole(lRoleName); - } - removePrivileges(pm, sentryRole); - pm.deletePersistent(sentryRole); - } - - /** - * Removes all the privileges associated with - * a particular role. After this dis-association if the - * privilege doesn't have any roles associated it will be - * removed from the underlying persistence layer. - * @param pm Instance of PersistenceManager - * @param sentryRole Role for which all the privileges are to be removed. - */ - private void removePrivileges(PersistenceManager pm, MSentryRole sentryRole) { - List<MSentryPrivilege> privilegesCopy = new ArrayList<>(sentryRole.getPrivileges()); - List<MSentryGMPrivilege> gmPrivilegesCopy = new ArrayList<>(sentryRole.getGmPrivileges()); - - sentryRole.removePrivileges(); - // with SENTRY-398 generic model - sentryRole.removeGMPrivileges(); - - removeStaledPrivileges(pm, privilegesCopy); - removeStaledGMPrivileges(pm, gmPrivilegesCopy); - } - - private void removeStaledPrivileges(PersistenceManager pm, List<MSentryPrivilege> privilegesCopy) { - List<MSentryPrivilege> stalePrivileges = new ArrayList<>(0); - for (MSentryPrivilege privilege : privilegesCopy) { - if (isPrivilegeStall(privilege)) { - stalePrivileges.add(privilege); - } - } - if(!stalePrivileges.isEmpty()) { - pm.deletePersistentAll(stalePrivileges); - } - } - - private void removeStaledGMPrivileges(PersistenceManager pm, List<MSentryGMPrivilege> privilegesCopy) { - List<MSentryGMPrivilege> stalePrivileges = new ArrayList<>(0); - for (MSentryGMPrivilege privilege : privilegesCopy) { - if (isPrivilegeStall(privilege)) { - stalePrivileges.add(privilege); - } - } - if(!stalePrivileges.isEmpty()) { - pm.deletePersistentAll(stalePrivileges); - } - } - - /** - * Assign a given role to a set of groups. - * - * @param grantorPrincipal grantorPrincipal currently is not used. - * @param roleName the role to be assigned to the groups. - * @param groupNames the list of groups to be added to the role, - * @throws Exception - */ - public void alterSentryRoleAddGroups(final String grantorPrincipal, - final String roleName, final Set<TSentryGroup> groupNames) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - alterSentryRoleAddGroupsCore(pm, roleName, groupNames); - return null; - }); - } - - /** - * Assign a given role to a set of groups. As well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param grantorPrincipal grantorPrincipal currently is not used. - * @param roleName the role to be assigned to the groups. - * @param groupNames the list of groups to be added to the role, - * @param update the corresponding permission delta update - * @throws Exception - */ - public synchronized void alterSentryRoleAddGroups(final String grantorPrincipal, - final String roleName, final Set<TSentryGroup> groupNames, - final Update update) throws Exception { - - execute(update, pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - alterSentryRoleAddGroupsCore(pm, roleName, groupNames); - return null; - }); - } - - private void alterSentryRoleAddGroupsCore(PersistenceManager pm, String roleName, - Set<TSentryGroup> groupNames) throws SentryNoSuchObjectException { - - // All role names are stored in lowercase. - String lRoleName = trimAndLower(roleName); - MSentryRole role = getRole(pm, lRoleName); - if (role == null) { - throw noSuchRole(lRoleName); - } - - // Add the group to the specified role if it does not belong to the role yet. - Query query = pm.newQuery(MSentryGroup.class); - query.setFilter("this.groupName == :groupName"); - query.setUnique(true); - List<MSentryGroup> groups = Lists.newArrayList(); - for (TSentryGroup tGroup : groupNames) { - String groupName = tGroup.getGroupName().trim(); - MSentryGroup group = (MSentryGroup) query.execute(groupName); - if (group == null) { - group = new MSentryGroup(groupName, System.currentTimeMillis(), Sets.newHashSet(role)); - } - group.appendRole(role); - groups.add(group); - } - pm.makePersistentAll(groups); - } - - public void alterSentryRoleAddUsers(final String roleName, - final Set<String> userNames) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - alterSentryRoleAddUsersCore(pm, roleName, userNames); - return null; - }); - } - - private void alterSentryRoleAddUsersCore(PersistenceManager pm, String roleName, - Set<String> userNames) throws SentryNoSuchObjectException { - String trimmedRoleName = trimAndLower(roleName); - MSentryRole role = getRole(pm, trimmedRoleName); - if (role == null) { - throw noSuchRole(trimmedRoleName); - } - Query query = pm.newQuery(MSentryUser.class); - query.setFilter("this.userName == :userName"); - query.setUnique(true); - List<MSentryUser> users = Lists.newArrayList(); - for (String userName : userNames) { - userName = userName.trim(); - MSentryUser user = (MSentryUser) query.execute(userName); - if (user == null) { - user = new MSentryUser(userName, System.currentTimeMillis(), Sets.newHashSet(role)); - } - user.appendRole(role); - users.add(user); - } - pm.makePersistentAll(users); - } - - public void alterSentryRoleDeleteUsers(final String roleName, - final Set<String> userNames) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = trimAndLower(roleName); - MSentryRole role = getRole(pm, trimmedRoleName); - if (role == null) { - throw noSuchRole(trimmedRoleName); - } else { - Query query = pm.newQuery(MSentryUser.class); - query.setFilter("this.userName == :userName"); - query.setUnique(true); - List<MSentryUser> users = Lists.newArrayList(); - for (String userName : userNames) { - userName = userName.trim(); - MSentryUser user = (MSentryUser) query.execute(userName); - if (user != null) { - user.removeRole(role); - users.add(user); - } - } - pm.makePersistentAll(users); - } - return null; - }); - } - - /** - * Revoke a given role to a set of groups. - * - * @param roleName the role to be assigned to the groups. - * @param groupNames the list of groups to be added to the role, - * @throws Exception - */ - public void alterSentryRoleDeleteGroups(final String roleName, - final Set<TSentryGroup> groupNames) throws Exception { - tm.executeTransactionWithRetry( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = trimAndLower(roleName); - MSentryRole role = getRole(pm, trimmedRoleName); - if (role == null) { - throw noSuchRole(trimmedRoleName); - } - Query query = pm.newQuery(MSentryGroup.class); - query.setFilter("this.groupName == :groupName"); - query.setUnique(true); - List<MSentryGroup> groups = Lists.newArrayList(); - for (TSentryGroup tGroup : groupNames) { - String groupName = tGroup.getGroupName().trim(); - MSentryGroup group = (MSentryGroup) query.execute(groupName); - if (group != null) { - group.removeRole(role); - groups.add(group); - } - } - pm.makePersistentAll(groups); - return null; - }); - } - - /** - * Revoke a given role to a set of groups. As well as persist the corresponding - * permission change to MSentryPermChange table in a single transaction. - * - * @param roleName the role to be assigned to the groups. - * @param groupNames the list of groups to be added to the role, - * @param update the corresponding permission delta update - * @throws Exception - */ - public synchronized void alterSentryRoleDeleteGroups(final String roleName, - final Set<TSentryGroup> groupNames, final Update update) - throws Exception { - execute(update, pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - String trimmedRoleName = trimAndLower(roleName); - MSentryRole role = getRole(pm, trimmedRoleName); - if (role == null) { - throw noSuchRole(trimmedRoleName); - } - - // Remove the group from the specified role if it belongs to the role. - Query query = pm.newQuery(MSentryGroup.class); - query.setFilter("this.groupName == :groupName"); - query.setUnique(true); - List<MSentryGroup> groups = Lists.newArrayList(); - for (TSentryGroup tGroup : groupNames) { - String groupName = tGroup.getGroupName().trim(); - MSentryGroup group = (MSentryGroup) query.execute(groupName); - if (group != null) { - group.removeRole(role); - groups.add(group); - } - } - pm.makePersistentAll(groups); - return null; - }); - } - - @VisibleForTesting - public MSentryRole getMSentryRoleByName(final String roleName) throws Exception { - return tm.executeTransaction( - pm -> { - String trimmedRoleName = trimAndLower(roleName); - MSentryRole sentryRole = getRole(pm, trimmedRoleName); - if (sentryRole == null) { - throw noSuchRole(trimmedRoleName); - } - return sentryRole; - }); - } - - /** - * Gets the MSentryPrivilege from sentry persistent storage based on TSentryPrivilege - * provided - * - * Method is currently used only in test framework - * @param tPrivilege - * @return MSentryPrivilege if the privilege is found in the storage - * null, if the privilege is not found in the storage. - * @throws Exception - */ - @VisibleForTesting - MSentryPrivilege findMSentryPrivilegeFromTSentryPrivilege(final TSentryPrivilege tPrivilege) throws Exception { - return tm.executeTransaction( - pm -> getMSentryPrivilege(tPrivilege, pm)); - } - - /** - * Returns a list with all the privileges in the sentry persistent storage - * - * Method is currently used only in test framework - * @return List of all sentry privileges in the store - * @throws Exception - */ - @VisibleForTesting - List<MSentryPrivilege> getAllMSentryPrivileges () throws Exception { - return tm.executeTransaction( - pm -> getAllMSentryPrivilegesCore(pm)); - } - - /** - * Method Returns all the privileges present in the persistent store as a list. - * @param pm PersistenceManager - * @returns list of all the privileges in the persistent store - */ - private List<MSentryPrivilege> getAllMSentryPrivilegesCore (PersistenceManager pm) { - Query query = pm.newQuery(MSentryPrivilege.class); - return (List<MSentryPrivilege>) query.execute(); - } - - private boolean hasAnyServerPrivileges(final Set<String> roleNames, final String serverName) throws Exception { - if (roleNames == null || roleNames.isEmpty()) { - return false; - } - return tm.executeTransaction( - pm -> { - pm.setDetachAllOnCommit(false); // No need to detach objects - Query query = pm.newQuery(MSentryPrivilege.class); - query.addExtension(LOAD_RESULTS_AT_COMMIT, "false"); - QueryParamBuilder paramBuilder = QueryParamBuilder.addRolesFilter(query,null, roleNames); - paramBuilder.add(SERVER_NAME, serverName); - query.setFilter(paramBuilder.toString()); - query.setResult("count(this)"); - Long numPrivs = (Long) query.executeWithMap(paramBuilder.getArguments()); - return numPrivs > 0; - }); - } - - private List<MSentryPrivilege> getMSentryPrivileges(final SentryEntityType entityType, final Set<String> entityNames, - final TSentryAuthorizable authHierarchy) - throws Exception { - if (entityNames == null || entityNames.isEmpty()) { - return Collections.emptyList(); - } - - return tm.executeTransaction( - pm -> { - Query query = pm.newQuery(MSentryPrivilege.class); - QueryParamBuilder paramBuilder = null; - if (entityType == SentryEntityType.ROLE) { - paramBuilder = QueryParamBuilder.addRolesFilter(query, null, entityNames); - } else if (entityType == SentryEntityType.USER) { - paramBuilder = QueryParamBuilder.addUsersFilter(query, null, entityNames); - } else { - throw new SentryInvalidInputException("entityType" + entityType + " is not valid"); - } - - if (authHierarchy != null && authHierarchy.getServer() != null) { - paramBuilder.add(SERVER_NAME, authHierarchy.getServer()); - if (authHierarchy.getDb() != null) { - paramBuilder.addNull(URI) - .newChild() - .add(DB_NAME, authHierarchy.getDb()) - .addNull(DB_NAME); - if (authHierarchy.getTable() != null - && !AccessConstants.ALL.equalsIgnoreCase(authHierarchy.getTable())) { - if (!AccessConstants.SOME.equalsIgnoreCase(authHierarchy.getTable())) { - paramBuilder.addNull(URI) - .newChild() - .add(TABLE_NAME, authHierarchy.getTable()) - .addNull(TABLE_NAME); - } - if (authHierarchy.getColumn() != null - && !AccessConstants.ALL.equalsIgnoreCase(authHierarchy.getColumn()) - && !AccessConstants.SOME.equalsIgnoreCase(authHierarchy.getColumn())) { - paramBuilder.addNull(URI) - .newChild() - .add(COLUMN_NAME, authHierarchy.getColumn()) - .addNull(COLUMN_NAME); - } - } - } - if (authHierarchy.getUri() != null) { - paramBuilder.addNull(DB_NAME) - .newChild() - .addNull(URI) - .newChild() - .addNotNull(URI) - .addCustomParam("\"" + authHierarchy.getUri() + - "\".startsWith(:URI)", URI, authHierarchy.getUri()); - } - } - - query.setFilter(paramBuilder.toString()); - @SuppressWarnings("unchecked") - List<MSentryPrivilege> result = - (List<MSentryPrivilege>) - query.executeWithMap(paramBuilder.getArguments()); - return result; - }); - } - - private List<MSentryPrivilege> getMSentryPrivilegesByAuth( - final SentryEntityType entityType, - final Set<String> entityNames, - final TSentryAuthorizable - authHierarchy) throws Exception { - return tm.executeTransaction( - pm -> { - Query query = pm.newQuery(MSentryPrivilege.class); - QueryParamBuilder paramBuilder = newQueryParamBuilder(); - if (entityNames == null || entityNames.isEmpty()) { - if (entityType == SentryEntityType.ROLE) { - paramBuilder.addString("!roles.isEmpty()"); - } else if (entityType == SentryEntityType.USER) { - paramBuilder.addString("!users.isEmpty()"); - } else { - throw new SentryInvalidInputException("entityType: " + entityType + " is invalid"); - } - } else { - if (entityType == SentryEntityType.ROLE) { - QueryParamBuilder.addRolesFilter(query, paramBuilder, entityNames)
<TRUNCATED>
