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 @Override 040 public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 041 { 042 Map<String, AccessResult> permissions = super.getPermissionByRight(user, userGroups, object); 043 044 @SuppressWarnings("unchecked") 045 Set<T> parents = _getParents((T) object); 046 if (parents != null) 047 { 048 // Determine parents permissions 049 Map<String, AccessResult> parentsPermissions = new HashMap<>(); 050 for (T parent : parents) 051 { 052 Map<String, AccessResult> parentPermissions = getPermissionByRight(user, userGroups, parent); 053 for (String rightId : parentPermissions.keySet()) 054 { 055 parentsPermissions.put(rightId, AccessResult.merge(parentPermissions.get(rightId), parentsPermissions.get(rightId))); 056 } 057 } 058 059 // Apply it to local permission 060 for (String rightId : parentsPermissions.keySet()) 061 { 062 if (!permissions.containsKey(rightId) || permissions.get(rightId) == AccessResult.UNKNOWN) 063 { 064 permissions.put(rightId, parentsPermissions.get(rightId)); 065 } 066 } 067 } 068 069 return permissions; 070 } 071 072 @SuppressWarnings("unchecked") 073 @Override 074 protected AccessResult _getPermission(UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profilesIds, Object object, Object convertedObject) 075 { 076 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 077 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 078 Map<UserIdentity, AccessResult> cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USER); 079 if (cacheResult != null && cacheResult.containsKey(user)) 080 { 081 return cacheResult.get(user); 082 } 083 084 AccessResult permission = super._getPermission(user, userGroups, profilesIds, object, convertedObject); 085 086 if (permission == AccessResult.UNKNOWN) 087 { 088 Set<T> parents = _getParents((T) object); 089 if (parents != null) 090 { 091 for (T parent : parents) 092 { 093 Object convertedParentObject = _convertContext(parent); 094 AccessResult parentResult = _getPermission(user, userGroups, profilesIds, parent, convertedParentObject); 095 permission = AccessResult.merge(permission, parentResult); 096 } 097 098 if (permission != AccessResult.UNKNOWN) 099 { 100 if (cacheResult == null) 101 { 102 cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USER); 103 cacheResult = cacheResult == null ? new HashMap<>() : cacheResult; 104 } 105 cacheResult.put(user, permission); 106 _putInSecondCache(profilesIds, convertedObject, cacheResult, CacheKind.USER); 107 } 108 } 109 } 110 111 return permission; 112 } 113 114 @Override 115 protected AccessResult _getPermissionForAnonymous(Set<String> profilesIds, Object object, Object convertedObject) 116 { 117 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 118 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 119 AccessResult cacheResult = (AccessResult) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.ANONYMOUS); 120 if (cacheResult != null) 121 { 122 return cacheResult; 123 } 124 125 AccessResult permission = super._getPermissionForAnonymous(profilesIds, object, convertedObject); 126 127 if (permission == AccessResult.UNKNOWN) 128 { 129 @SuppressWarnings("unchecked") 130 Set<T> parents = _getParents((T) object); 131 if (parents != null) 132 { 133 for (T parent : parents) 134 { 135 Object convertedParentObject = _convertContext(parent); 136 AccessResult parentResult = _getPermissionForAnonymous(profilesIds, parent, convertedParentObject); 137 permission = AccessResult.merge(permission, parentResult); 138 } 139 140 if (permission != AccessResult.UNKNOWN) 141 { 142 // LET'S ADD THE ANSWER TO THE CACHE... because #_getParents can be long to execute 143 _putInSecondCache(profilesIds, convertedObject, permission, CacheKind.ANONYMOUS); 144 } 145 } 146 } 147 148 return permission; 149 } 150 151 @Override 152 protected AccessResult _getPermissionForAnyConnectedUser(Set<String> profilesIds, Object object, Object convertedObject) 153 { 154 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 155 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 156 AccessResult cacheResult = (AccessResult) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.ANY_CONNECTED_USER); 157 if (cacheResult != null) 158 { 159 return cacheResult; 160 } 161 162 AccessResult permission = super._getPermissionForAnyConnectedUser(profilesIds, object, convertedObject); 163 164 if (permission == AccessResult.UNKNOWN) 165 { 166 @SuppressWarnings("unchecked") 167 Set<T> parents = _getParents((T) object); 168 if (parents != null) 169 { 170 for (T parent : parents) 171 { 172 Object convertedParentObject = _convertContext(parent); 173 AccessResult parentResult = _getPermissionForAnyConnectedUser(profilesIds, parent, convertedParentObject); 174 permission = AccessResult.merge(permission, parentResult); 175 } 176 177 if (permission != AccessResult.UNKNOWN) 178 { 179 // LET'S ADD THE ANSWER TO THE CACHE... because #_getParents can be long to execute 180 _putInSecondCache(profilesIds, convertedObject, permission, CacheKind.ANY_CONNECTED_USER); 181 } 182 } 183 } 184 185 return permission; 186 } 187 188 @Override 189 protected Map<UserIdentity, AccessResult> _getPermissionByUser(Set<String> profilesIds, Object object, Object convertedObject) 190 { 191 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 192 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 193 @SuppressWarnings("unchecked") 194 Map<UserIdentity, AccessResult> cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USERS); 195 if (cacheResult != null) 196 { 197 return cacheResult; 198 } 199 200 Map<UserIdentity, AccessResult> permissions = super._getPermissionByUser(profilesIds, object, convertedObject); 201 202 @SuppressWarnings("unchecked") 203 Set<T> parents = _getParents((T) object); 204 if (parents != null) 205 { 206 // Determine parents permissions 207 Map<UserIdentity, AccessResult> parentsPermissions = new HashMap<>(); 208 for (T parent : parents) 209 { 210 Object convertedParentObject = _convertContext(parent); 211 Map<UserIdentity, AccessResult> parentPermissions = _getPermissionByUser(profilesIds, parent, convertedParentObject); 212 for (UserIdentity user : parentPermissions.keySet()) 213 { 214 parentsPermissions.put(user, AccessResult.merge(parentPermissions.get(user), parentsPermissions.get(user))); 215 } 216 } 217 218 // Apply it to local permission 219 for (UserIdentity user : parentsPermissions.keySet()) 220 { 221 if (!permissions.containsKey(user) || permissions.get(user) == AccessResult.UNKNOWN) 222 { 223 permissions.put(user, parentsPermissions.get(user)); 224 } 225 } 226 } 227 228 _putInSecondCache(profilesIds, convertedObject, permissions, CacheKind.USERS); 229 230 return permissions; 231 } 232 233 @Override 234 protected Map<GroupIdentity, AccessResult> _getPermissionByGroup(Set<String> profilesIds, Object object, Object convertedObject) 235 { 236 // Let's manage the cache by ourselves, to avoid useless call to #_getParents 237 // Try to retrieve in second cache (do not delegate the cache reading to the superclass because we known the unknown result is final 238 @SuppressWarnings("unchecked") 239 Map<GroupIdentity, AccessResult> cacheResult = (Map<GroupIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.GROUPS); 240 if (cacheResult != null) 241 { 242 return cacheResult; 243 } 244 245 Map<GroupIdentity, AccessResult> permissions = super._getPermissionByGroup(profilesIds, object, convertedObject); 246 247 @SuppressWarnings("unchecked") 248 Set<T> parents = _getParents((T) object); 249 if (parents != null) 250 { 251 // Determine parents permissions 252 Map<GroupIdentity, AccessResult> parentsPermissions = new HashMap<>(); 253 for (T parent : parents) 254 { 255 Object convertedParentObject = _convertContext(parent); 256 Map<GroupIdentity, AccessResult> parentPermissions = _getPermissionByGroup(profilesIds, parent, convertedParentObject); 257 for (GroupIdentity group : parentPermissions.keySet()) 258 { 259 parentsPermissions.put(group, AccessResult.merge(parentPermissions.get(group), parentsPermissions.get(group))); 260 } 261 } 262 263 // Apply it to local permission 264 for (GroupIdentity group : parentsPermissions.keySet()) 265 { 266 if (!permissions.containsKey(group) || permissions.get(group) == AccessResult.UNKNOWN) 267 { 268 permissions.put(group, parentsPermissions.get(group)); 269 } 270 } 271 } 272 273 _putInSecondCache(profilesIds, convertedObject, permissions, CacheKind.GROUPS); 274 275 return permissions; 276 } 277}