| /** |
| * 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.generic.service.persistent; |
| |
| import java.lang.reflect.Constructor; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.jdo.PersistenceManager; |
| import javax.jdo.Query; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.sentry.core.common.exception.SentryUserException; |
| import org.apache.sentry.core.common.Action; |
| import org.apache.sentry.core.common.Authorizable; |
| import org.apache.sentry.core.common.BitFieldAction; |
| import org.apache.sentry.core.common.BitFieldActionFactory; |
| import org.apache.sentry.core.model.kafka.KafkaActionFactory; |
| import org.apache.sentry.core.model.search.SearchActionFactory; |
| import org.apache.sentry.core.model.sqoop.SqoopActionFactory; |
| import org.apache.sentry.provider.db.generic.service.persistent.PrivilegeObject.Builder; |
| import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege; |
| import org.apache.sentry.provider.db.service.model.MSentryRole; |
| |
| import com.google.common.base.Strings; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| import org.apache.sentry.provider.db.service.persistent.QueryParamBuilder; |
| import org.apache.sentry.provider.db.service.persistent.SentryStore; |
| import org.apache.sentry.service.thrift.ServiceConstants; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import static org.apache.sentry.provider.db.service.persistent.SentryStore.toNULLCol; |
| |
| /** |
| * Sentry Generic model privilege persistence support. |
| * <p> |
| * This class is similar to {@link SentryStore} but operates on generic |
| * privileges. |
| */ |
| public class PrivilegeOperatePersistence { |
| private static final String SERVICE_NAME = "serviceName"; |
| private static final String COMPONENT_NAME = "componentName"; |
| private static final String SCOPE = "scope"; |
| private static final String ACTION = "action"; |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(PrivilegeOperatePersistence.class); |
| private static final Map<String, BitFieldActionFactory> actionFactories = Maps.newHashMap(); |
| static{ |
| actionFactories.put("solr", new SearchActionFactory()); |
| actionFactories.put("sqoop", new SqoopActionFactory()); |
| actionFactories.put("kafka", KafkaActionFactory.getInstance()); |
| } |
| |
| private final Configuration conf; |
| |
| PrivilegeOperatePersistence(Configuration conf) { |
| this.conf = conf; |
| } |
| |
| /** |
| * Return query builder to execute in JDO for search the given privilege |
| * @param privilege Privilege to extract |
| * @return query builder suitable for executing the query |
| */ |
| private static QueryParamBuilder toQueryParam(MSentryGMPrivilege privilege) { |
| QueryParamBuilder paramBuilder = QueryParamBuilder.newQueryParamBuilder(); |
| paramBuilder.add(SERVICE_NAME, toNULLCol(privilege.getServiceName()), true) |
| .add(COMPONENT_NAME, toNULLCol(privilege.getComponentName()), true) |
| .add(SCOPE, toNULLCol(privilege.getScope()), true) |
| .add(ACTION, toNULLCol(privilege.getAction()), true); |
| |
| Boolean grantOption = privilege.getGrantOption(); |
| paramBuilder.addObject(SentryStore.GRANT_OPTION, grantOption); |
| |
| List<? extends Authorizable> authorizables = privilege.getAuthorizables(); |
| int nAuthorizables = authorizables.size(); |
| for (int i = 0; i < MSentryGMPrivilege.AUTHORIZABLE_LEVEL; i++) { |
| String resourceName = MSentryGMPrivilege.PREFIX_RESOURCE_NAME + String.valueOf(i); |
| String resourceType = MSentryGMPrivilege.PREFIX_RESOURCE_TYPE + String.valueOf(i); |
| |
| if (i >= nAuthorizables) { |
| paramBuilder.addNull(resourceName); |
| paramBuilder.addNull(resourceType); |
| } else { |
| paramBuilder.add(resourceName, authorizables.get(i).getName(), true); |
| paramBuilder.add(resourceType, authorizables.get(i).getTypeName(), true); |
| } |
| } |
| return paramBuilder; |
| } |
| |
| /** |
| * Create a query template tha includes information from the input privilege: |
| * <ul> |
| * <li>Service name</li> |
| * <li>Component name</li> |
| * <li>Name and type for each authorizable present</li> |
| * </ul> |
| * For exmaple, for Solr may configure the following privileges: |
| * <ul> |
| * <li>{@code p1:Collection=c1->action=query}</li> |
| * <li>{@code p2:Collection=c1->Field=f1->action=query}</li> |
| * <li>{@code p3:Collection=c1->Field=f2->action=query}</li> |
| * </ul> |
| * When the request for privilege revoke has |
| * {@code p4:Collection=c1->action=query} |
| * all privileges matching {@code Collection=c1} should be revoke which means that p1, p2 and p3 |
| * should all be revoked. |
| * |
| * @param privilege Source privilege |
| * @return ParamBuilder suitable for executing the query |
| */ |
| private static QueryParamBuilder populateIncludePrivilegesParams(MSentryGMPrivilege privilege) { |
| QueryParamBuilder paramBuilder = QueryParamBuilder.newQueryParamBuilder(); |
| paramBuilder.add(SERVICE_NAME, toNULLCol(privilege.getServiceName()), true); |
| paramBuilder.add(COMPONENT_NAME, toNULLCol(privilege.getComponentName()), true); |
| |
| List<? extends Authorizable> authorizables = privilege.getAuthorizables(); |
| int i = 0; |
| for(Authorizable auth: authorizables) { |
| String resourceName = MSentryGMPrivilege.PREFIX_RESOURCE_NAME + String.valueOf(i); |
| String resourceType = MSentryGMPrivilege.PREFIX_RESOURCE_TYPE + String.valueOf(i); |
| paramBuilder.add(resourceName, auth.getName(), true); |
| paramBuilder.add(resourceType, auth.getTypeName(), true); |
| i++; |
| } |
| return paramBuilder; |
| } |
| |
| /** |
| * Verify whether specified privilege can be granted |
| * @param roles set of roles for the privilege |
| * @param privilege privilege being checked |
| * @param pm Persistentence manager instance |
| * @return true iff at least one privilege within the role allows for the |
| * requested privilege |
| */ |
| boolean checkPrivilegeOption(Set<MSentryRole> roles, PrivilegeObject privilege, PersistenceManager pm) { |
| MSentryGMPrivilege requestPrivilege = convertToPrivilege(privilege); |
| if (roles.isEmpty()) { |
| return false; |
| } |
| // get persistent privileges by roles |
| // Find all GM privileges for all the input roles |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| QueryParamBuilder paramBuilder = QueryParamBuilder.addRolesFilter(query, null, |
| SentryStore.rolesToRoleNames(roles)); |
| query.setFilter(paramBuilder.toString()); |
| List<MSentryGMPrivilege> tPrivileges = |
| (List<MSentryGMPrivilege>)query.executeWithMap(paramBuilder.getArguments()); |
| |
| for (MSentryGMPrivilege tPrivilege : tPrivileges) { |
| if (tPrivilege.getGrantOption() && tPrivilege.implies(requestPrivilege)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void grantPrivilege(PrivilegeObject privilege,MSentryRole role, PersistenceManager pm) throws SentryUserException { |
| MSentryGMPrivilege mPrivilege = convertToPrivilege(privilege); |
| grantRolePartial(mPrivilege, role, pm); |
| } |
| |
| private void grantRolePartial(MSentryGMPrivilege grantPrivilege, |
| MSentryRole role,PersistenceManager pm) throws SentryUserException { |
| /* |
| * If Grant is for ALL action and other actions belongs to ALL action already exists.. |
| * need to remove it and GRANT ALL action |
| */ |
| String component = grantPrivilege.getComponentName(); |
| BitFieldAction action = getAction(component, grantPrivilege.getAction()); |
| BitFieldAction allAction = getAction(component, Action.ALL); |
| |
| if (action.implies(allAction)) { |
| /* |
| * ALL action is a multi-bit set action that includes some actions such as INSERT,SELECT and CREATE. |
| */ |
| List<? extends BitFieldAction> actions = getActionFactory(component).getActionsByCode(allAction.getActionCode()); |
| for (BitFieldAction ac : actions) { |
| grantPrivilege.setAction(ac.getValue()); |
| MSentryGMPrivilege existPriv = getPrivilege(grantPrivilege, pm); |
| if (existPriv != null && role.getGmPrivileges().contains(existPriv)) { |
| /* |
| * force to load all roles related this privilege |
| * avoid the lazy-loading risk,such as: |
| * if the roles field of privilege aren't loaded, then the roles is a empty set |
| * privilege.removeRole(role) and pm.makePersistent(privilege) |
| * will remove other roles that shouldn't been removed |
| */ |
| pm.retrieve(existPriv); |
| existPriv.removeRole(role); |
| pm.makePersistent(existPriv); |
| } |
| } |
| } else { |
| /* |
| * If ALL Action already exists.. |
| * do nothing. |
| */ |
| grantPrivilege.setAction(allAction.getValue()); |
| MSentryGMPrivilege allPrivilege = getPrivilege(grantPrivilege, pm); |
| if (allPrivilege != null && role.getGmPrivileges().contains(allPrivilege)) { |
| return; |
| } |
| } |
| |
| /* |
| * restore the action |
| */ |
| grantPrivilege.setAction(action.getValue()); |
| /* |
| * check the privilege is exist or not |
| */ |
| MSentryGMPrivilege mPrivilege = getPrivilege(grantPrivilege, pm); |
| if (mPrivilege == null) { |
| mPrivilege = grantPrivilege; |
| } |
| mPrivilege.appendRole(role); |
| pm.makePersistent(mPrivilege); |
| } |
| |
| |
| public void revokePrivilege(PrivilegeObject privilege,MSentryRole role, PersistenceManager pm) throws SentryUserException { |
| MSentryGMPrivilege mPrivilege = getPrivilege(convertToPrivilege(privilege), pm); |
| if (mPrivilege == null) { |
| mPrivilege = convertToPrivilege(privilege); |
| } else { |
| mPrivilege = pm.detachCopy(mPrivilege); |
| } |
| |
| Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); |
| privilegeGraph.addAll(populateIncludePrivileges(Sets.newHashSet(role), mPrivilege, pm)); |
| |
| /* |
| * Get the privilege graph |
| * populateIncludePrivileges will get the privileges that needed revoke |
| */ |
| for (MSentryGMPrivilege persistedPriv : privilegeGraph) { |
| /* |
| * force to load all roles related this privilege |
| * avoid the lazy-loading risk,such as: |
| * if the roles field of privilege aren't loaded, then the roles is a empty set |
| * privilege.removeRole(role) and pm.makePersistent(privilege) |
| * will remove other roles that shouldn't been removed |
| */ |
| revokeRolePartial(mPrivilege, persistedPriv, role, pm); |
| } |
| pm.makePersistent(role); |
| } |
| |
| private Set<MSentryGMPrivilege> populateIncludePrivileges(Set<MSentryRole> roles, |
| MSentryGMPrivilege parent, PersistenceManager pm) { |
| Set<MSentryGMPrivilege> childrens = Sets.newHashSet(); |
| |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| QueryParamBuilder paramBuilder = populateIncludePrivilegesParams(parent); |
| |
| // add filter for role names |
| if ((roles != null) && !roles.isEmpty()) { |
| QueryParamBuilder.addRolesFilter(query, paramBuilder, SentryStore.rolesToRoleNames(roles)); |
| } |
| query.setFilter(paramBuilder.toString()); |
| |
| List<MSentryGMPrivilege> privileges = |
| (List<MSentryGMPrivilege>)query.executeWithMap(paramBuilder.getArguments()); |
| childrens.addAll(privileges); |
| return childrens; |
| } |
| |
| /** |
| * Roles can be granted multi-bit set action like ALL action on resource object. |
| * Take solr component for example, When a role has been granted ALL action but |
| * QUERY or UPDATE or CREATE are revoked, we need to remove the ALL |
| * privilege and add left privileges like UPDATE and CREATE(QUERY was revoked) or |
| * QUERY and UPDATE(CREATEE was revoked). |
| */ |
| private void revokeRolePartial(MSentryGMPrivilege revokePrivilege, |
| MSentryGMPrivilege persistedPriv, MSentryRole role, |
| PersistenceManager pm) throws SentryUserException { |
| String component = revokePrivilege.getComponentName(); |
| BitFieldAction revokeaction = getAction(component, revokePrivilege.getAction()); |
| BitFieldAction persistedAction = getAction(component, persistedPriv.getAction()); |
| BitFieldAction allAction = getAction(component, Action.ALL); |
| |
| if (revokeaction.implies(allAction)) { |
| /* |
| * if revoke action is ALL, directly revoke its children privileges and itself |
| */ |
| persistedPriv.removeRole(role); |
| pm.makePersistent(persistedPriv); |
| } else { |
| /* |
| * if persisted action is ALL, it only revoke the requested action and left partial actions |
| * like the requested action is SELECT, the UPDATE and CREATE action are left |
| */ |
| if (persistedAction.implies(allAction)) { |
| /* |
| * revoke the ALL privilege |
| */ |
| persistedPriv.removeRole(role); |
| pm.makePersistent(persistedPriv); |
| |
| List<? extends BitFieldAction> actions = getActionFactory(component).getActionsByCode(allAction.getActionCode()); |
| for (BitFieldAction ac: actions) { |
| if (ac.getActionCode() != revokeaction.getActionCode()) { |
| /* |
| * grant the left privileges to role |
| */ |
| MSentryGMPrivilege tmpPriv = new MSentryGMPrivilege(persistedPriv); |
| tmpPriv.setAction(ac.getValue()); |
| MSentryGMPrivilege leftPersistedPriv = getPrivilege(tmpPriv, pm); |
| if (leftPersistedPriv == null) { |
| //leftPersistedPriv isn't exist |
| leftPersistedPriv = tmpPriv; |
| role.appendGMPrivilege(leftPersistedPriv); |
| } |
| leftPersistedPriv.appendRole(role); |
| pm.makePersistent(leftPersistedPriv); |
| } |
| } |
| } else if (revokeaction.implies(persistedAction)) { |
| /* |
| * if the revoke action is equal to the persisted action and they aren't ALL action |
| * directly remove the role from privilege |
| */ |
| persistedPriv.removeRole(role); |
| pm.makePersistent(persistedPriv); |
| } |
| /* |
| * if the revoke action is not equal to the persisted action, |
| * do nothing |
| */ |
| } |
| } |
| |
| /** |
| * Drop any role related to the requested privilege and its children privileges |
| */ |
| public void dropPrivilege(PrivilegeObject privilege,PersistenceManager pm) throws SentryUserException { |
| MSentryGMPrivilege requestPrivilege = convertToPrivilege(privilege); |
| |
| if (Strings.isNullOrEmpty(privilege.getAction())) { |
| requestPrivilege.setAction(getAction(privilege.getComponent(), Action.ALL).getValue()); |
| } |
| /* |
| * Get the privilege graph |
| * populateIncludePrivileges will get the privileges that need dropped, |
| */ |
| Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); |
| privilegeGraph.addAll(populateIncludePrivileges(null, requestPrivilege, pm)); |
| |
| for (MSentryGMPrivilege mPrivilege : privilegeGraph) { |
| /* |
| * force to load all roles related this privilege |
| * avoid the lazy-loading |
| */ |
| pm.retrieve(mPrivilege); |
| Set<MSentryRole> roles = mPrivilege.getRoles(); |
| for (MSentryRole role : roles) { |
| revokeRolePartial(requestPrivilege, mPrivilege, role, pm); |
| } |
| } |
| } |
| |
| private MSentryGMPrivilege convertToPrivilege(PrivilegeObject privilege) { |
| return new MSentryGMPrivilege(privilege.getComponent(), |
| privilege.getService(), privilege.getAuthorizables(), |
| privilege.getAction(), privilege.getGrantOption()); |
| } |
| |
| private MSentryGMPrivilege getPrivilege(MSentryGMPrivilege privilege, PersistenceManager pm) { |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| QueryParamBuilder paramBuilder = toQueryParam(privilege); |
| query.setFilter(paramBuilder.toString()); |
| query.setUnique(true); |
| MSentryGMPrivilege result = (MSentryGMPrivilege)query.executeWithMap(paramBuilder.getArguments()); |
| return result; |
| } |
| |
| /** |
| * Get all privileges associated with a given roles |
| * @param roles Set of roles |
| * @param pm Persistence manager instance |
| * @return Set (potentially empty) of privileges associated with roles |
| */ |
| Set<PrivilegeObject> getPrivilegesByRole(Set<MSentryRole> roles, PersistenceManager pm) { |
| if (roles == null || roles.isEmpty()) { |
| return Collections.emptySet(); |
| } |
| |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| // Find privileges matching all roles |
| QueryParamBuilder paramBuilder = QueryParamBuilder.addRolesFilter(query, null, |
| SentryStore.rolesToRoleNames(roles)); |
| query.setFilter(paramBuilder.toString()); |
| List<MSentryGMPrivilege> mPrivileges = |
| (List<MSentryGMPrivilege>)query.executeWithMap(paramBuilder.getArguments()); |
| if (mPrivileges.isEmpty()) { |
| return Collections.emptySet(); |
| } |
| |
| Set<PrivilegeObject> privileges = new HashSet<>(mPrivileges.size()); |
| for (MSentryGMPrivilege mPrivilege : mPrivileges) { |
| privileges.add(new Builder() |
| .setComponent(mPrivilege.getComponentName()) |
| .setService(mPrivilege.getServiceName()) |
| .setAction(mPrivilege.getAction()) |
| .setAuthorizables(mPrivilege.getAuthorizables()) |
| .withGrantOption(mPrivilege.getGrantOption()) |
| .build()); |
| } |
| return privileges; |
| } |
| |
| Set<PrivilegeObject> getPrivilegesByProvider(String component, |
| String service, Set<MSentryRole> roles, |
| List<? extends Authorizable> authorizables, PersistenceManager pm) { |
| Set<PrivilegeObject> privileges = Sets.newHashSet(); |
| if (roles == null || roles.isEmpty()) { |
| return privileges; |
| } |
| |
| MSentryGMPrivilege parentPrivilege = new MSentryGMPrivilege(component, service, authorizables, null, null); |
| Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); |
| privilegeGraph.addAll(populateIncludePrivileges(roles, parentPrivilege, pm)); |
| |
| for (MSentryGMPrivilege mPrivilege : privilegeGraph) { |
| privileges.add(new Builder() |
| .setComponent(mPrivilege.getComponentName()) |
| .setService(mPrivilege.getServiceName()) |
| .setAction(mPrivilege.getAction()) |
| .setAuthorizables(mPrivilege.getAuthorizables()) |
| .withGrantOption(mPrivilege.getGrantOption()) |
| .build()); |
| } |
| return privileges; |
| } |
| |
| Set<MSentryGMPrivilege> getPrivilegesByAuthorizable(String component, |
| String service, Set<MSentryRole> roles, |
| List<? extends Authorizable> authorizables, PersistenceManager pm) { |
| |
| Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); |
| |
| if (roles == null || roles.isEmpty()) { |
| return privilegeGraph; |
| } |
| |
| MSentryGMPrivilege parentPrivilege = new MSentryGMPrivilege(component, service, authorizables, null, null); |
| privilegeGraph.addAll(populateIncludePrivileges(roles, parentPrivilege, pm)); |
| return privilegeGraph; |
| } |
| |
| public void renamePrivilege(String component, String service, |
| List<? extends Authorizable> oldAuthorizables, List<? extends Authorizable> newAuthorizables, |
| String grantorPrincipal, PersistenceManager pm) |
| throws SentryUserException { |
| MSentryGMPrivilege oldPrivilege = new MSentryGMPrivilege(component, service, oldAuthorizables, null, null); |
| oldPrivilege.setAction(getAction(component,Action.ALL).getValue()); |
| /* |
| * Get the privilege graph |
| * populateIncludePrivileges will get the old privileges that need dropped |
| */ |
| Set<MSentryGMPrivilege> privilegeGraph = Sets.newHashSet(); |
| privilegeGraph.addAll(populateIncludePrivileges(null, oldPrivilege, pm)); |
| |
| for (MSentryGMPrivilege dropPrivilege : privilegeGraph) { |
| /* |
| * construct the new privilege needed to add |
| */ |
| List<Authorizable> authorizables = new ArrayList<Authorizable>( |
| dropPrivilege.getAuthorizables()); |
| for (int i = 0; i < newAuthorizables.size(); i++) { |
| authorizables.set(i, newAuthorizables.get(i)); |
| } |
| MSentryGMPrivilege newPrivilge = new MSentryGMPrivilege( |
| component,service, authorizables, dropPrivilege.getAction(), |
| dropPrivilege.getGrantOption()); |
| |
| /* |
| * force to load all roles related this privilege |
| * avoid the lazy-loading |
| */ |
| pm.retrieve(dropPrivilege); |
| |
| Set<MSentryRole> roles = dropPrivilege.getRoles(); |
| for (MSentryRole role : roles) { |
| revokeRolePartial(oldPrivilege, dropPrivilege, role, pm); |
| grantRolePartial(newPrivilge, role, pm); |
| } |
| } |
| } |
| |
| private BitFieldAction getAction(String component, String name) throws SentryUserException { |
| BitFieldActionFactory actionFactory = getActionFactory(component); |
| BitFieldAction action = actionFactory.getActionByName(name); |
| if (action == null) { |
| throw new SentryUserException("Can not get BitFieldAction for name: " + name); |
| } |
| return action; |
| } |
| |
| private BitFieldActionFactory getActionFactory(String component) throws SentryUserException { |
| String caseInsensitiveComponent = component.toLowerCase(); |
| if (actionFactories.containsKey(caseInsensitiveComponent)) { |
| return actionFactories.get(caseInsensitiveComponent); |
| } |
| BitFieldActionFactory actionFactory = createActionFactory(caseInsensitiveComponent); |
| actionFactories.put(caseInsensitiveComponent, actionFactory); |
| LOGGER.info("Action factory for component {} is not found in cache. Loaded it from configuration as {}.", |
| component, actionFactory.getClass().getName()); |
| return actionFactory; |
| } |
| |
| private BitFieldActionFactory createActionFactory(String component) throws SentryUserException { |
| String actionFactoryClassName = |
| conf.get(String.format(ServiceConstants.ServerConfig.SENTRY_COMPONENT_ACTION_FACTORY_FORMAT, component)); |
| if (actionFactoryClassName == null) { |
| throw new SentryUserException("ActionFactory not defined for component " + component + |
| ". Please define the parameter " + |
| "sentry." + component + ".action.factory in configuration"); |
| } |
| Class<?> actionFactoryClass; |
| try { |
| actionFactoryClass = Class.forName(actionFactoryClassName); |
| } catch (ClassNotFoundException e) { |
| throw new SentryUserException("ActionFactory class " + actionFactoryClassName + " not found."); |
| } |
| if (!BitFieldActionFactory.class.isAssignableFrom(actionFactoryClass)) { |
| throw new SentryUserException("ActionFactory class " + actionFactoryClassName + " must extend " |
| + BitFieldActionFactory.class.getName()); |
| } |
| BitFieldActionFactory actionFactory; |
| try { |
| Constructor<?> actionFactoryConstructor = actionFactoryClass.getDeclaredConstructor(); |
| actionFactoryConstructor.setAccessible(true); |
| actionFactory = (BitFieldActionFactory) actionFactoryClass.newInstance(); |
| } catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) { |
| throw new SentryUserException("Could not instantiate actionFactory " + actionFactoryClassName + |
| " for component: " + component, e); |
| } |
| return actionFactory; |
| } |
| } |