/** * 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.search.internal; import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; import com.liferay.portal.kernel.exception.NoSuchResourceException; 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.model.Role; import com.liferay.portal.kernel.model.RoleConstants; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.search.BooleanClauseOccur; import com.liferay.portal.kernel.search.Document; import com.liferay.portal.kernel.search.Field; import com.liferay.portal.kernel.search.Indexer; import com.liferay.portal.kernel.search.IndexerRegistry; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.SearchPermissionChecker; import com.liferay.portal.kernel.search.filter.BooleanFilter; import com.liferay.portal.kernel.search.filter.TermsFilter; import com.liferay.portal.kernel.security.permission.ActionKeys; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionCheckerFactoryUtil; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.security.permission.UserBag; import com.liferay.portal.kernel.service.GroupLocalService; import com.liferay.portal.kernel.service.ResourceBlockLocalService; import com.liferay.portal.kernel.service.ResourcePermissionLocalService; import com.liferay.portal.kernel.service.RoleLocalService; import com.liferay.portal.kernel.service.UserLocalService; 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.Portal; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.search.configuration.SearchPermissionCheckerConfiguration; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Reference; /** * @author Allen Chiang * @author Bruno Farache * @author Raymond Augé * @author Amos Fong * @author Preston Crary */ @Component( configurationPid = "com.liferay.portal.search.configuration.SearchPermissionCheckerConfiguration", immediate = true, service = SearchPermissionChecker.class ) public class SearchPermissionCheckerImpl implements SearchPermissionChecker { @Override public void addPermissionFields(long companyId, Document document) { try { long groupId = GetterUtil.getLong(document.get(Field.GROUP_ID)); String className = document.get(Field.ENTRY_CLASS_NAME); String classPK = document.get(Field.ENTRY_CLASS_PK); if (Validator.isNull(className) && Validator.isNull(classPK)) { className = document.get(Field.ROOT_ENTRY_CLASS_NAME); classPK = document.get(Field.ROOT_ENTRY_CLASS_PK); } boolean relatedEntry = GetterUtil.getBoolean( document.get(Field.RELATED_ENTRY)); if (relatedEntry) { long classNameId = GetterUtil.getLong( document.get(Field.CLASS_NAME_ID)); className = _portal.getClassName(classNameId); classPK = document.get(Field.CLASS_PK); } if (Validator.isNull(className) || Validator.isNull(classPK)) { return; } Indexer<?> indexer = _indexerRegistry.nullSafeGetIndexer(className); if (!indexer.isPermissionAware()) { return; } String viewActionId = document.get(Field.VIEW_ACTION_ID); if (Validator.isNull(viewActionId)) { viewActionId = ActionKeys.VIEW; } _addPermissionFields( companyId, groupId, className, classPK, viewActionId, document); } catch (NoSuchResourceException nsre) { if (_log.isDebugEnabled()) { _log.debug(nsre, nsre); } } catch (Exception e) { _log.error(e, e); } } @Override public BooleanFilter getPermissionBooleanFilter( long companyId, long[] groupIds, long userId, String className, BooleanFilter booleanFilter, SearchContext searchContext) { try { booleanFilter = _getPermissionBooleanFilter( companyId, groupIds, userId, className, booleanFilter, searchContext); } catch (Exception e) { _log.error(e, e); } return booleanFilter; } @Override public void updatePermissionFields( String resourceName, String resourceClassPK) { try { Indexer<?> indexer = _indexerRegistry.nullSafeGetIndexer( resourceName); indexer.reindex(resourceName, GetterUtil.getLong(resourceClassPK)); } catch (Exception e) { _log.error(e, e); } } @Activate @Modified protected void activate(Map<String, Object> properties) { _searchPermissionCheckerConfiguration = ConfigurableUtil.createConfigurable( SearchPermissionCheckerConfiguration.class, properties); } private void _addPermissionFields( long companyId, long groupId, String className, String classPK, String viewActionId, Document doc) throws Exception { List<Role> roles = null; if (_resourceBlockLocalService.isSupported(className)) { roles = _resourceBlockLocalService.getRoles( className, Long.valueOf(classPK), viewActionId); } else { roles = _resourcePermissionLocalService.getRoles( companyId, className, ResourceConstants.SCOPE_INDIVIDUAL, classPK, viewActionId); } if (roles.isEmpty()) { return; } List<Long> roleIds = new ArrayList<>(); List<String> groupRoleIds = new ArrayList<>(); for (Role role : roles) { if ((role.getType() == RoleConstants.TYPE_ORGANIZATION) || (role.getType() == RoleConstants.TYPE_SITE)) { groupRoleIds.add(groupId + StringPool.DASH + role.getRoleId()); } else { roleIds.add(role.getRoleId()); } } doc.addKeyword( Field.ROLE_ID, roleIds.toArray(new Long[roleIds.size()])); doc.addKeyword( Field.GROUP_ROLE_ID, groupRoleIds.toArray(new String[groupRoleIds.size()])); } private SearchPermissionContext _createSearchPermissionContext( long companyId, long userId, PermissionChecker permissionChecker) throws Exception { UserBag userBag = permissionChecker.getUserBag(); if (userBag == null) { return null; } Set<Role> roles = new HashSet<>(); if (permissionChecker.isSignedIn()) { roles.addAll(userBag.getRoles()); roles.add( _roleLocalService.getRole(companyId, RoleConstants.GUEST)); } else { roles.addAll( _roleLocalService.getRoles( permissionChecker.getGuestUserRoleIds())); } int termsCount = roles.size(); int permissionTermsLimit = _searchPermissionCheckerConfiguration.permissionTermsLimit(); if (termsCount > permissionTermsLimit) { if (_log.isDebugEnabled()) { _log.debug( "Skipping presearch permission checking due to too many " + "roles: " + termsCount + " > " + permissionTermsLimit); } return null; } Role organizationUserRole = _roleLocalService.getRole( companyId, RoleConstants.ORGANIZATION_USER); Role siteMemberRole = _roleLocalService.getRole( companyId, RoleConstants.SITE_MEMBER); Collection<Group> groups = userBag.getGroups(); List<UsersGroupIdRoles> usersGroupIdsRoles = new ArrayList<>( groups.size()); termsCount += groups.size(); if (termsCount > permissionTermsLimit) { if (_log.isDebugEnabled()) { _log.debug( "Skipping presearch permission checking due to too many " + "roles and groups: " + termsCount + " > " + permissionTermsLimit); } return null; } for (Group group : groups) { long[] roleIds = permissionChecker.getRoleIds( userId, group.getGroupId()); List<Role> groupRoles = _roleLocalService.getRoles(roleIds); roles.addAll(groupRoles); Iterator<Role> iterator = groupRoles.iterator(); while (iterator.hasNext()) { Role groupRole = iterator.next(); if ((groupRole.getType() != RoleConstants.TYPE_ORGANIZATION) && (groupRole.getType() != RoleConstants.TYPE_SITE)) { iterator.remove(); } } if (group.isOrganization() && !groupRoles.contains(organizationUserRole)) { groupRoles.add(organizationUserRole); } if (group.isSite() && !groupRoles.contains(siteMemberRole)) { groupRoles.add(siteMemberRole); } usersGroupIdsRoles.add( new UsersGroupIdRoles(group.getGroupId(), groupRoles)); termsCount += groupRoles.size(); if (termsCount > permissionTermsLimit) { if (_log.isDebugEnabled()) { _log.debug( "Skipping presearch permission checking due to too " + "many roles, groups, and groupRoles: " + termsCount + " > " + permissionTermsLimit); } return null; } } return new SearchPermissionContext(roles, usersGroupIdsRoles); } private BooleanFilter _getPermissionBooleanFilter( long companyId, long[] searchGroupIds, long userId, String className, BooleanFilter booleanFilter, SearchContext searchContext) throws Exception { Indexer<?> indexer = _indexerRegistry.getIndexer(className); if (!indexer.isPermissionAware()) { return booleanFilter; } PermissionChecker permissionChecker = PermissionThreadLocal.getPermissionChecker(); User user = permissionChecker.getUser(); if ((user == null) || (user.getUserId() != userId)) { user = _userLocalService.fetchUser(userId); if (user == null) { return booleanFilter; } permissionChecker = PermissionCheckerFactoryUtil.create(user); } Object searchPermissionContextObject = searchContext.getAttribute( "searchPermissionContext"); SearchPermissionContext searchPermissionContext = null; if (searchPermissionContextObject != null) { if (searchPermissionContextObject == _nullSearchPermissionContext) { return booleanFilter; } } else if (!permissionChecker.isCompanyAdmin(companyId)) { searchPermissionContext = _createSearchPermissionContext( companyId, userId, permissionChecker); } if (searchPermissionContext == null) { searchContext.setAttribute( "searchPermissionContext", _nullSearchPermissionContext); return booleanFilter; } searchContext.setAttribute( "searchPermissionContext", searchPermissionContext); return _getPermissionFilter( companyId, searchGroupIds, userId, permissionChecker, className, booleanFilter, searchPermissionContext); } private BooleanFilter _getPermissionFilter( long companyId, long[] searchGroupIds, long userId, PermissionChecker permissionChecker, String className, BooleanFilter booleanFilter, SearchPermissionContext searchPermissionContext) throws Exception { List<UsersGroupIdRoles> usersGroupIdsRoles = searchPermissionContext._usersGroupIdsRoles; BooleanFilter permissionBooleanFilter = new BooleanFilter(); if (userId > 0) { permissionBooleanFilter.addTerm(Field.USER_ID, userId); } TermsFilter groupsTermsFilter = new TermsFilter(Field.GROUP_ID); TermsFilter groupRolesTermsFilter = searchPermissionContext._groupRolesTermsFilter; TermsFilter rolesTermsFilter = searchPermissionContext._rolesTermsFilter; long[] roleIds = searchPermissionContext._roleIds; long[] regularRoleIds = searchPermissionContext._regularRoleIds; if (_resourcePermissionLocalService.hasResourcePermission( companyId, className, ResourceConstants.SCOPE_COMPANY, String.valueOf(companyId), roleIds, ActionKeys.VIEW)) { return booleanFilter; } if (_resourcePermissionLocalService.hasResourcePermission( companyId, className, ResourceConstants.SCOPE_GROUP_TEMPLATE, String.valueOf(GroupConstants.DEFAULT_PARENT_GROUP_ID), regularRoleIds, ActionKeys.VIEW)) { return booleanFilter; } for (UsersGroupIdRoles usersGroupIdRoles : usersGroupIdsRoles) { long groupId = usersGroupIdRoles._groupId; List<Role> groupRoles = usersGroupIdRoles._groupRoles; if (permissionChecker.isGroupAdmin(groupId) || _resourcePermissionLocalService.hasResourcePermission( companyId, className, ResourceConstants.SCOPE_GROUP, String.valueOf(groupId), roleIds, ActionKeys.VIEW) || _resourcePermissionLocalService.hasResourcePermission( companyId, className, ResourceConstants.SCOPE_GROUP_TEMPLATE, String.valueOf(GroupConstants.DEFAULT_PARENT_GROUP_ID), ListUtil.toLongArray(groupRoles, Role.ROLE_ID_ACCESSOR), ActionKeys.VIEW)) { groupsTermsFilter.addValue(String.valueOf(groupId)); } } if (ArrayUtil.isNotEmpty(searchGroupIds)) { for (long searchGroupId : searchGroupIds) { if (!searchPermissionContext.containsGroupId(searchGroupId) && _resourcePermissionLocalService.hasResourcePermission( companyId, className, ResourceConstants.SCOPE_GROUP, String.valueOf(searchGroupId), roleIds, ActionKeys.VIEW)) { groupsTermsFilter.addValue(String.valueOf(searchGroupId)); } } } if (!groupsTermsFilter.isEmpty()) { permissionBooleanFilter.add(groupsTermsFilter); } if (!groupRolesTermsFilter.isEmpty()) { permissionBooleanFilter.add(groupRolesTermsFilter); } if (!rolesTermsFilter.isEmpty()) { permissionBooleanFilter.add(rolesTermsFilter); } if (!permissionBooleanFilter.hasClauses()) { return booleanFilter; } BooleanFilter fullBooleanFilter = new BooleanFilter(); if ((booleanFilter != null) && booleanFilter.hasClauses()) { fullBooleanFilter.add(booleanFilter, BooleanClauseOccur.MUST); } fullBooleanFilter.add(permissionBooleanFilter, BooleanClauseOccur.MUST); return fullBooleanFilter; } private static final Log _log = LogFactoryUtil.getLog( SearchPermissionCheckerImpl.class); private static final String _nullSearchPermissionContext = StringPool.BLANK; @Reference private GroupLocalService _groupLocalService; @Reference private IndexerRegistry _indexerRegistry; @Reference private Portal _portal; @Reference private ResourceBlockLocalService _resourceBlockLocalService; @Reference private ResourcePermissionLocalService _resourcePermissionLocalService; @Reference private RoleLocalService _roleLocalService; private volatile SearchPermissionCheckerConfiguration _searchPermissionCheckerConfiguration; @Reference private UserLocalService _userLocalService; private static class SearchPermissionContext implements Serializable { public boolean containsGroupId(long groupId) { for (UsersGroupIdRoles usersGroupIdRoles : _usersGroupIdsRoles) { if (groupId == usersGroupIdRoles._groupId) { return true; } } return false; } private SearchPermissionContext( Set<Role> roles, List<UsersGroupIdRoles> usersGroupIdsRoles) { _usersGroupIdsRoles = usersGroupIdsRoles; List<Long> roleIds = new ArrayList<>(roles.size()); List<Long> regularRoleIds = new ArrayList<>(); for (Role role : roles) { roleIds.add(role.getRoleId()); if (role.getType() == RoleConstants.TYPE_REGULAR) { regularRoleIds.add(role.getRoleId()); } _rolesTermsFilter.addValue(String.valueOf(role.getRoleId())); } _roleIds = ArrayUtil.toLongArray(roleIds); _regularRoleIds = ArrayUtil.toLongArray(regularRoleIds); for (UsersGroupIdRoles usersGroupIdRoles : _usersGroupIdsRoles) { long groupId = usersGroupIdRoles._groupId; List<Role> groupRoles = usersGroupIdRoles._groupRoles; for (Role groupRole : groupRoles) { _groupRolesTermsFilter.addValue( groupId + StringPool.DASH + groupRole.getRoleId()); } } } private static final long serialVersionUID = 1L; private final TermsFilter _groupRolesTermsFilter = new TermsFilter( Field.GROUP_ROLE_ID); private final long[] _regularRoleIds; private final long[] _roleIds; private final TermsFilter _rolesTermsFilter = new TermsFilter( Field.ROLE_ID); private final List<UsersGroupIdRoles> _usersGroupIdsRoles; } private static class UsersGroupIdRoles implements Serializable { private UsersGroupIdRoles(long groupId, List<Role> groupRoles) { _groupId = groupId; _groupRoles = groupRoles; } private static final long serialVersionUID = 1L; private final long _groupId; private final List<Role> _groupRoles; } }