/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.service.impl;
import com.liferay.exportimport.kernel.staging.MergeLayoutPrototypesThreadLocal;
import com.liferay.portal.kernel.dao.orm.QueryPos;
import com.liferay.portal.kernel.dao.orm.SQLQuery;
import com.liferay.portal.kernel.dao.orm.Session;
import com.liferay.portal.kernel.dao.orm.Type;
import com.liferay.portal.kernel.exception.NoSuchResourcePermissionException;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.model.Resource;
import com.liferay.portal.kernel.model.ResourceAction;
import com.liferay.portal.kernel.model.ResourceConstants;
import com.liferay.portal.kernel.model.ResourcePermission;
import com.liferay.portal.kernel.model.ResourcePermissionConstants;
import com.liferay.portal.kernel.model.Role;
import com.liferay.portal.kernel.model.RoleConstants;
import com.liferay.portal.kernel.search.IndexWriterHelperUtil;
import com.liferay.portal.kernel.security.auth.PrincipalException;
import com.liferay.portal.kernel.security.permission.ActionKeys;
import com.liferay.portal.kernel.security.permission.PermissionThreadLocal;
import com.liferay.portal.kernel.security.permission.PermissionUpdateHandler;
import com.liferay.portal.kernel.security.permission.PermissionUpdateHandlerRegistryUtil;
import com.liferay.portal.kernel.security.permission.ResourceActionsUtil;
import com.liferay.portal.kernel.service.ExceptionRetryAcceptor;
import com.liferay.portal.kernel.spring.aop.Property;
import com.liferay.portal.kernel.spring.aop.Retry;
import com.liferay.portal.kernel.transaction.TransactionCommitCallbackUtil;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.security.permission.PermissionCacheUtil;
import com.liferay.portal.service.base.ResourcePermissionLocalServiceBaseImpl;
import com.liferay.portal.util.PropsValues;
import com.liferay.portal.util.ResourcePermissionsThreadLocal;
import com.liferay.util.dao.orm.CustomSQLUtil;
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.Set;
import java.util.concurrent.Callable;
/**
* Provides the local service for accessing, adding, checking, deleting,
* granting, and revoking resource permissions.
*
* <p>
* Before attempting to read any of the documentation for this class, first read
* {@link com.liferay.portal.model.impl.ResourcePermissionImpl} for an
* explanation of scoping.
* </p>
*
* @author Brian Wing Shun Chan
* @author Raymond Augé
* @author Connor McKay
*/
public class ResourcePermissionLocalServiceImpl
extends ResourcePermissionLocalServiceBaseImpl {
/**
* @see com.liferay.portal.verify.VerifyPermission#fixOrganizationRolePermissions
*/
public static final String[] EMPTY_ACTION_IDS = {null};
/**
* Grants the role permission at the scope to perform the action on
* resources of the type. Existing actions are retained.
*
* <p>
* This method cannot be used to grant individual scope permissions, but is
* only intended for adding permissions at the company, group, and
* group-template scopes. For example, this method could be used to grant a
* company scope permission to edit message board posts.
* </p>
*
* <p>
* If a company scope permission is granted to resources that the role
* already had group scope permissions to, the group scope permissions are
* deleted. Likewise, if a group scope permission is granted to resources
* that the role already had company scope permissions to, the company scope
* permissions are deleted. Be aware that this latter behavior can result in
* an overall reduction in permissions for the role.
* </p>
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope. This method only supports company, group, and
* group-template scope.
* @param primKey the primary key
* @param roleId the primary key of the role
* @param actionId the action ID
*/
@Override
@Retry(
acceptor = ExceptionRetryAcceptor.class,
properties = {
@Property(
name = ExceptionRetryAcceptor.EXCEPTION_NAME,
value = "org.springframework.dao.DataIntegrityViolationException"
)
}
)
public void addResourcePermission(
long companyId, String name, int scope, String primKey, long roleId,
String actionId)
throws PortalException {
if (scope == ResourceConstants.SCOPE_COMPANY) {
// Remove group permission
removeResourcePermissions(
companyId, name, ResourceConstants.SCOPE_GROUP, roleId,
actionId);
}
else if (scope == ResourceConstants.SCOPE_GROUP) {
// Remove company permission
removeResourcePermissions(
companyId, name, ResourceConstants.SCOPE_COMPANY, roleId,
actionId);
}
else if (scope == ResourceConstants.SCOPE_INDIVIDUAL) {
throw new NoSuchResourcePermissionException();
}
updateResourcePermission(
companyId, name, scope, primKey, roleId, 0, new String[] {actionId},
ResourcePermissionConstants.OPERATOR_ADD);
}
/**
* Grants the role permissions at the scope to perform the actions on all
* resources of the type. Existing actions are retained.
*
* <p>
* This method should only be used to add default permissions to existing
* resources en masse during upgrades or while verifying permissions. For
* example, this method could be used to grant site members individual scope
* permissions to view all blog posts.
* </p>
*
* @param resourceName the resource's name, which can be either a class name
* or a portlet ID
* @param roleName the role's name
* @param scope the scope
* @param resourceActionBitwiseValue the bitwise IDs of the actions
*/
@Override
public void addResourcePermissions(
String resourceName, String roleName, int scope,
long resourceActionBitwiseValue) {
List<Role> roles = rolePersistence.findByName(roleName);
if (roles.isEmpty()) {
return;
}
Session session = resourcePermissionPersistence.openSession();
try {
// Update existing resource permissions
String sql = CustomSQLUtil.get(_UPDATE_ACTION_IDS);
sql = StringUtil.replace(
sql, "[$ROLE_ID$]",
ListUtil.toString(roles, Role.ROLE_ID_ACCESSOR));
SQLQuery sqlQuery = session.createSynchronizedSQLQuery(sql);
QueryPos qPos = QueryPos.getInstance(sqlQuery);
qPos.add(resourceActionBitwiseValue);
qPos.add(resourceActionBitwiseValue);
qPos.add(resourceName);
qPos.add(scope);
sqlQuery.executeUpdate();
// Add missing resource permissions
sql = CustomSQLUtil.get(_FIND_MISSING_RESOURCE_PERMISSIONS);
sqlQuery = session.createSynchronizedSQLQuery(sql);
sqlQuery.addScalar("companyId", Type.LONG);
sqlQuery.addScalar("name", Type.STRING);
sqlQuery.addScalar("scope", Type.INTEGER);
sqlQuery.addScalar("primKey", Type.STRING);
sqlQuery.addScalar("roleId", Type.LONG);
qPos = QueryPos.getInstance(sqlQuery);
qPos.add(resourceName);
qPos.add(scope);
qPos.add(roleName);
List<Object[]> resourcePermissionArrays = sqlQuery.list(true);
if (resourcePermissionArrays.isEmpty()) {
return;
}
for (Object[] resourcePermissionArray : resourcePermissionArrays) {
long resourcePermissionId = counterLocalService.increment(
ResourcePermission.class.getName());
ResourcePermission resourcePermission =
resourcePermissionPersistence.create(resourcePermissionId);
resourcePermission.setCompanyId(
(Long)resourcePermissionArray[0]);
resourcePermission.setName((String)resourcePermissionArray[1]);
resourcePermission.setScope(
(Integer)resourcePermissionArray[2]);
String primKey = (String)resourcePermissionArray[3];
resourcePermission.setPrimKey(primKey);
resourcePermission.setPrimKeyId(GetterUtil.getLong(primKey));
resourcePermission.setRoleId((Long)resourcePermissionArray[4]);
resourcePermission.setActionIds(resourceActionBitwiseValue);
resourcePermission.setViewActionId(
resourceActionBitwiseValue % 2 == 1);
session.save(resourcePermission);
PermissionCacheUtil.clearResourcePermissionCache(
resourcePermission.getScope(), resourcePermission.getName(),
resourcePermission.getPrimKey());
}
}
catch (Exception e) {
throw new SystemException(e);
}
finally {
resourcePermissionPersistence.closeSession(session);
resourcePermissionPersistence.clearCache();
}
}
/**
* Deletes all resource permissions at the scope to resources of the type.
* This method should not be confused with any of the
* <code>removeResourcePermission</code> methods, as its purpose is very
* different. This method should only be used for deleting resource
* permissions that refer to a resource when that resource is deleted. For
* example this method could be used to delete all individual scope
* permissions to a blog post when it is deleted.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
*/
@Override
public void deleteResourcePermissions(
long companyId, String name, int scope, long primKey)
throws PortalException {
deleteResourcePermissions(
companyId, name, scope, String.valueOf(primKey));
}
/**
* Deletes all resource permissions at the scope to resources of the type.
* This method should not be confused with any of the
* <code>removeResourcePermission</code> methods, as its purpose is very
* different. This method should only be used for deleting resource
* permissions that refer to a resource when that resource is deleted. For
* example this method could be used to delete all individual scope
* permissions to a blog post when it is deleted.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
*/
@Override
public void deleteResourcePermissions(
long companyId, String name, int scope, String primKey)
throws PortalException {
List<ResourcePermission> resourcePermissions =
resourcePermissionPersistence.findByC_N_S_P(
companyId, name, scope, primKey);
for (ResourcePermission resourcePermission : resourcePermissions) {
deleteResourcePermission(
resourcePermission.getResourcePermissionId());
}
}
@Override
public ResourcePermission fetchResourcePermission(
long companyId, String name, int scope, String primKey, long roleId) {
return resourcePermissionPersistence.fetchByC_N_S_P_R(
companyId, name, scope, primKey, roleId);
}
@Override
public Map<Long, Set<String>> getAvailableResourcePermissionActionIds(
long companyId, String name, int scope, String primKey,
Collection<String> actionIds) {
if (actionIds.isEmpty()) {
return Collections.emptyMap();
}
List<ResourcePermission> resourcePermissions = getResourcePermissions(
companyId, name, scope, primKey);
Map<Long, Set<String>> roleIdsToActionIds = new HashMap<>(
resourcePermissions.size());
for (ResourcePermission resourcePermission : resourcePermissions) {
if (resourcePermission.getActionIds() == 0) {
roleIdsToActionIds.put(
resourcePermission.getRoleId(),
Collections.<String>emptySet());
continue;
}
Set<String> availableActionIds = new HashSet<>(actionIds.size());
for (String actionId : actionIds) {
if (resourcePermission.hasActionId(actionId)) {
availableActionIds.add(actionId);
}
}
if (!availableActionIds.isEmpty()) {
roleIdsToActionIds.put(
resourcePermission.getRoleId(), availableActionIds);
}
}
return roleIdsToActionIds;
}
/**
* Returns the intersection of action IDs the role has permission at the
* scope to perform on resources of the type.
*
* @param companyId he primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @param actionIds the action IDs
* @return the intersection of action IDs the role has permission at the
* scope to perform on resources of the type
*/
@Override
public List<String> getAvailableResourcePermissionActionIds(
long companyId, String name, int scope, String primKey, long roleId,
Collection<String> actionIds)
throws PortalException {
ResourcePermission resourcePermission =
resourcePermissionPersistence.fetchByC_N_S_P_R(
companyId, name, scope, primKey, roleId);
if (resourcePermission == null) {
return Collections.emptyList();
}
List<String> availableActionIds = new ArrayList<>(actionIds.size());
for (String actionId : actionIds) {
ResourceAction resourceAction =
resourceActionLocalService.getResourceAction(name, actionId);
if (hasActionId(resourcePermission, resourceAction)) {
availableActionIds.add(actionId);
}
}
return availableActionIds;
}
/**
* @deprecated As of 7.0.0, replaced by {@link
* #getAvailableResourcePermissionActionIds(long, String, int,
* String, Collection)}
*/
@Deprecated
@Override
public Map<Long, Set<String>> getAvailableResourcePermissionActionIds(
long companyId, String name, int scope, String primKey, long[] roleIds,
Collection<String> actionIds) {
return getAvailableResourcePermissionActionIds(
companyId, name, scope, primKey, new ArrayList<String>(actionIds));
}
/**
* Returns the resource permission for the role at the scope to perform the
* actions on resources of the type.
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @return the resource permission for the role at the scope to perform the
* actions on resources of the type
*/
@Override
public ResourcePermission getResourcePermission(
long companyId, String name, int scope, String primKey, long roleId)
throws PortalException {
return resourcePermissionPersistence.findByC_N_S_P_R(
companyId, name, scope, primKey, roleId);
}
/**
* Returns all the resource permissions at the scope of the type.
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @return the resource permissions at the scope of the type
*/
@Override
public List<ResourcePermission> getResourcePermissions(
long companyId, String name, int scope, String primKey) {
return resourcePermissionPersistence.findByC_N_S_P(
companyId, name, scope, primKey);
}
/**
* Returns the number of resource permissions at the scope of the type.
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @return the number of resource permissions at the scope of the type
*/
@Override
public int getResourcePermissionsCount(
long companyId, String name, int scope, String primKey) {
return resourcePermissionPersistence.countByC_N_S_P(
companyId, name, scope, primKey);
}
/**
* Returns the resource permissions that apply to the resource.
*
* @param companyId the primary key of the resource's company
* @param groupId the primary key of the resource's group
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param primKey the primary key of the resource
* @return the resource permissions associated with the resource
*/
@Override
public List<ResourcePermission> getResourceResourcePermissions(
long companyId, long groupId, String name, String primKey) {
return resourcePermissionFinder.findByResource(
companyId, groupId, name, primKey);
}
/**
* Returns all the resource permissions for the role.
*
* @param roleId the primary key of the role
* @return the resource permissions for the role
*/
@Override
public List<ResourcePermission> getRoleResourcePermissions(long roleId) {
return resourcePermissionPersistence.findByRoleId(roleId);
}
/**
* Returns a range of all the resource permissions for the role at the
* scopes.
*
* <p>
* Useful when paginating results. Returns a maximum of <code>end -
* start</code> instances. <code>start</code> and <code>end</code> are not
* primary keys, they are indexes in the result set. Thus, <code>0</code>
* refers to the first result in the set. Setting both <code>start</code>
* and <code>end</code> to {@link
* com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
* result set.
* </p>
*
* @param roleId the primary key of the role
* @param scopes the scopes
* @param start the lower bound of the range of results
* @param end the upper bound of the range of results (not inclusive)
* @return the range of resource permissions for the role at the scopes
*/
@Override
public List<ResourcePermission> getRoleResourcePermissions(
long roleId, int[] scopes, int start, int end) {
return resourcePermissionFinder.findByR_S(roleId, scopes, start, end);
}
@Override
public List<Role> getRoles(
long companyId, String name, int scope, String primKey,
String actionId)
throws PortalException {
List<ResourcePermission> resourcePermissions =
resourcePermissionPersistence.findByC_N_S_P(
companyId, name, scope, primKey);
if (resourcePermissions.isEmpty()) {
return Collections.emptyList();
}
ResourceAction resourceAction =
resourceActionLocalService.getResourceAction(name, actionId);
Set<Long> rolesIds = new HashSet<>();
for (ResourcePermission resourcePermission : resourcePermissions) {
if (resourcePermission.hasAction(resourceAction)) {
rolesIds.add(resourcePermission.getRoleId());
}
}
List<Role> roles = new ArrayList<>(rolesIds.size());
for (long roleId : rolesIds) {
roles.add(roleLocalService.getRole(roleId));
}
return roles;
}
/**
* Returns all the resource permissions where scope = any ?.
*
* <p>
* Useful when paginating results. Returns a maximum of <code>end -
* start</code> instances. <code>start</code> and <code>end</code> are not
* primary keys, they are indexes in the result set. Thus, <code>0</code>
* refers to the first result in the set. Setting both <code>start</code>
* and <code>end</code> to {@link
* com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
* result set.
* </p>
*
* @param scopes the scopes
* @return the resource permissions where scope = any ?
*/
@Override
public List<ResourcePermission> getScopeResourcePermissions(int[] scopes) {
return resourcePermissionPersistence.findByScope(scopes);
}
/**
* Returns <code>true</code> if the resource permission grants permission to
* perform the resource action. Note that this method does not ensure that
* the resource permission refers to the same type of resource as the
* resource action.
*
* @param resourcePermission the resource permission
* @param resourceAction the resource action
* @return <code>true</code> if the resource permission grants permission to
* perform the resource action
*/
@Override
public boolean hasActionId(
ResourcePermission resourcePermission, ResourceAction resourceAction) {
long actionIds = resourcePermission.getActionIds();
long bitwiseValue = resourceAction.getBitwiseValue();
if ((actionIds & bitwiseValue) == bitwiseValue) {
return true;
}
else {
return false;
}
}
/**
* Returns <code>true</code> if the roles have permission at the scope to
* perform the action on the resources.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param resources the resources
* @param roleIds the primary keys of the roles
* @param actionId the action ID
* @return <code>true</code> if any one of the roles has permission to
* perform the action on any one of the resources;
* <code>false</code> otherwise
*/
@Override
public boolean hasResourcePermission(
List<Resource> resources, long[] roleIds, String actionId)
throws PortalException {
if (roleIds.length == 0) {
return false;
}
int size = resources.size();
if (size < 2) {
throw new IllegalArgumentException(
"The list of resources must contain at least two values");
}
Resource individualResource = resources.get(0);
if (individualResource.getScope() !=
ResourceConstants.SCOPE_INDIVIDUAL) {
throw new IllegalArgumentException(
"The first resource must be an individual scope");
}
Resource companyResource = resources.get(size - 1);
if (companyResource.getScope() != ResourceConstants.SCOPE_COMPANY) {
throw new IllegalArgumentException(
"The last resource must be a company scope");
}
// See LPS-47464
if (resourcePermissionPersistence.countByC_N_S_P(
individualResource.getCompanyId(), individualResource.getName(),
individualResource.getScope(),
individualResource.getPrimKey()) < 1) {
StringBundler sb = new StringBundler(9);
sb.append("{companyId=");
sb.append(individualResource.getCompanyId());
sb.append(", name=");
sb.append(individualResource.getName());
sb.append(", primKey=");
sb.append(individualResource.getPrimKey());
sb.append(", scope=");
sb.append(individualResource.getScope());
sb.append("}");
throw new NoSuchResourcePermissionException(sb.toString());
}
// Iterate the list of resources in reverse order to test permissions
// from company scope to individual scope because it is more likely that
// a permission is assigned at a higher scope. Optimizing this method to
// one SQL call may actually slow things down since most of the calls
// will pull from the cache after the first request.
for (int i = size - 1; i >= 0; i--) {
Resource resource = resources.get(i);
if (hasResourcePermission(
resource.getCompanyId(), resource.getName(),
resource.getScope(), resource.getPrimKey(), roleIds,
actionId)) {
return true;
}
}
return false;
}
/**
* Returns <code>true</code> if the role has permission at the scope to
* perform the action on resources of the type.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @param actionId the action ID
* @return <code>true</code> if the role has permission to perform the
* action on the resource; <code>false</code> otherwise
*/
@Override
public boolean hasResourcePermission(
long companyId, String name, int scope, String primKey, long roleId,
String actionId)
throws PortalException {
ResourcePermission resourcePermission =
resourcePermissionPersistence.fetchByC_N_S_P_R(
companyId, name, scope, primKey, roleId);
if (resourcePermission == null) {
return false;
}
ResourceAction resourceAction =
resourceActionLocalService.getResourceAction(name, actionId);
if (hasActionId(resourcePermission, resourceAction)) {
return true;
}
return false;
}
/**
* Returns <code>true</code> if the roles have permission at the scope to
* perform the action on resources of the type.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleIds the primary keys of the roles
* @param actionId the action ID
* @return <code>true</code> if any one of the roles has permission to
* perform the action on the resource; <code>false</code> otherwise
*/
@Override
public boolean hasResourcePermission(
long companyId, String name, int scope, String primKey,
long[] roleIds, String actionId)
throws PortalException {
if (roleIds.length == 0) {
return false;
}
ResourceAction resourceAction =
resourceActionLocalService.getResourceAction(name, actionId);
if (roleIds.length >
PropsValues.
PERMISSIONS_ROLE_RESOURCE_PERMISSION_QUERY_THRESHOLD) {
int count = resourcePermissionFinder.countByC_N_S_P_R_A(
companyId, name, scope, primKey, roleIds,
resourceAction.getBitwiseValue());
if (count > 0) {
return true;
}
}
else {
List<ResourcePermission> resourcePermissions =
resourcePermissionPersistence.findByC_N_S_P_R(
companyId, name, scope, primKey, roleIds);
if (resourcePermissions.isEmpty()) {
return false;
}
for (ResourcePermission resourcePermission : resourcePermissions) {
if (hasActionId(resourcePermission, resourceAction)) {
return true;
}
}
}
return false;
}
/**
* @deprecated As of 7.0.0, replaced by {@link #getRoles(long, String, int,
* String, String}
*/
@Deprecated
@Override
public boolean[] hasResourcePermissions(
long companyId, String name, int scope, String primKey,
long[] roleIds, String actionId)
throws PortalException {
boolean[] hasResourcePermissions = new boolean[roleIds.length];
if (roleIds.length == 0) {
return hasResourcePermissions;
}
ResourceAction resourceAction =
resourceActionLocalService.getResourceAction(name, actionId);
List<ResourcePermission> resourcePermissions =
resourcePermissionPersistence.findByC_N_S_P_R(
companyId, name, scope, primKey, roleIds);
if (resourcePermissions.isEmpty()) {
return hasResourcePermissions;
}
for (ResourcePermission resourcePermission : resourcePermissions) {
if (hasActionId(resourcePermission, resourceAction)) {
long roleId = resourcePermission.getRoleId();
for (int i = 0; i < roleIds.length; i++) {
if (roleIds[i] == roleId) {
hasResourcePermissions[i] = true;
break;
}
}
}
}
return hasResourcePermissions;
}
/**
* Returns <code>true</code> if the role has permission at the scope to
* perform the action on the resource.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param roleId the primary key of the role
* @param actionId the action ID
* @return <code>true</code> if the role has permission to perform the
* action on the resource; <code>false</code> otherwise
*/
@Override
public boolean hasScopeResourcePermission(
long companyId, String name, int scope, long roleId,
String actionId)
throws PortalException {
List<ResourcePermission> resourcePermissions =
resourcePermissionPersistence.findByC_N_S(companyId, name, scope);
for (ResourcePermission resourcePermission : resourcePermissions) {
if (hasResourcePermission(
companyId, name, scope, resourcePermission.getPrimKey(),
roleId, actionId)) {
return true;
}
}
return false;
}
/**
* Reassigns all the resource permissions from the source role to the
* destination role, and deletes the source role.
*
* @param fromRoleId the primary key of the source role
* @param toRoleId the primary key of the destination role
*/
@Override
public void mergePermissions(long fromRoleId, long toRoleId)
throws PortalException {
Role fromRole = rolePersistence.findByPrimaryKey(fromRoleId);
Role toRole = rolePersistence.findByPrimaryKey(toRoleId);
if (fromRole.getType() != toRole.getType()) {
throw new PortalException("Role types are mismatched");
}
else if (toRole.isSystem()) {
throw new PortalException("Cannot move permissions to system role");
}
else if (fromRole.isSystem()) {
throw new PortalException(
"Cannot move permissions from system role");
}
List<ResourcePermission> resourcePermissions =
getRoleResourcePermissions(fromRoleId);
for (ResourcePermission resourcePermission : resourcePermissions) {
resourcePermission.setRoleId(toRoleId);
resourcePermissionPersistence.update(resourcePermission);
}
roleLocalService.deleteRole(fromRoleId);
}
/**
* Grants the role default permissions to all the resources of the type and
* at the scope stored in the resource permission, deletes the resource
* permission, and deletes the resource permission's role if it has no
* permissions remaining.
*
* @param resourcePermissionId the primary key of the resource permission
* @param toRoleId the primary key of the role
*/
@Override
public void reassignPermissions(long resourcePermissionId, long toRoleId)
throws PortalException {
ResourcePermission resourcePermission = getResourcePermission(
resourcePermissionId);
long companyId = resourcePermission.getCompanyId();
String name = resourcePermission.getName();
int scope = resourcePermission.getScope();
String primKey = resourcePermission.getPrimKey();
long fromRoleId = resourcePermission.getRoleId();
Role toRole = roleLocalService.getRole(toRoleId);
List<String> actionIds = null;
if (toRole.getType() == RoleConstants.TYPE_REGULAR) {
actionIds = ResourceActionsUtil.getModelResourceActions(name);
}
else {
actionIds = ResourceActionsUtil.getModelResourceGroupDefaultActions(
name);
}
setResourcePermissions(
companyId, name, scope, primKey, toRoleId,
actionIds.toArray(new String[actionIds.size()]));
resourcePermissionPersistence.remove(resourcePermissionId);
List<ResourcePermission> resourcePermissions =
getRoleResourcePermissions(fromRoleId);
if (resourcePermissions.isEmpty()) {
roleLocalService.deleteRole(fromRoleId);
}
}
/**
* Revokes permission at the scope from the role to perform the action on
* resources of the type. For example, this method could be used to revoke a
* group scope permission to edit blog posts.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @param actionId the action ID
*/
@Override
public void removeResourcePermission(
long companyId, String name, int scope, String primKey, long roleId,
String actionId)
throws PortalException {
updateResourcePermission(
companyId, name, scope, primKey, roleId, 0, new String[] {actionId},
ResourcePermissionConstants.OPERATOR_REMOVE);
}
/**
* Revokes all permissions at the scope from the role to perform the action
* on resources of the type. For example, this method could be used to
* revoke all individual scope permissions to edit blog posts from site
* members.
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param roleId the primary key of the role
* @param actionId the action ID
*/
@Override
public void removeResourcePermissions(
long companyId, String name, int scope, long roleId,
String actionId)
throws PortalException {
List<ResourcePermission> resourcePermissions =
resourcePermissionPersistence.findByC_N_S(companyId, name, scope);
for (ResourcePermission resourcePermission : resourcePermissions) {
updateResourcePermission(
companyId, name, scope, resourcePermission.getPrimKey(), roleId,
0, new String[] {actionId},
ResourcePermissionConstants.OPERATOR_REMOVE);
}
}
/**
* Updates the role's permissions at the scope, setting the actions that can
* be performed on resources of the type, also setting the owner of any
* newly created resource permissions. Existing actions are replaced.
*
* <p>
* This method can be used to set permissions at any scope, but it is
* generally only used at the individual scope. For example, it could be
* used to set the guest permissions on a blog post.
* </p>
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @param ownerId the primary key of the owner (generally the user that
* created the resource)
* @param actionIds the action IDs of the actions
*/
@Override
@Retry(
acceptor = ExceptionRetryAcceptor.class,
properties = {
@Property(
name = ExceptionRetryAcceptor.EXCEPTION_NAME,
value = "org.springframework.dao.DataIntegrityViolationException"
)
}
)
public void setOwnerResourcePermissions(
long companyId, String name, int scope, String primKey, long roleId,
long ownerId, String[] actionIds)
throws PortalException {
updateResourcePermission(
companyId, name, scope, primKey, roleId, ownerId, actionIds,
ResourcePermissionConstants.OPERATOR_SET);
}
/**
* Updates the role's permissions at the scope, setting the actions that can
* be performed on resources of the type. Existing actions are replaced.
*
* <p>
* This method can be used to set permissions at any scope, but it is
* generally only used at the individual scope. For example, it could be
* used to set the guest permissions on a blog post.
* </p>
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @param actionIds the action IDs of the actions
*/
@Override
@Retry(
acceptor = ExceptionRetryAcceptor.class,
properties = {
@Property(
name = ExceptionRetryAcceptor.EXCEPTION_NAME,
value = "org.springframework.dao.DataIntegrityViolationException"
)
}
)
public void setResourcePermissions(
long companyId, String name, int scope, String primKey, long roleId,
String[] actionIds)
throws PortalException {
updateResourcePermission(
companyId, name, scope, primKey, roleId, 0, actionIds,
ResourcePermissionConstants.OPERATOR_SET);
}
/**
* Updates the role's permissions at the scope, setting the actions that can
* be performed on resources of the type. Existing actions are replaced.
*
* <p>
* This method can be used to set permissions at any scope, but it is
* generally only used at the individual scope. For example, it could be
* used to set the guest permissions on a blog post.
* </p>
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleIdsToActionIds a map of role IDs to action IDs of the actions
*/
@Override
@Retry(
acceptor = ExceptionRetryAcceptor.class,
properties = {
@Property(
name = ExceptionRetryAcceptor.EXCEPTION_NAME,
value = "org.springframework.dao.DataIntegrityViolationException"
)
}
)
public void setResourcePermissions(
long companyId, String name, int scope, String primKey,
Map<Long, String[]> roleIdsToActionIds)
throws PortalException {
updateResourcePermission(
companyId, name, scope, primKey, 0, roleIdsToActionIds);
}
protected void doUpdateResourcePermission(
long companyId, String name, int scope, String primKey,
long ownerId, long roleId, String[] actionIds, int operator,
boolean fetch)
throws PortalException {
ResourcePermission resourcePermission = null;
Map<Long, ResourcePermission> resourcePermissionsMap =
ResourcePermissionsThreadLocal.getResourcePermissions();
if (resourcePermissionsMap != null) {
resourcePermission = resourcePermissionsMap.get(roleId);
}
else if (fetch) {
resourcePermission = resourcePermissionPersistence.fetchByC_N_S_P_R(
companyId, name, scope, primKey, roleId);
}
if (resourcePermission == null) {
if (((operator == ResourcePermissionConstants.OPERATOR_ADD) ||
(operator == ResourcePermissionConstants.OPERATOR_SET)) &&
(actionIds.length == 0)) {
return;
}
if (operator == ResourcePermissionConstants.OPERATOR_REMOVE) {
return;
}
long resourcePermissionId = counterLocalService.increment(
ResourcePermission.class.getName());
resourcePermission = resourcePermissionPersistence.create(
resourcePermissionId);
resourcePermission.setCompanyId(companyId);
resourcePermission.setName(name);
resourcePermission.setScope(scope);
resourcePermission.setPrimKey(primKey);
resourcePermission.setPrimKeyId(GetterUtil.getLong(primKey));
resourcePermission.setRoleId(roleId);
resourcePermission.setOwnerId(ownerId);
if (resourcePermissionsMap != null) {
resourcePermissionsMap.put(roleId, resourcePermission);
}
}
List<String> unsupportedActionIds = Collections.emptyList();
if (((operator == ResourcePermissionConstants.OPERATOR_ADD) ||
(operator == ResourcePermissionConstants.OPERATOR_SET)) &&
isGuestRoleId(companyId, roleId)) {
unsupportedActionIds =
ResourceActionsUtil.getResourceGuestUnsupportedActions(
name, name);
}
long actionIdsLong = resourcePermission.getActionIds();
if (operator == ResourcePermissionConstants.OPERATOR_SET) {
actionIdsLong = 0;
}
for (String actionId : actionIds) {
if (actionId == null) {
break;
}
if (unsupportedActionIds.contains(actionId)) {
throw new PrincipalException(
actionId + "is not supported by role " + roleId);
}
ResourceAction resourceAction =
resourceActionLocalService.getResourceAction(name, actionId);
if ((operator == ResourcePermissionConstants.OPERATOR_ADD) ||
(operator == ResourcePermissionConstants.OPERATOR_SET)) {
actionIdsLong |= resourceAction.getBitwiseValue();
}
else {
actionIdsLong =
actionIdsLong & (~resourceAction.getBitwiseValue());
}
}
if ((actionIdsLong != resourcePermission.getActionIds()) ||
resourcePermission.isNew()) {
resourcePermission.setActionIds(actionIdsLong);
resourcePermission.setViewActionId(actionIdsLong % 2 == 1);
resourcePermissionPersistence.update(resourcePermission);
if (ArrayUtil.contains(actionIds, ActionKeys.MANAGE_SUBGROUPS)) {
PermissionCacheUtil.clearPrimaryKeyRoleCache();
}
IndexWriterHelperUtil.updatePermissionFields(name, primKey);
}
}
protected boolean isGuestRoleId(long companyId, long roleId)
throws PortalException {
Role guestRole = roleLocalService.getRole(
companyId, RoleConstants.GUEST);
if (roleId == guestRole.getRoleId()) {
return true;
}
return false;
}
/**
* Updates the role's permissions at the scope, either adding to, removing
* from, or setting the actions that can be performed on resources of the
* type. Automatically creates a new resource permission if none exists, or
* deletes the existing resource permission if it no longer grants
* permissions to perform any action.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param roleId the primary key of the role
* @param ownerId the primary key of the owner
* @param actionIds the action IDs of the actions
* @param operator whether to add to, remove from, or set/replace the
* existing actions. Possible values can be found in {@link
* ResourcePermissionConstants}.
*/
protected void updateResourcePermission(
long companyId, String name, int scope, String primKey, long roleId,
long ownerId, String[] actionIds, int operator)
throws PortalException {
doUpdateResourcePermission(
companyId, name, scope, primKey, ownerId, roleId, actionIds,
operator, true);
}
/**
* Updates the role's permissions at the scope, either adding to, removing
* from, or setting the actions that can be performed on resources of the
* type. Automatically creates a new resource permission if none exists, or
* deletes the existing resource permission if it no longer grants
* permissions to perform any action.
*
* <p>
* Depending on the scope, the value of <code>primKey</code> will have
* different meanings. For more information, see {@link
* com.liferay.portal.model.impl.ResourcePermissionImpl}.
* </p>
*
* @param companyId the primary key of the company
* @param name the resource's name, which can be either a class name or a
* portlet ID
* @param scope the scope
* @param primKey the primary key
* @param ownerId the primary key of the owner
*/
protected void updateResourcePermission(
long companyId, String name, int scope, String primKey,
long ownerId, Map<Long, String[]> roleIdsToActionIds)
throws PortalException {
boolean flushResourcePermissionEnabled =
PermissionThreadLocal.isFlushResourcePermissionEnabled(
name, primKey);
PermissionThreadLocal.setFlushResourcePermissionEnabled(
name, primKey, false);
try {
long[] roleIds = ArrayUtil.toLongArray(roleIdsToActionIds.keySet());
List<ResourcePermission> resourcePermissions = new ArrayList<>(
roleIds.length);
int batchSize = 1000;
int start = 0;
while (start < (roleIds.length - batchSize)) {
resourcePermissions.addAll(
resourcePermissionPersistence.findByC_N_S_P_R(
companyId, name, scope, primKey,
ArrayUtil.subset(roleIds, start, start + batchSize)));
start += batchSize;
}
resourcePermissions.addAll(
resourcePermissionPersistence.findByC_N_S_P_R(
companyId, name, scope, primKey,
ArrayUtil.subset(roleIds, start, roleIds.length)));
roleIdsToActionIds = new HashMap<>(roleIdsToActionIds);
for (ResourcePermission resourcePermission : resourcePermissions) {
long roleId = resourcePermission.getRoleId();
String[] actionIds = roleIdsToActionIds.remove(roleId);
doUpdateResourcePermission(
companyId, name, scope, primKey, ownerId, roleId, actionIds,
ResourcePermissionConstants.OPERATOR_SET, true);
}
if (roleIdsToActionIds.isEmpty()) {
return;
}
for (Map.Entry<Long, String[]> entry :
roleIdsToActionIds.entrySet()) {
long roleId = entry.getKey();
String[] actionIds = entry.getValue();
doUpdateResourcePermission(
companyId, name, scope, primKey, ownerId, roleId, actionIds,
ResourcePermissionConstants.OPERATOR_SET, false);
}
if (!MergeLayoutPrototypesThreadLocal.isInProgress()) {
TransactionCommitCallbackUtil.registerCallback(
new UpdateResourcePermissionCallable(name, primKey));
}
}
finally {
PermissionThreadLocal.setFlushResourcePermissionEnabled(
name, primKey, flushResourcePermissionEnabled);
PermissionCacheUtil.clearResourcePermissionCache(
scope, name, primKey);
IndexWriterHelperUtil.updatePermissionFields(name, primKey);
}
}
private static final String _FIND_MISSING_RESOURCE_PERMISSIONS =
ResourcePermissionLocalServiceImpl.class.getName() +
".findMissingResourcePermissions";
private static final String _UPDATE_ACTION_IDS =
ResourcePermissionLocalServiceImpl.class.getName() + ".updateActionIds";
private static class UpdateResourcePermissionCallable
implements Callable<Void> {
public UpdateResourcePermissionCallable(String name, String primKey) {
_name = name;
_primKey = primKey;
}
@Override
public Void call() {
PermissionUpdateHandler permissionUpdateHandler =
PermissionUpdateHandlerRegistryUtil.getPermissionUpdateHandler(
_name);
if (permissionUpdateHandler == null) {
return null;
}
permissionUpdateHandler.updatedPermission(_primKey);
return null;
}
private final String _name;
private final String _primKey;
}
}