001/* 002 * Copyright 2026 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.forms.rights; 017 018import java.util.HashMap; 019import java.util.List; 020import java.util.Map; 021import java.util.Set; 022import java.util.stream.Collectors; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027 028import org.ametys.core.group.GroupIdentity; 029import org.ametys.core.right.AccessController; 030import org.ametys.core.right.AccessController.Permission.PermissionType; 031import org.ametys.core.right.AccessExplanation; 032import org.ametys.core.right.AllowedUsers; 033import org.ametys.core.right.RightManager; 034import org.ametys.core.right.RightManager.ContextPermissions; 035import org.ametys.core.right.RightManager.RightResult; 036import org.ametys.core.right.RightsException; 037import org.ametys.core.user.UserIdentity; 038import org.ametys.plugins.forms.dao.FormDAO; 039import org.ametys.plugins.forms.dao.FormEntryDAO; 040import org.ametys.plugins.forms.repository.Form; 041import org.ametys.plugins.forms.repository.FormEntry; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.runtime.i18n.I18nizableText; 044import org.ametys.runtime.plugin.component.PluginAware; 045 046/** 047 * {@link AccessController} to allow access to a {@link FormEntry} 048 * For the read permission, it checks the defined read right on the parent form. 049 * For the other permissions, it checks the right on the parent form. 050 */ 051public class FormEntryAccessController implements AccessController, Serviceable, PluginAware 052{ 053 private static final String __READ_RIGHT = FormEntryDAO.HANDLE_FORMS_ENTRIES_RIGHT_ID; 054 055 /** The right manager */ 056 protected RightManager _rightManager; 057 058 /** The form DAO */ 059 protected FormDAO _formDAO; 060 061 /** The ametys object resolver */ 062 protected AmetysObjectResolver _resolver; 063 064 /** The access controller id */ 065 protected String _id; 066 067 public void service(ServiceManager manager) throws ServiceException 068 { 069 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 070 _formDAO = (FormDAO) manager.lookup(FormDAO.ROLE); 071 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 072 } 073 074 public void setPluginInfo(String pluginName, String featureName, String id) 075 { 076 _id = id; 077 } 078 079 public String getId() 080 { 081 return _id; 082 } 083 084 public boolean supports(Object element) 085 { 086 return element instanceof FormEntry; 087 } 088 089 public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object) 090 { 091 Form form = ((FormEntry) object).getForm(); 092 RightResult anonymousRight = _rightManager.hasAnonymousRight(rightId, form); 093 if (anonymousRight == RightResult.RIGHT_ALLOW) 094 { 095 return AccessResult.ANONYMOUS_ALLOWED; 096 } 097 else 098 { 099 RightResult hasRight = _rightManager.hasRight(user, rightId, form); 100 if (hasRight == RightResult.RIGHT_DENY) 101 { 102 return AccessResult.USER_DENIED; 103 } 104 105 RightResult anyConnectedUserRight = _rightManager.hasAnyConnectedUserRight(rightId, form); 106 if (anyConnectedUserRight == RightResult.RIGHT_ALLOW) 107 { 108 return AccessResult.ANY_CONNECTED_ALLOWED; 109 } 110 else if (hasRight == RightResult.RIGHT_ALLOW) 111 { 112 return AccessResult.USER_ALLOWED; 113 } 114 else if (anyConnectedUserRight == RightResult.RIGHT_DENY) 115 { 116 return AccessResult.ANY_CONNECTED_DENIED; 117 } 118 else if (anonymousRight == RightResult.RIGHT_DENY) 119 { 120 return AccessResult.ANONYMOUS_DENIED; 121 } 122 } 123 124 return AccessResult.UNKNOWN; 125 } 126 127 public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 128 { 129 return getPermission(user, userGroups, __READ_RIGHT, object); 130 } 131 132 public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 133 { 134 Form form = ((FormEntry) object).getForm(); 135 136 Map<String, AccessResult> permissionByRight = new HashMap<>(); 137 138 _rightManager.getUserRights(user, form).stream().forEach(rightId -> { 139 permissionByRight.put(rightId, AccessResult.USER_ALLOWED); 140 }); 141 142 return permissionByRight; 143 144 } 145 146 public AccessResult getPermissionForAnonymous(String rightId, Object object) 147 { 148 Form form = ((FormEntry) object).getForm(); 149 RightResult anonymousRight = _rightManager.hasAnonymousRight(rightId, form); 150 switch (anonymousRight) 151 { 152 case RIGHT_ALLOW: 153 return AccessResult.ANONYMOUS_ALLOWED; 154 case RIGHT_DENY: 155 return AccessResult.ANONYMOUS_DENIED; 156 default: 157 return AccessResult.UNKNOWN; 158 } 159 } 160 161 public AccessResult getReadAccessPermissionForAnonymous(Object object) 162 { 163 return getPermissionForAnonymous(__READ_RIGHT, object); 164 } 165 166 public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object) 167 { 168 Form form = ((FormEntry) object).getForm(); 169 RightResult anonymousRight = _rightManager.hasAnonymousRight(rightId, form); 170 if (anonymousRight == RightResult.RIGHT_ALLOW) 171 { 172 return AccessResult.ANONYMOUS_ALLOWED; 173 } 174 else 175 { 176 RightResult anyConnectedUserRight = _rightManager.hasAnyConnectedUserRight(rightId, form); 177 switch (anyConnectedUserRight) 178 { 179 case RIGHT_ALLOW: 180 return AccessResult.ANY_CONNECTED_ALLOWED; 181 case RIGHT_DENY: 182 return AccessResult.ANY_CONNECTED_DENIED; 183 default: 184 return anonymousRight == RightResult.RIGHT_DENY ? AccessResult.ANONYMOUS_DENIED : AccessResult.UNKNOWN; 185 } 186 } 187 } 188 189 public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object) 190 { 191 return getPermissionForAnyConnectedUser(__READ_RIGHT, object); 192 } 193 194 public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object) 195 { 196 Form form = ((FormEntry) object).getForm(); 197 Map<UserIdentity, AccessResult> permissionsByUser = new HashMap<>(); 198 199 AllowedUsers allowedUsers = _rightManager.getAllowedUsers(rightId, form); 200 allowedUsers.getAllowedUsers().forEach(user -> { 201 permissionsByUser.put(user, AccessResult.USER_ALLOWED); 202 }); 203 204 allowedUsers.getDeniedUsers().forEach(user -> { 205 permissionsByUser.put(user, AccessResult.USER_DENIED); 206 }); 207 208 return permissionsByUser; 209 } 210 211 public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object) 212 { 213 return getPermissionByUser(__READ_RIGHT, object); 214 } 215 216 public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object) 217 { 218 Form form = ((FormEntry) object).getForm(); 219 Map<GroupIdentity, AccessResult> permissionsByGroup = new HashMap<>(); 220 221 AllowedUsers allowedUsers = _rightManager.getAllowedUsers(rightId, form); 222 allowedUsers.getAllowedGroups().forEach(group -> { 223 permissionsByGroup.put(group, AccessResult.USER_ALLOWED); 224 }); 225 226 allowedUsers.getAllowedGroups().forEach(group -> { 227 permissionsByGroup.put(group, AccessResult.USER_DENIED); 228 }); 229 230 return permissionsByGroup; 231 } 232 233 public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object) 234 { 235 return getPermissionByGroup(__READ_RIGHT, object); 236 } 237 238 public Map<Permission, AccessExplanation> explainAllPermissionsForAnonymous(Object object) 239 { 240 FormEntry entry = (FormEntry) object; 241 242 ContextPermissions explainAllPermissions = _rightManager.explainAllPermissions(entry.getForm()); 243 Map<Permission, AccessExplanation> permissionsForAnonymous = _transformPermissionsByAccessExplanation(entry, explainAllPermissions.permissionsForAnonymous()); 244 245 AccessResult accessResult = getReadAccessPermissionForAnonymous(object); 246 permissionsForAnonymous.put(new Permission(PermissionType.READ, null), _getReadAccessExplanation(accessResult, object)); 247 248 return permissionsForAnonymous; 249 } 250 251 public Map<Permission, AccessExplanation> explainAllPermissionsForAnyConnected(Object object) 252 { 253 FormEntry entry = (FormEntry) object; 254 255 ContextPermissions explainAllPermissions = _rightManager.explainAllPermissions(entry.getForm()); 256 Map<Permission, AccessExplanation> permissionsForAnyConnected = _transformPermissionsByAccessExplanation(entry, explainAllPermissions.permissionsForAnyConnected()); 257 258 AccessResult accessResult = getReadAccessPermissionForAnonymous(object); 259 permissionsForAnyConnected.put(new Permission(PermissionType.READ, null), _getReadAccessExplanation(accessResult, object)); 260 261 return permissionsForAnyConnected; 262 } 263 264 public Map<UserIdentity, Map<Permission, AccessExplanation>> explainAllPermissionsByUser(Object object) 265 { 266 FormEntry entry = (FormEntry) object; 267 268 ContextPermissions explainAllPermissions = _rightManager.explainAllPermissions(entry.getForm()); 269 Map<UserIdentity, Map<Permission, AccessExplanation>> results = explainAllPermissions.permissionsByUser() 270 .entrySet() 271 .stream() 272 .collect(Collectors.toMap(e -> e.getKey(), e -> _transformPermissionsByAccessExplanation(entry, e.getValue()))); 273 274 Map<UserIdentity, AccessResult> readAccessPermissionByUser = getReadAccessPermissionByUser(object); 275 for (UserIdentity identity: readAccessPermissionByUser.keySet()) 276 { 277 AccessResult accessResult = readAccessPermissionByUser.get(identity); 278 AccessExplanation explanation = _getReadAccessExplanation(accessResult, object); 279 Map<Permission, AccessExplanation> userResults = results.computeIfAbsent(identity, __ -> new HashMap<>()); 280 userResults.put(new Permission(PermissionType.READ, null), explanation); 281 } 282 283 return results; 284 } 285 286 public Map<GroupIdentity, Map<Permission, AccessExplanation>> explainAllPermissionsByGroup(Object object) 287 { 288 FormEntry entry = (FormEntry) object; 289 290 ContextPermissions explainAllPermissions = _rightManager.explainAllPermissions(entry.getForm()); 291 Map<GroupIdentity, Map<Permission, AccessExplanation>> results = explainAllPermissions.permissionsByGroup() 292 .entrySet() 293 .stream() 294 .collect(Collectors.toMap(e -> e.getKey(), e -> _transformPermissionsByAccessExplanation(entry, e.getValue()))); 295 296 Map<GroupIdentity, AccessResult> readAccessPermissionByGroup = getReadAccessPermissionByGroup(object); 297 for (GroupIdentity identity: readAccessPermissionByGroup.keySet()) 298 { 299 AccessResult accessResult = readAccessPermissionByGroup.get(identity); 300 AccessExplanation explanation = _getReadAccessExplanation(accessResult, object); 301 Map<Permission, AccessExplanation> groupResults = results.computeIfAbsent(identity, __ -> new HashMap<>()); 302 groupResults.put(new Permission(PermissionType.READ, null), explanation); 303 } 304 305 return results; 306 } 307 308 private Map<Permission, AccessExplanation> _transformPermissionsByAccessExplanation(FormEntry formEntry, Map<Permission, List<AccessExplanation>> permissionsByRightExplanation) 309 { 310 Map<Permission, AccessExplanation> result = new HashMap<>(); 311 for (Permission permission : permissionsByRightExplanation.keySet()) 312 { 313 // For non read permission, we merge all explanations into one explanation with merged access result 314 if (permission.type() != PermissionType.READ) 315 { 316 List<AccessExplanation> explanations = permissionsByRightExplanation.get(permission); 317 List<AccessResult> accessResults = explanations.stream() 318 .map(AccessExplanation::accessResult) 319 .toList(); 320 321 AccessResult accessResult = AccessResult.merge(accessResults); 322 result.put(permission, _getFormReadAccessExplanation(accessResult, formEntry)); 323 } 324 } 325 return result; 326 } 327 328 private AccessExplanation _getFormReadAccessExplanation(AccessResult result, Object object) 329 { 330 switch (result) 331 { 332 case USER_ALLOWED: 333 case USER_DENIED: 334 case ANONYMOUS_ALLOWED: 335 case ANONYMOUS_DENIED: 336 case ANY_CONNECTED_ALLOWED: 337 case ANY_CONNECTED_DENIED: 338 return new AccessExplanation( 339 getId(), 340 result, 341 new I18nizableText("plugin.forms", "PLUGINS_FORM_ENTRY_FROM_FORM_ACCESS_CONTROLLER_" + result.name() + "_EXPLANATION", Map.of("title", getObjectLabel(object))) 342 ); 343 default: 344 return AccessController.getDefaultAccessExplanation(getId(), result); 345 } 346 347 } 348 349 private AccessExplanation _getReadAccessExplanation(AccessResult result, Object object) 350 { 351 switch (result) 352 { 353 case USER_ALLOWED: 354 case USER_DENIED: 355 case ANONYMOUS_ALLOWED: 356 case ANONYMOUS_DENIED: 357 case ANY_CONNECTED_ALLOWED: 358 case ANY_CONNECTED_DENIED: 359 return new AccessExplanation( 360 getId(), 361 result, 362 new I18nizableText("plugin.forms", "PLUGINS_FORM_ENTRY_ACCESS_CONTROLLER_" + result.name() + "_EXPLANATION", Map.of("title", getObjectLabel(object))) 363 ); 364 default: 365 return AccessController.getDefaultAccessExplanation(getId(), result); 366 } 367 } 368 369 public I18nizableText getObjectLabel(Object object) 370 { 371 if (object instanceof FormEntry formEntry) 372 { 373 Form form = formEntry.getForm(); 374 return new I18nizableText(FormAccessController.getFormDirectoryPathLabel(form.getParent()) + form.getTitle()); 375 } 376 throw new RightsException("Unsupported context: " + object.toString()); 377 } 378 379 public I18nizableText getObjectCategory(Object object) 380 { 381 return FormAccessController.FORM_CONTEXT_CATEGORY; 382 } 383 384 // ************************************************************************************************************************************************************ 385 // *** No workspace permission 386 // ************************************************************************************************************************************************************ 387 388 public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups) 389 { 390 return false; 391 } 392 393 public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts) 394 { 395 return false; 396 } 397 398 public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts) 399 { 400 return false; 401 } 402 403 public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId) 404 { 405 return false; 406 } 407 408 public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId) 409 { 410 return false; 411 } 412 413 public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId) 414 { 415 return false; 416 } 417 418 public Map<ExplanationObject, Map<Permission, AccessExplanation>> explainAllPermissions(UserIdentity identity, Set<GroupIdentity> groups, Set<Object> workspacesContexts) 419 { 420 return Map.of(); 421 } 422}