001/* 002 * Copyright 2024 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.extraction.rights; 017 018import java.io.IOException; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023import java.util.stream.Stream; 024 025import org.apache.avalon.framework.activity.Initializable; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029import org.apache.commons.collections.MapUtils; 030import org.apache.excalibur.source.SourceException; 031import org.apache.excalibur.source.SourceResolver; 032import org.apache.excalibur.source.impl.FileSource; 033 034import org.ametys.cms.contenttype.ContentTypesHelper; 035import org.ametys.cms.repository.Content; 036import org.ametys.core.group.GroupIdentity; 037import org.ametys.core.right.AccessController; 038import org.ametys.core.right.AccessExplanation; 039import org.ametys.core.right.RightsException; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.plugins.core.impl.right.AbstractRightBasedAccessController; 042import org.ametys.plugins.extraction.ExtractionConstants; 043import org.ametys.plugins.extraction.execution.Extraction; 044import org.ametys.plugins.extraction.execution.ExtractionDAO; 045import org.ametys.runtime.i18n.I18nizableText; 046 047 048/** 049 * {@link AccessController} to allow read access and handle for author of a extraction file 050 * 051 */ 052public class ExtractionAuthorAccessController extends AbstractRightBasedAccessController implements Serviceable, Initializable 053{ 054 private static final List<String> __AUTHOR_RIGHTS = List.of(ExtractionConstants.MODIFY_EXTRACTION_RIGHT_ID, "Workflow_Rights_Edition_Online"); 055 056 private SourceResolver _srcResolver; 057 private String _rootPath; 058 private ExtractionDAO _extractionDAO; 059 private ContentTypesHelper _contentTypesHelper; 060 061 public void service(ServiceManager manager) throws ServiceException 062 { 063 _srcResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 064 _extractionDAO = (ExtractionDAO) manager.lookup(ExtractionDAO.ROLE); 065 _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 066 } 067 068 public void initialize() throws Exception 069 { 070 FileSource rootDir = (FileSource) _srcResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR); 071 if (rootDir.getFile().exists()) 072 { 073 _rootPath = rootDir.getFile().getPath(); 074 } 075 } 076 077 public boolean isSupported(Object object) 078 { 079 return object instanceof Extraction 080 || object instanceof FileSource fileSource && !fileSource.isCollection() && fileSource.getFile().getPath().startsWith(_rootPath) 081 || object instanceof Content content && _contentTypesHelper.isInstanceOf(content, ExtractionConstants.DESCRIPTION_CONTENT_TYPE_ID); 082 } 083 084 public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object) 085 { 086 if (user.equals(_getAuthor(object))) 087 { 088 return __AUTHOR_RIGHTS.contains(rightId) ? AccessResult.USER_ALLOWED : AccessResult.UNKNOWN; 089 } 090 091 return AccessResult.UNKNOWN; 092 } 093 094 public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 095 { 096 return user.equals(_getAuthor(object)) ? AccessResult.USER_ALLOWED : AccessResult.UNKNOWN; 097 } 098 099 /** 100 * If creator, access to a list of rights 101 */ 102 public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object) 103 { 104 Map<String, AccessResult> permissionByRight = new HashMap<>(); 105 106 if (user.equals(_getAuthor(object))) 107 { 108 for (String rightId : __AUTHOR_RIGHTS) 109 { 110 permissionByRight.put(rightId, AccessResult.USER_ALLOWED); 111 } 112 } 113 114 return permissionByRight; 115 } 116 117 public AccessResult getPermissionForAnonymous(String rightId, Object object) 118 { 119 return AccessResult.UNKNOWN; 120 } 121 122 public AccessResult getReadAccessPermissionForAnonymous(Object object) 123 { 124 return AccessResult.UNKNOWN; 125 } 126 127 public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object) 128 { 129 return AccessResult.UNKNOWN; 130 } 131 132 public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object) 133 { 134 return AccessResult.UNKNOWN; 135 } 136 137 /** 138 * If right requested is in the list, the creator is added the list of USER_ALLOWED 139 */ 140 public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object) 141 { 142 Map<UserIdentity, AccessResult> permissionByUser = new HashMap<>(); 143 144 if (__AUTHOR_RIGHTS.contains(rightId)) 145 { 146 UserIdentity extractionAuthor = _getAuthor(object); 147 permissionByUser.put(extractionAuthor, AccessResult.USER_ALLOWED); 148 } 149 return permissionByUser; 150 } 151 152 public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object) 153 { 154 return MapUtils.EMPTY_MAP; 155 } 156 157 public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object) 158 { 159 return MapUtils.EMPTY_MAP; 160 } 161 162 public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object) 163 { 164 return MapUtils.EMPTY_MAP; 165 } 166 167 public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId) 168 { 169 return false; 170 } 171 172 public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups) 173 { 174 return false; 175 } 176 177 public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId) 178 { 179 return false; 180 } 181 182 public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts) 183 { 184 return false; 185 } 186 187 public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId) 188 { 189 return false; 190 } 191 192 public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts) 193 { 194 return false; 195 } 196 197 @Override 198 public AccessExplanation getStandardAccessExplanation(AccessResult permission, Object object) 199 { 200 switch (permission) 201 { 202 case USER_ALLOWED: 203 case UNKNOWN: 204 if (object instanceof Content) 205 { 206 return new AccessExplanation( 207 getId(), 208 permission, 209 new I18nizableText("plugin.extraction", "PLUGINS_EXTRACTION_CONTENT_AUTHOR_ACCESS_CONTROLLER_" + permission.name() + "_EXPLANATION", 210 Map.of("title", getObjectLabel(object))) 211 ); 212 } 213 else 214 { 215 return new AccessExplanation( 216 getId(), 217 permission, 218 new I18nizableText("plugin.extraction", "PLUGINS_EXTRACTION_AUTHOR_ACCESS_CONTROLLER_" + permission.name() + "_EXPLANATION", 219 Map.of("title", getObjectLabel(object))) 220 ); 221 } 222 default: 223 return super.getStandardAccessExplanation(permission, object); 224 } 225 } 226 227 private UserIdentity _getAuthor(Object object) 228 { 229 if (object instanceof Extraction extraction) 230 { 231 return extraction.getAuthor(); 232 } 233 else if (object instanceof FileSource fileSource) 234 { 235 return _extractionDAO.getAuthor(fileSource); 236 } 237 else if (object instanceof Content content) 238 { 239 return content.getCreator(); 240 } 241 return null; 242 } 243 244 private String _getExtractionName(Object object) 245 { 246 if (object instanceof Extraction extraction) 247 { 248 return extraction.getFileName(); 249 } 250 else if (object instanceof FileSource fileSource) 251 { 252 return fileSource.getFile().getName(); 253 } 254 else if (object instanceof Content content) 255 { 256 return content.getTitle(); 257 } 258 return null; 259 } 260 261 public I18nizableText getObjectLabel(Object object) 262 { 263 String extractionName = _getExtractionName(object); 264 if (extractionName != null) 265 { 266 return new I18nizableText(extractionName); 267 } 268 throw new RightsException("Unsupported context: " + object.toString()); 269 } 270 271 public I18nizableText getObjectCategory(Object object) 272 { 273 return ExtractionAccessController.EXTRACTION_CONTEXT_CATEGORY; 274 } 275 276 @Override 277 protected Iterable< ? extends Object> getHandledObjects(UserIdentity identity, Set<GroupIdentity> groups) 278 { 279 // FIXME context are not merged with the ExtractionAccessController 280 // because one handles FileSource and the other handles String 281 try 282 { 283 FileSource rootDir = (FileSource) _srcResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR); 284 if (rootDir.getFile().exists()) 285 { 286 Stream<FileSource> definitions = _getDefinitions(rootDir); 287 return definitions.toList(); 288 } 289 } 290 catch (IOException e) 291 { 292 getLogger().warn("Failed to compute the list of extractions"); 293 } 294 295 return List.of(); 296 } 297 298 private Stream<FileSource> _getDefinitions(FileSource source) 299 { 300 // TODO filter author 301 if (source.isCollection()) 302 { 303 try 304 { 305 return source.getChildren().stream() 306 .filter(FileSource.class::isInstance) 307 .flatMap(src -> _getDefinitions((FileSource) src)); 308 } 309 catch (SourceException e) 310 { 311 getLogger().warn("Failed to compute the list of extractions"); 312 return Stream.of(); 313 } 314 } 315 else 316 { 317 return Stream.of(source); 318 } 319 } 320 321 public ExplanationObject getExplanationObject(Object object) 322 { 323 if (object instanceof FileSource source) 324 { 325 return new ExplanationObject( 326 // we convert the source to the right path to be able to merge with ExtractionAccessController 327 _extractionDAO.getExtractionRightPath(source), 328 getObjectLabel(object), 329 getObjectCategory(object) 330 ); 331 } 332 return super.getExplanationObject(object); 333 } 334}