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.plugins.core.impl.right; 017 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Set; 021 022import org.ametys.core.group.GroupIdentity; 023import org.ametys.core.right.AccessController; 024import org.ametys.core.user.UserIdentity; 025 026/** 027 * Abstract {@link AccessController} for a hierarchical type of object. 028 * @param <T> The class of a supported object, from which you can retrieve its parent with {@link #_getParents(Object)} 029 */ 030public abstract class AbstractHierarchicalAccessController<T> extends AbstractProfileStorageBasedAccessController 031{ 032 /** 033 * Gets the parents of the object. Must return null when the object is the "root" (where "root" means here the root of the hierarchy of the access controller) 034 * @param object The object 035 * @return the parents of the object, or null if the object is the "root" of the hierarchy 036 */ 037 protected abstract Set<T> _getParents(T object); 038 039 /** 040 * Determines if the inheritance of permissions is disallowed on the given object 041 * @param object The object 042 * @return true if the inheritance of permissions is disallowed on the given object 043 */ 044 protected boolean isInheritanceDisallowed(T object) 045 { 046 return _profileAssignmentStorageEP.isInheritanceDisallowed(object); 047 } 048 049 @Override 050 public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 051 { 052 Map<String, AccessResult> permissions = super.getPermissionByRight(user, userGroups, object); 053 054 @SuppressWarnings("unchecked") 055 boolean inheritanceDisallowed = isInheritanceDisallowed((T) object); 056 if (!inheritanceDisallowed) 057 { 058 @SuppressWarnings("unchecked") 059 Set<T> parents = _getParents((T) object); 060 if (parents != null && !_profileAssignmentStorageEP.isInheritanceDisallowed(object)) 061 { 062 // Determine parents permissions 063 Map<String, AccessResult> parentsPermissions = new HashMap<>(); 064 for (T parent : parents) 065 { 066 Map<String, AccessResult> parentPermissions = getPermissionByRight(user, userGroups, parent); 067 for (String rightId : parentPermissions.keySet()) 068 { 069 parentsPermissions.put(rightId, AccessResult.merge(parentPermissions.get(rightId), parentsPermissions.get(rightId))); 070 } 071 } 072 073 // Apply it to local permission 074 for (String rightId : parentsPermissions.keySet()) 075 { 076 if (!permissions.containsKey(rightId) || permissions.get(rightId) == AccessResult.UNKNOWN) 077 { 078 permissions.put(rightId, parentsPermissions.get(rightId)); 079 } 080 } 081 } 082 } 083 084 return permissions; 085 } 086 087 @SuppressWarnings("unchecked") 088 @Override 089 protected AccessResult _getPermission(UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profilesIds, Object object, Object convertedObject) 090 { 091 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 092 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 093 Map<UserIdentity, AccessResult> cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USER); 094 if (cacheResult != null && cacheResult.containsKey(user)) 095 { 096 return cacheResult.get(user); 097 } 098 099 AccessResult permission = super._getPermission(user, userGroups, profilesIds, object, convertedObject); 100 101 boolean inheritanceDisallowed = isInheritanceDisallowed((T) object); 102 103 if (permission == AccessResult.UNKNOWN && !inheritanceDisallowed) 104 { 105 Set<T> parents = _getParents((T) object); 106 if (parents != null) 107 { 108 for (T parent : parents) 109 { 110 Object convertedParentObject = _convertContext(parent); 111 AccessResult parentResult = _getPermission(user, userGroups, profilesIds, parent, convertedParentObject); 112 permission = AccessResult.merge(permission, parentResult); 113 } 114 115 if (permission != AccessResult.UNKNOWN) 116 { 117 if (cacheResult == null) 118 { 119 cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USER); 120 cacheResult = cacheResult == null ? new HashMap<>() : cacheResult; 121 } 122 cacheResult.put(user, permission); 123 _putInSecondCache(profilesIds, convertedObject, cacheResult, CacheKind.USER); 124 } 125 } 126 } 127 128 return permission; 129 } 130 131 @Override 132 protected AccessResult _getPermissionForAnonymous(Set<String> profilesIds, Object object, Object convertedObject) 133 { 134 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 135 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 136 AccessResult cacheResult = (AccessResult) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.ANONYMOUS); 137 if (cacheResult != null) 138 { 139 return cacheResult; 140 } 141 142 AccessResult permission = super._getPermissionForAnonymous(profilesIds, object, convertedObject); 143 144 @SuppressWarnings("unchecked") 145 boolean inheritanceDisallowed = isInheritanceDisallowed((T) object); 146 147 if (permission == AccessResult.UNKNOWN && !inheritanceDisallowed) 148 { 149 @SuppressWarnings("unchecked") 150 Set<T> parents = _getParents((T) object); 151 if (parents != null) 152 { 153 for (T parent : parents) 154 { 155 Object convertedParentObject = _convertContext(parent); 156 AccessResult parentResult = _getPermissionForAnonymous(profilesIds, parent, convertedParentObject); 157 permission = AccessResult.merge(permission, parentResult); 158 } 159 160 if (permission != AccessResult.UNKNOWN) 161 { 162 // LET'S ADD THE ANSWER TO THE CACHE... because #_getParents can be long to execute 163 _putInSecondCache(profilesIds, convertedObject, permission, CacheKind.ANONYMOUS); 164 } 165 } 166 } 167 168 return permission; 169 } 170 171 @Override 172 protected AccessResult _getPermissionForAnyConnectedUser(Set<String> profilesIds, Object object, Object convertedObject) 173 { 174 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 175 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 176 AccessResult cacheResult = (AccessResult) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.ANY_CONNECTED_USER); 177 if (cacheResult != null) 178 { 179 return cacheResult; 180 } 181 182 AccessResult permission = super._getPermissionForAnyConnectedUser(profilesIds, object, convertedObject); 183 184 @SuppressWarnings("unchecked") 185 boolean inheritanceDisallowed = isInheritanceDisallowed((T) object); 186 187 if (permission == AccessResult.UNKNOWN && !inheritanceDisallowed) 188 { 189 @SuppressWarnings("unchecked") 190 Set<T> parents = _getParents((T) object); 191 if (parents != null) 192 { 193 for (T parent : parents) 194 { 195 Object convertedParentObject = _convertContext(parent); 196 AccessResult parentResult = _getPermissionForAnyConnectedUser(profilesIds, parent, convertedParentObject); 197 permission = AccessResult.merge(permission, parentResult); 198 } 199 200 if (permission != AccessResult.UNKNOWN) 201 { 202 // LET'S ADD THE ANSWER TO THE CACHE... because #_getParents can be long to execute 203 _putInSecondCache(profilesIds, convertedObject, permission, CacheKind.ANY_CONNECTED_USER); 204 } 205 } 206 } 207 208 return permission; 209 } 210 211 @Override 212 protected Map<UserIdentity, AccessResult> _getPermissionByUser(Set<String> profilesIds, Object object, Object convertedObject) 213 { 214 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 215 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 216 @SuppressWarnings("unchecked") 217 Map<UserIdentity, AccessResult> cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USERS); 218 if (cacheResult != null) 219 { 220 return cacheResult; 221 } 222 223 Map<UserIdentity, AccessResult> permissions = super._getPermissionByUser(profilesIds, object, convertedObject); 224 225 @SuppressWarnings("unchecked") 226 boolean inheritanceDisallowed = isInheritanceDisallowed((T) object); 227 if (!inheritanceDisallowed) 228 { 229 @SuppressWarnings("unchecked") 230 Set<T> parents = _getParents((T) object); 231 if (parents != null) 232 { 233 // Determine parents permissions 234 Map<UserIdentity, AccessResult> parentsPermissions = new HashMap<>(); 235 for (T parent : parents) 236 { 237 Object convertedParentObject = _convertContext(parent); 238 Map<UserIdentity, AccessResult> parentPermissions = _getPermissionByUser(profilesIds, parent, convertedParentObject); 239 for (UserIdentity user : parentPermissions.keySet()) 240 { 241 parentsPermissions.put(user, AccessResult.merge(parentPermissions.get(user), parentsPermissions.get(user))); 242 } 243 } 244 245 // Apply it to local permission 246 for (UserIdentity user : parentsPermissions.keySet()) 247 { 248 if (!permissions.containsKey(user) || permissions.get(user) == AccessResult.UNKNOWN) 249 { 250 permissions.put(user, parentsPermissions.get(user)); 251 } 252 } 253 } 254 } 255 256 _putInSecondCache(profilesIds, convertedObject, permissions, CacheKind.USERS); 257 258 return permissions; 259 } 260 261 @Override 262 protected Map<GroupIdentity, AccessResult> _getPermissionByGroup(Set<String> profilesIds, Object object, Object convertedObject) 263 { 264 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 265 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 266 @SuppressWarnings("unchecked") 267 Map<GroupIdentity, AccessResult> cacheResult = (Map<GroupIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.GROUPS); 268 if (cacheResult != null) 269 { 270 return cacheResult; 271 } 272 273 Map<GroupIdentity, AccessResult> permissions = super._getPermissionByGroup(profilesIds, object, convertedObject); 274 275 @SuppressWarnings("unchecked") 276 boolean inheritanceDisallowed = isInheritanceDisallowed((T) object); 277 if (!inheritanceDisallowed) 278 { 279 @SuppressWarnings("unchecked") 280 Set<T> parents = _getParents((T) object); 281 if (parents != null) 282 { 283 // Determine parents permissions 284 Map<GroupIdentity, AccessResult> parentsPermissions = new HashMap<>(); 285 for (T parent : parents) 286 { 287 Object convertedParentObject = _convertContext(parent); 288 Map<GroupIdentity, AccessResult> parentPermissions = _getPermissionByGroup(profilesIds, parent, convertedParentObject); 289 for (GroupIdentity group : parentPermissions.keySet()) 290 { 291 parentsPermissions.put(group, AccessResult.merge(parentPermissions.get(group), parentsPermissions.get(group))); 292 } 293 } 294 295 // Apply it to local permission 296 for (GroupIdentity group : parentsPermissions.keySet()) 297 { 298 if (!permissions.containsKey(group) || permissions.get(group) == AccessResult.UNKNOWN) 299 { 300 permissions.put(group, parentsPermissions.get(group)); 301 } 302 } 303 } 304 } 305 306 _putInSecondCache(profilesIds, convertedObject, permissions, CacheKind.GROUPS); 307 308 return permissions; 309 } 310}