| /** |
| * 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.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.jdo.PersistenceManager; |
| import javax.jdo.Query; |
| |
| import org.apache.sentry.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.search.SearchActionFactory; |
| 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.MSentryPrivilege; |
| import org.apache.sentry.provider.db.service.model.MSentryRole; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Strings; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| |
| /** |
| * This class used do some operations related privilege and make the results |
| * persistence |
| */ |
| public class PrivilegeOperatePersistence { |
| private static final Map<String, BitFieldActionFactory> actionFactories = Maps.newHashMap(); |
| static{ |
| actionFactories.put("solr", new SearchActionFactory()); |
| } |
| |
| public boolean checkPrivilegeOption(Set<MSentryRole> roles, PrivilegeObject privilege, PersistenceManager pm) { |
| MSentryGMPrivilege requestPrivilege = convertToPrivilege(privilege); |
| boolean hasGrant = false; |
| //get persistent privileges by roles |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| StringBuilder filters = new StringBuilder(); |
| if ((roles != null) && (roles.size() > 0)) { |
| query.declareVariables("org.apache.sentry.provider.db.service.model.MSentryRole role"); |
| List<String> rolesFiler = new LinkedList<String>(); |
| for (MSentryRole role : roles) { |
| rolesFiler.add("role.roleName == \"" + role.getRoleName() + "\" "); |
| } |
| filters.append("roles.contains(role) " + "&& (" + Joiner.on(" || ").join(rolesFiler) + ")"); |
| } |
| query.setFilter(filters.toString()); |
| |
| List<MSentryGMPrivilege> tPrivileges = (List<MSentryGMPrivilege>)query.execute(); |
| for (MSentryGMPrivilege tPrivilege : tPrivileges) { |
| if (tPrivilege.getGrantOption() && tPrivilege.implies(requestPrivilege)) { |
| hasGrant = true; |
| break; |
| } |
| } |
| return hasGrant; |
| } |
| 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) { |
| /** |
| * 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 = (MSentryGMPrivilege) 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); |
| } |
| |
| /** |
| * Explore Privilege graph and collect privileges that are belong to the specific privilege |
| */ |
| @SuppressWarnings("unchecked") |
| private Set<MSentryGMPrivilege> populateIncludePrivileges(Set<MSentryRole> roles, |
| MSentryGMPrivilege parent, PersistenceManager pm) { |
| Set<MSentryGMPrivilege> childrens = Sets.newHashSet(); |
| |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| StringBuilder filters = new StringBuilder(); |
| //add populateIncludePrivilegesQuery |
| filters.append(MSentryGMPrivilege.populateIncludePrivilegesQuery(parent)); |
| // add filter for role names |
| if ((roles != null) && (roles.size() > 0)) { |
| query.declareVariables("org.apache.sentry.provider.db.service.model.MSentryRole role"); |
| List<String> rolesFiler = new LinkedList<String>(); |
| for (MSentryRole role : roles) { |
| rolesFiler.add("role.roleName == \"" + role.getRoleName() + "\" "); |
| } |
| filters.append("&& roles.contains(role) " + "&& (" + Joiner.on(" || ").join(rolesFiler) + ")"); |
| } |
| query.setFilter(filters.toString()); |
| |
| List<MSentryGMPrivilege> privileges = (List<MSentryGMPrivilege>)query.execute(); |
| 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) { |
| 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); |
| } else { |
| /** |
| * 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) { |
| 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); |
| query.setFilter(MSentryGMPrivilege.toQuery(privilege)); |
| query.setUnique(true); |
| return (MSentryGMPrivilege)query.execute(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public Set<PrivilegeObject> getPrivilegesByRole(Set<MSentryRole> roles, PersistenceManager pm) { |
| Set<PrivilegeObject> privileges = Sets.newHashSet(); |
| if ((roles == null) || (roles.size() == 0)) { |
| return privileges; |
| } |
| Query query = pm.newQuery(MSentryGMPrivilege.class); |
| StringBuilder filters = new StringBuilder(); |
| // add filter for role names |
| query.declareVariables("org.apache.sentry.provider.db.service.model.MSentryRole role"); |
| List<String> rolesFiler = new LinkedList<String>(); |
| for (MSentryRole role : roles) { |
| rolesFiler.add("role.roleName == \"" + role.getRoleName() + "\" "); |
| } |
| filters.append("roles.contains(role) " + "&& (" + Joiner.on(" || ").join(rolesFiler) + ")"); |
| |
| query.setFilter(filters.toString()); |
| List<MSentryGMPrivilege> mPrivileges = (List<MSentryGMPrivilege>) query.execute(); |
| if ((mPrivileges == null) || (mPrivileges.size() ==0)) { |
| return privileges; |
| } |
| 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; |
| } |
| |
| public 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.size() == 0)) 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; |
| } |
| |
| |
| 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); |
| } |
| } |
| } |
| |
| public static BitFieldAction getAction(String component, String name) { |
| BitFieldActionFactory actionFactory = getActionFactory(component); |
| BitFieldAction action = actionFactory.getActionByName(name); |
| if (action == null) { |
| throw new RuntimeException("can't get BitFieldAction for name:" + name); |
| } |
| return action; |
| } |
| |
| public static BitFieldActionFactory getActionFactory(String component) { |
| BitFieldActionFactory actionFactory = actionFactories.get(component.toLowerCase()); |
| if (actionFactory == null) { |
| throw new RuntimeException("can't get actionFactory for component:" + component); |
| } |
| return actionFactory; |
| } |
| } |