001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.indexing.solr; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Map.Entry; 021import java.util.Set; 022import java.util.stream.Collectors; 023 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028import org.apache.solr.common.SolrInputDocument; 029 030import org.ametys.cms.content.indexing.solr.SolrFieldNames; 031import org.ametys.core.group.GroupIdentity; 032import org.ametys.core.right.AccessController.AccessResult; 033import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint; 034import org.ametys.core.user.UserIdentity; 035 036/** 037 * Component indexing rights (hierarchical computed) for an object. 038 * @param <T> The type of object 039 */ 040public abstract class AbstractSolrHierarchicalRightIndexer<T> implements Component, Serviceable, SolrFieldNames 041{ 042 /** The extension point for the profile assignement storages */ 043 protected ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP; 044 045 @Override 046 public void service(ServiceManager serviceManager) throws ServiceException 047 { 048 _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) serviceManager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE); 049 } 050 051 /** 052 * Indexes the ACLs of the object (resolving the hierarchy) 053 * @param object The object to index 054 * @param document The document to index 055 * @param profiles The profiles to check 056 */ 057 protected void indexAcls(T object, SolrInputDocument document, Set<String> profiles) 058 { 059 AccessResult permissionForAnonymous = _getPermissionForAnonymous(profiles, object); 060 switch (permissionForAnonymous) 061 { 062 case ANONYMOUS_ALLOWED: 063 // Any anonymous user is allowed 064 document.addField(ACL_READ_ALLOW_ANONYMOUS, "true"); 065 return; 066 case ANONYMOUS_DENIED: 067 // Any anonymous user is denied 068 document.addField(ACL_READ_ALLOW_ANONYMOUS, "false"); 069 break; 070 case UNKNOWN: 071 default: 072 // Unknown 073 document.addField(ACL_READ_ALLOW_ANONYMOUS, "unknown"); 074 break; 075 } 076 077 AccessResult permissionForAnyConnectedUser = _getPermissionForAnyConnectedUser(profiles, object); 078 switch (permissionForAnyConnectedUser) 079 { 080 case ANY_CONNECTED_ALLOWED: 081 // Any connected user is allowed 082 document.addField(ACL_READ_ALLOW_ANY_CONNECTED_USER, "true"); 083 break; 084 case ANY_CONNECTED_DENIED: 085 // Any connected user is denied 086 document.addField(ACL_READ_ALLOW_ANY_CONNECTED_USER, "false"); 087 break; 088 case UNKNOWN: 089 default: 090 // Unknown 091 document.addField(ACL_READ_ALLOW_ANY_CONNECTED_USER, "unknown"); 092 break; 093 } 094 095 Map<UserIdentity, AccessResult> permissionsByUser = _getPermissionsByUser(profiles, object); 096 097 Set<UserIdentity> allowedUsersOnObj = permissionsByUser.entrySet().stream() 098 .filter(entry -> AccessResult.USER_ALLOWED.equals(entry.getValue())) 099 .map(Entry::getKey) 100 .collect(Collectors.toSet()); 101 102 Set<UserIdentity> deniedUsersOnObj = permissionsByUser.entrySet().stream() 103 .filter(entry -> AccessResult.USER_DENIED.equals(entry.getValue())) 104 .map(Entry::getKey) 105 .collect(Collectors.toSet()); 106 107 108 Map<GroupIdentity, AccessResult> permissionsByGroup = _getPermissionsByGroup(profiles, object); 109 110 Set<GroupIdentity> allowedGroupsOnObj = permissionsByGroup.entrySet().stream() 111 .filter(entry -> AccessResult.GROUP_ALLOWED.equals(entry.getValue())) 112 .map(Entry::getKey) 113 .collect(Collectors.toSet()); 114 115 Set<GroupIdentity> deniedGroupsOnObj = permissionsByGroup.entrySet().stream() 116 .filter(entry -> AccessResult.GROUP_DENIED.equals(entry.getValue())) 117 .map(Entry::getKey) 118 .collect(Collectors.toSet()); 119 120 document.addField(ACL_READ_ALLOWED_USERS, _userIdentitiesToStrings(allowedUsersOnObj)); 121 document.addField(ACL_READ_DENIED_USERS, _userIdentitiesToStrings(deniedUsersOnObj)); 122 document.addField(ACL_READ_ALLOWED_GROUPS, _groupIdentitiesToStrings(allowedGroupsOnObj)); 123 document.addField(ACL_READ_DENIED_GROUPS, _groupIdentitiesToStrings(deniedGroupsOnObj)); 124 } 125 126 /** 127 * Gets the parent of the object 128 * @param object The object 129 * @return the parent of the object 130 */ 131 protected abstract Set<T> _getParents(T object); 132 133 /** 134 * Gets the permissions for anonymous of the given object, resolving the hierarchy 135 * @param profileIds The profiles to check 136 * @param object The object 137 * @return the permissions for anonymous 138 */ 139 protected AccessResult _getPermissionForAnonymous(Set<String> profileIds, T object) 140 { 141 // Get permission on object itself 142 AccessResult permission = _profileAssignmentStorageEP.getPermissionForAnonymous(profileIds, object); 143 144 if (AccessResult.UNKNOWN.equals(permission)) 145 { 146 // Add recursively the permission given by parent 147 Set<T> parents = _getParents(object); 148 if (parents != null) 149 { 150 for (T parent : parents) 151 { 152 AccessResult parentResult = _getPermissionForAnonymous(profileIds, parent); 153 permission = AccessResult.merge(permission, parentResult); 154 } 155 } 156 } 157 158 return permission; 159 } 160 161 /** 162 * Gets the permissions for any connected user of the given object, resolving the hierarchy 163 * @param profileIds The profiles to check 164 * @param object The object 165 * @return the permissions for any connected user 166 */ 167 protected AccessResult _getPermissionForAnyConnectedUser(Set<String> profileIds, T object) 168 { 169 // Get permission on object itself 170 AccessResult permission = _profileAssignmentStorageEP.getPermissionForAnyConnectedUser(profileIds, object); 171 172 if (AccessResult.UNKNOWN.equals(permission)) 173 { 174 // Add recursively the permission given by parent 175 Set<T> parents = _getParents(object); 176 if (parents != null) 177 { 178 for (T parent : parents) 179 { 180 AccessResult parentResult = _getPermissionForAnyConnectedUser(profileIds, parent); 181 permission = AccessResult.merge(permission, parentResult); 182 } 183 } 184 } 185 186 return permission; 187 } 188 189 /** 190 * Gets the permissions by user of the given object, resolving the hierarchy 191 * @param profileIds The profiles to check 192 * @param object The object 193 * @return the permissions by user 194 */ 195 protected Map<UserIdentity, AccessResult> _getPermissionsByUser(Set<String> profileIds, T object) 196 { 197 // Get permissions on object itself 198 Map<UserIdentity, AccessResult> permissionsByUser = _profileAssignmentStorageEP.getPermissionsByUser(profileIds, object); 199 200 // Add recursively the permission given by parent 201 Set<T> parents = _getParents(object); 202 if (parents != null) 203 { 204 Map<UserIdentity, AccessResult> parentsResult = new HashMap<>(); 205 for (T parent : parents) 206 { 207 Map<UserIdentity, AccessResult> parentResult = _getPermissionsByUser(profileIds, parent); 208 for (UserIdentity user : parentResult.keySet()) 209 { 210 parentsResult.put(user, AccessResult.merge(parentsResult.get(user), parentResult.get(user))); 211 } 212 } 213 214 for (UserIdentity user : parentsResult.keySet()) 215 { 216 if (!permissionsByUser.containsKey(user) || AccessResult.UNKNOWN.equals(permissionsByUser.get(user))) 217 { 218 permissionsByUser.put(user, AccessResult.merge(parentsResult.get(user))); 219 } 220 } 221 } 222 223 return permissionsByUser; 224 } 225 226 /** 227 * Gets the permissions by group of the given object, resolving the hierarchy 228 * @param profileIds The profiles to check 229 * @param object The object 230 * @return the permissions by group 231 */ 232 protected Map<GroupIdentity, AccessResult> _getPermissionsByGroup(Set<String> profileIds, T object) 233 { 234 // Get permissions on object itself 235 Map<GroupIdentity, AccessResult> permissionsByGroup = _profileAssignmentStorageEP.getPermissionsByGroup(profileIds, object); 236 237 // Add recursively the permission given by parent 238 Set<T> parents = _getParents(object); 239 if (parents != null) 240 { 241 Map<GroupIdentity, AccessResult> parentsResult = new HashMap<>(); 242 for (T parent : parents) 243 { 244 Map<GroupIdentity, AccessResult> parentResult = _getPermissionsByGroup(profileIds, parent); 245 for (GroupIdentity group : parentResult.keySet()) 246 { 247 parentsResult.put(group, AccessResult.merge(parentsResult.get(group), parentResult.get(group))); 248 } 249 } 250 251 for (GroupIdentity group : parentsResult.keySet()) 252 { 253 if (!permissionsByGroup.containsKey(group) || AccessResult.UNKNOWN.equals(permissionsByGroup.get(group))) 254 { 255 permissionsByGroup.put(group, AccessResult.merge(parentsResult.get(group))); 256 } 257 } 258 } 259 260 return permissionsByGroup; 261 } 262 263 /** 264 * Converts a set of identities to a set of their string representations 265 * @param userIdentities The identities 266 * @return a set of string representing the identities 267 */ 268 protected Set<String> _userIdentitiesToStrings(Set<UserIdentity> userIdentities) 269 { 270 return userIdentities.stream().map(UserIdentity::userIdentityToString).collect(Collectors.toSet()); 271 } 272 273 /** 274 * Converts a set of identities to a set of their string representations 275 * @param groupIdentities The identities 276 * @return a set of string representing the identities 277 */ 278 protected Set<String> _groupIdentitiesToStrings(Set<GroupIdentity> groupIdentities) 279 { 280 return groupIdentities.stream().map(GroupIdentity::groupIdentityToString).collect(Collectors.toSet()); 281 } 282}