/** * 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.security.permission; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Group; import com.liferay.portal.kernel.model.GroupConstants; import com.liferay.portal.kernel.model.ResourceConstants; import com.liferay.portal.kernel.security.pacl.DoPrivileged; import com.liferay.portal.kernel.security.permission.ActionKeys; import com.liferay.portal.kernel.security.permission.InlineSQLHelper; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.service.GroupLocalServiceUtil; import com.liferay.portal.kernel.service.ResourceBlockLocalServiceUtil; import com.liferay.portal.kernel.service.ResourcePermissionLocalServiceUtil; import com.liferay.portal.kernel.service.ResourceTypePermissionLocalServiceUtil; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.CharPool; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.util.PropsValues; import com.liferay.util.dao.orm.CustomSQLUtil; import java.util.HashSet; import java.util.Set; /** * @author Raymond Augé * @author Connor McKay */ @DoPrivileged public class InlineSQLHelperImpl implements InlineSQLHelper { public static final String FILTER_BY_RESOURCE_BLOCK_ID = InlineSQLHelper.class.getName() + ".filterByResourceBlockId"; public static final String FILTER_BY_RESOURCE_BLOCK_ID_OWNER = InlineSQLHelper.class.getName() + ".filterByResourceBlockIdOwner"; public static final String FIND_BY_RESOURCE_BLOCK_ID = InlineSQLHelper.class.getName() + ".findByResourceBlockId"; public static final String FIND_BY_RESOURCE_PERMISSION = InlineSQLHelper.class.getName() + ".findByResourcePermission"; @Override public boolean isEnabled() { return isEnabled(0, 0); } @Override public boolean isEnabled(long groupId) { return isEnabled(0, groupId); } @Override public boolean isEnabled(long companyId, long groupId) { if (!PropsValues.PERMISSIONS_INLINE_SQL_CHECK_ENABLED) { return false; } PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); if (permissionChecker == null) { throw new IllegalStateException("Permission checker is null"); } if (groupId > 0) { if (permissionChecker.isGroupAdmin(groupId) || permissionChecker.isGroupOwner(groupId)) { return false; } } else if (companyId > 0) { if (permissionChecker.isCompanyAdmin(companyId)) { return false; } } else { if (permissionChecker.isOmniadmin()) { return false; } } return true; } @Override public boolean isEnabled(long[] groupIds) { if (!PropsValues.PERMISSIONS_INLINE_SQL_CHECK_ENABLED) { return false; } for (long groupId : groupIds) { if (isEnabled(groupId)) { return true; } } return false; } @Override public String replacePermissionCheck( String sql, String className, String classPKField) { return replacePermissionCheck( sql, className, classPKField, null, new long[] {0}, null); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, long groupId) { return replacePermissionCheck( sql, className, classPKField, null, new long[] {groupId}, null); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, long groupId, String bridgeJoin) { return replacePermissionCheck( sql, className, classPKField, null, new long[] {groupId}, bridgeJoin); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, long[] groupIds) { return replacePermissionCheck( sql, className, classPKField, null, groupIds, null); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, long[] groupIds, String bridgeJoin) { return replacePermissionCheck( sql, className, classPKField, null, groupIds, bridgeJoin); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField) { return replacePermissionCheck( sql, className, classPKField, userIdField, new long[] {0}, null); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField, long groupId) { return replacePermissionCheck( sql, className, classPKField, userIdField, new long[] {groupId}, null); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField, long groupId, String bridgeJoin) { return replacePermissionCheck( sql, className, classPKField, userIdField, new long[] {groupId}, bridgeJoin); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField, long[] groupIds) { return replacePermissionCheck( sql, className, classPKField, userIdField, groupIds, null); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField, long[] groupIds, String bridgeJoin) { String groupIdField = classPKField.substring( 0, classPKField.lastIndexOf(CharPool.PERIOD)); return replacePermissionCheck( sql, className, classPKField, userIdField, groupIdField.concat(".groupId"), groupIds, bridgeJoin); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField, String bridgeJoin) { return replacePermissionCheck( sql, className, classPKField, userIdField, 0, bridgeJoin); } @Override public String replacePermissionCheck( String sql, String className, String classPKField, String userIdField, String groupIdField, long[] groupIds, String bridgeJoin) { if (!isEnabled(groupIds)) { return sql; } if (Validator.isNull(className)) { throw new IllegalArgumentException("className is null"); } if (Validator.isNull(sql)) { return sql; } if (ResourceBlockLocalServiceUtil.isSupported(className)) { return replacePermissionCheckBlocks( sql, className, classPKField, userIdField, groupIds, bridgeJoin); } else { return replacePermissionCheckJoin( sql, className, classPKField, userIdField, groupIdField, groupIds, bridgeJoin); } } protected Set<Long> getOwnerResourceBlockIds( long companyId, long[] groupIds, String className) { Set<Long> resourceBlockIds = new HashSet<>(); PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); for (long groupId : groupIds) { resourceBlockIds.addAll( permissionChecker.getOwnerResourceBlockIds( companyId, groupId, className, ActionKeys.VIEW)); } return resourceBlockIds; } protected String getOwnerResourceBlockIdsSQL( PermissionChecker permissionChecker, long checkGroupId, String className, Set<Long> ownerResourceBlockIds) { if (ownerResourceBlockIds.size() < PropsValues. PERMISSIONS_INLINE_SQL_RESOURCE_BLOCK_QUERY_THRESHOLD) { return StringUtil.merge(ownerResourceBlockIds); } return StringUtil.replace( CustomSQLUtil.get(FIND_BY_RESOURCE_BLOCK_ID), new String[] { "[$COMPANY_ID$]", "[$GROUP_ID$]", "[$RESOURCE_BLOCK_NAME$]", "[$ROLE_ID$]" }, new String[] { String.valueOf(permissionChecker.getCompanyId()), String.valueOf(checkGroupId), className, StringUtil.valueOf(permissionChecker.getOwnerRoleId()) }); } protected Set<Long> getResourceBlockIds( long companyId, long[] groupIds, String className) { Set<Long> resourceBlockIds = new HashSet<>(); PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); for (long groupId : groupIds) { resourceBlockIds.addAll( permissionChecker.getResourceBlockIds( companyId, groupId, permissionChecker.getUserId(), className, ActionKeys.VIEW)); } return resourceBlockIds; } protected long[] getRoleIds(long groupId) { long[] roleIds = PermissionChecker.DEFAULT_ROLE_IDS; PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); if (permissionChecker != null) { roleIds = permissionChecker.getRoleIds( permissionChecker.getUserId(), groupId); } return roleIds; } protected long[] getRoleIds(long[] groupIds) { if (groupIds.length == 1) { return getRoleIds(groupIds[0]); } Set<Long> roleIds = new HashSet<>(); for (long groupId : groupIds) { for (long roleId : getRoleIds(groupId)) { roleIds.add(roleId); } } return ArrayUtil.toLongArray(roleIds); } protected String getRoleIdsOrOwnerIdSQL( PermissionChecker permissionChecker, long[] groupIds, String userIdField) { StringBundler sb = new StringBundler(9); long[] roleIds = getRoleIds(groupIds); if (roleIds.length > 0) { sb.append(StringPool.OPEN_PARENTHESIS); sb.append("ResourcePermission.roleId IN ("); sb.append(StringUtil.merge(roleIds)); sb.append(StringPool.CLOSE_PARENTHESIS); } if (permissionChecker.isSignedIn()) { if (roleIds.length > 0) { sb.append(" OR "); } else { sb.append(StringPool.OPEN_PARENTHESIS); } long userId = permissionChecker.getUserId(); if (Validator.isNull(userIdField)) { sb.append("ResourcePermission.ownerId = "); sb.append(userId); } else { sb.append(userIdField); sb.append(" = "); sb.append(userId); } sb.append(StringPool.CLOSE_PARENTHESIS); } else if (roleIds.length > 0) { sb.append(StringPool.CLOSE_PARENTHESIS); } return sb.toString(); } protected long getUserId() { long userId = 0; PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); if (permissionChecker != null) { userId = permissionChecker.getUserId(); } return userId; } protected String getUserResourceBlockIdsSQL( PermissionChecker permissionChecker, long checkGroupId, long[] roleIds, String className, Set<Long> userResourceBlockIds) { if (userResourceBlockIds.size() < PropsValues. PERMISSIONS_INLINE_SQL_RESOURCE_BLOCK_QUERY_THRESHOLD) { return StringUtil.merge(userResourceBlockIds); } return StringUtil.replace( CustomSQLUtil.get(FIND_BY_RESOURCE_BLOCK_ID), new String[] { "[$COMPANY_ID$]", "[$GROUP_ID$]", "[$RESOURCE_BLOCK_NAME$]", "[$ROLE_ID$]" }, new String[] { String.valueOf(permissionChecker.getCompanyId()), String.valueOf(checkGroupId), className, StringUtil.merge(roleIds) }); } protected String replacePermissionCheckBlocks( String sql, String className, String classPKField, String userIdField, long[] groupIds, String bridgeJoin) { PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); long checkGroupId = 0; if (groupIds.length == 1) { checkGroupId = groupIds[0]; } long[] roleIds = permissionChecker.getRoleIds( getUserId(), checkGroupId); try { for (long roleId : roleIds) { if (ResourceTypePermissionLocalServiceUtil. hasCompanyScopePermission( permissionChecker.getCompanyId(), className, roleId, ActionKeys.VIEW)) { return sql; } } } catch (Exception e) { } Set<Long> userResourceBlockIds = getResourceBlockIds( permissionChecker.getCompanyId(), groupIds, className); String permissionWhere = StringPool.BLANK; if (Validator.isNotNull(bridgeJoin)) { permissionWhere = bridgeJoin; } Set<Long> ownerResourceBlockIds = getOwnerResourceBlockIds( permissionChecker.getCompanyId(), groupIds, className); // If a user has regular access to a resource block, it isn't necessary // to check owner permissions on it as well. ownerResourceBlockIds.removeAll(userResourceBlockIds); // A SQL syntax error occurs if there is not at least one resource block // ID. if (ownerResourceBlockIds.isEmpty()) { ownerResourceBlockIds.add(_NO_RESOURCE_BLOCKS_ID); } if (userResourceBlockIds.isEmpty()) { userResourceBlockIds.add(_NO_RESOURCE_BLOCKS_ID); } if (Validator.isNotNull(userIdField)) { permissionWhere = permissionWhere.concat( CustomSQLUtil.get(FILTER_BY_RESOURCE_BLOCK_ID_OWNER)); permissionWhere = StringUtil.replace( permissionWhere, new String[] { "[$OWNER_RESOURCE_BLOCK_ID$]", "[$USER_ID$]", "[$USER_ID_FIELD$]", "[$USER_RESOURCE_BLOCK_ID$]" }, new String[] { getOwnerResourceBlockIdsSQL( permissionChecker, checkGroupId, className, ownerResourceBlockIds), String.valueOf(permissionChecker.getUserId()), userIdField, getUserResourceBlockIdsSQL( permissionChecker, checkGroupId, roleIds, className, userResourceBlockIds) }); } else { permissionWhere = permissionWhere.concat( CustomSQLUtil.get(FILTER_BY_RESOURCE_BLOCK_ID)); permissionWhere = StringUtil.replace( permissionWhere, "[$USER_RESOURCE_BLOCK_ID$]", getUserResourceBlockIdsSQL( permissionChecker, checkGroupId, roleIds, className, userResourceBlockIds)); } int pos = sql.indexOf(_WHERE_CLAUSE); if (pos != -1) { StringBundler sb = new StringBundler(4); sb.append(sql.substring(0, pos)); sb.append(permissionWhere); sb.append(" AND "); sb.append(sql.substring(pos + 7)); return sb.toString(); } pos = sql.indexOf(_GROUP_BY_CLAUSE); if (pos != -1) { return sql.substring(0, pos + 1).concat(permissionWhere).concat( sql.substring(pos + 1)); } pos = sql.indexOf(_ORDER_BY_CLAUSE); if (pos != -1) { return sql.substring(0, pos + 1).concat(permissionWhere).concat( sql.substring(pos + 1)); } return sql.concat(StringPool.SPACE).concat(permissionWhere); } protected String replacePermissionCheckJoin( String sql, String className, String classPKField, String userIdField, String groupIdField, long[] groupIds, String bridgeJoin) { if (Validator.isNull(classPKField)) { throw new IllegalArgumentException("classPKField is null"); } PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); long companyId = 0; if (groupIds.length == 1) { long groupId = groupIds[0]; Group group = GroupLocalServiceUtil.fetchGroup(groupId); if (group != null) { companyId = group.getCompanyId(); long[] roleIds = getRoleIds(groupId); try { if (ResourcePermissionLocalServiceUtil. hasResourcePermission( companyId, className, ResourceConstants.SCOPE_GROUP, String.valueOf(groupId), roleIds, ActionKeys.VIEW) || ResourcePermissionLocalServiceUtil. hasResourcePermission( companyId, className, ResourceConstants.SCOPE_GROUP_TEMPLATE, String.valueOf( GroupConstants.DEFAULT_PARENT_GROUP_ID), roleIds, ActionKeys.VIEW)) { return sql; } } catch (PortalException pe) { if (_log.isDebugEnabled()) { _log.debug( "Unable to get resource permissions for " + className + " with group " + groupId, pe); } } } } else { for (long groupId : groupIds) { Group group = GroupLocalServiceUtil.fetchGroup(groupId); if (group == null) { continue; } if (companyId == 0) { companyId = group.getCompanyId(); continue; } if (group.getCompanyId() != companyId) { throw new IllegalArgumentException( "Permission queries across multiple portal instances " + "are not supported"); } } } if (companyId == 0) { companyId = permissionChecker.getCompanyId(); } try { if (ResourcePermissionLocalServiceUtil.hasResourcePermission( companyId, className, ResourceConstants.SCOPE_COMPANY, String.valueOf(companyId), getRoleIds(0), ActionKeys.VIEW)) { return sql; } } catch (PortalException pe) { if (_log.isDebugEnabled()) { _log.debug( "Unable to get resource permissions for " + className + " with company " + companyId, pe); } } String permissionSQL = CustomSQLUtil.get(FIND_BY_RESOURCE_PERMISSION); if (Validator.isNotNull(bridgeJoin)) { permissionSQL = bridgeJoin.concat(permissionSQL); } String roleIdsOrOwnerIdSQL = getRoleIdsOrOwnerIdSQL( permissionChecker, groupIds, userIdField); StringBundler groupAdminResourcePermissionSB = null; for (long groupId : groupIds) { if (!isEnabled(groupId)) { groupAdminResourcePermissionSB = new StringBundler(5); if (!roleIdsOrOwnerIdSQL.isEmpty()) { groupAdminResourcePermissionSB.append(" OR "); } groupAdminResourcePermissionSB.append( "((ResourcePermission.primKeyId = 0) AND "); groupAdminResourcePermissionSB.append( "(ResourcePermission.roleId = "); groupAdminResourcePermissionSB.append( permissionChecker.getOwnerRoleId()); groupAdminResourcePermissionSB.append("))"); break; } } int scope = ResourceConstants.SCOPE_INDIVIDUAL; String groupAdminSQL = StringPool.BLANK; if (groupAdminResourcePermissionSB != null) { groupAdminSQL = groupAdminResourcePermissionSB.toString(); } permissionSQL = StringUtil.replace( permissionSQL, new String[] { "[$CLASS_NAME$]", "[$COMPANY_ID$]", "[$GROUP_ADMIN_RESOURCE_PERMISSION$]", "[$RESOURCE_SCOPE_INDIVIDUAL$]", "[$ROLE_IDS_OR_OWNER_ID$]" }, new String[] { className, String.valueOf(companyId), groupAdminSQL, String.valueOf(scope), roleIdsOrOwnerIdSQL }); StringBundler sb = new StringBundler(8); int pos = sql.indexOf(_WHERE_CLAUSE); if (pos == -1) { pos = sql.indexOf(_GROUP_BY_CLAUSE); if (pos == -1) { pos = sql.indexOf(_ORDER_BY_CLAUSE); } if (pos == -1) { sb.append(sql); } else { sb.append(sql.substring(0, pos)); } sb.append(_WHERE_CLAUSE); _appendPermissionSQL(sb, classPKField, permissionSQL); if (pos != -1) { sb.append(sql.substring(pos)); } } else { pos += _WHERE_CLAUSE.length(); sb.append(sql.substring(0, pos)); _appendPermissionSQL(sb, classPKField, permissionSQL); sb.append("AND "); sb.append(sql.substring(pos)); } return sb.toString(); } private void _appendPermissionSQL( StringBundler sb, String classPKField, String permissionSQL) { sb.append("("); sb.append(classPKField); sb.append(" IN ("); sb.append(permissionSQL); sb.append(")) "); } private static final String _GROUP_BY_CLAUSE = " GROUP BY "; private static final long _NO_RESOURCE_BLOCKS_ID = -1; private static final String _ORDER_BY_CLAUSE = " ORDER BY "; private static final String _WHERE_CLAUSE = " WHERE "; private static final Log _log = LogFactoryUtil.getLog( InlineSQLHelperImpl.class); }