001/* 002 * Copyright 2021 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.execution; 017 018import java.io.IOException; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.activity.Initializable; 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.commons.io.FileUtils; 031import org.apache.commons.lang3.StringUtils; 032import org.apache.excalibur.source.SourceException; 033import org.apache.excalibur.source.SourceResolver; 034import org.apache.excalibur.source.TraversableSource; 035import org.apache.excalibur.source.impl.FileSource; 036 037import org.ametys.core.cache.AbstractCacheManager; 038import org.ametys.core.cache.Cache; 039import org.ametys.core.file.FileHelper; 040import org.ametys.core.group.GroupIdentity; 041import org.ametys.core.right.ProfileAssignmentStorage.AnonymousOrAnyConnectedKeys; 042import org.ametys.core.right.ProfileAssignmentStorage.UserOrGroup; 043import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint; 044import org.ametys.core.right.RightManager; 045import org.ametys.core.right.RightManager.RightResult; 046import org.ametys.core.ui.Callable; 047import org.ametys.core.user.CurrentUserProvider; 048import org.ametys.core.user.UserIdentity; 049import org.ametys.plugins.core.user.UserHelper; 050import org.ametys.plugins.extraction.ExtractionConstants; 051import org.ametys.plugins.extraction.rights.ExtractionAccessController; 052import org.ametys.runtime.i18n.I18nizableText; 053import org.ametys.runtime.plugin.component.AbstractLogEnabled; 054 055/** 056 * Object representing the extraction definition file content 057 */ 058public class ExtractionDAO extends AbstractLogEnabled implements Serviceable, Component, Initializable 059{ 060 /** The Avalon role */ 061 public static final String ROLE = ExtractionDAO.class.getName(); 062 063 /** Extraction author cache id */ 064 private static final String EXTRACTION_AUTHOR_CACHE = ExtractionDAO.class.getName() + "$extractionAuthor"; 065 066 private CurrentUserProvider _userProvider; 067 private RightManager _rightManager; 068 private SourceResolver _sourceResolver; 069 private ExtractionDefinitionReader _definitionReader; 070 private ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP; 071 private CurrentUserProvider _currentUserProvider; 072 private AbstractCacheManager _cacheManager; 073 private UserHelper _userHelper; 074 private TraversableSource _root; 075 private FileHelper _fileHelper; 076 077 public void service(ServiceManager manager) throws ServiceException 078 { 079 _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 080 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 081 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 082 _definitionReader = (ExtractionDefinitionReader) manager.lookup(ExtractionDefinitionReader.ROLE); 083 _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) manager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE); 084 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 085 _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE); 086 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 087 _fileHelper = (FileHelper) manager.lookup(FileHelper.ROLE); 088 } 089 090 public void initialize() throws Exception 091 { 092 _root = (TraversableSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR); 093 094 _cacheManager.createRequestCache(EXTRACTION_AUTHOR_CACHE, 095 new I18nizableText("plugin.extraction", "PLUGINS_EXTRACTION_CACHE_DEFINITION_AUTHOR_LABEL"), 096 new I18nizableText("plugin.extraction", "PLUGINS_EXTRACTION_CACHE_DEFINITION_AUTHOR_DESCRIPTION"), 097 true); 098 } 099 100 /** 101 * Get the root container properties 102 * @return The root container properties 103 * @throws IOException If an error occurred while reading folder 104 */ 105 @Callable(rights = Callable.NO_CHECK_REQUIRED) // required for bus message 106 public Map<String, Object> getRootProperties() throws IOException 107 { 108 TraversableSource rootDir = (TraversableSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR); 109 return getExtractionContainerProperties(rootDir, rootDir, true); 110 } 111 112 /** 113 * Get the extraction folder properties 114 * @param relPath the relative folder path 115 * @return the extraction container properties 116 * @throws IOException if an error occured 117 */ 118 @Callable (rights = Callable.NO_CHECK_REQUIRED) // required for bus message 119 public Map<String, Object> getExtractionContainerProperties(String relPath) throws IOException 120 { 121 TraversableSource root = (TraversableSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR); 122 TraversableSource folderSrc = (FileSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR + relPath); 123 return getExtractionContainerProperties(root, folderSrc, true); 124 } 125 126 /** 127 * Get extraction container properties 128 * @param root the root of extraction definitions 129 * @param folder the source of the extraction container 130 * @param withRights true to include rights information 131 * @return The extraction container properties 132 */ 133 public Map<String, Object> getExtractionContainerProperties(TraversableSource root, TraversableSource folder, boolean withRights) 134 { 135 Map<String, Object> infos = new HashMap<>(); 136 137 infos.put("type", "collection"); 138 infos.put("isRoot", folder.getURI().equals(root.getURI())); 139 infos.put("name", folder.getName()); 140 infos.put("path", _getRelativePath(root, folder)); 141 142 if (withRights) 143 { 144 UserIdentity currentUser = _userProvider.getUser(); 145 infos.put("canRead", canRead(currentUser, folder)); 146 infos.put("canRename", canRename(currentUser, folder)); 147 infos.put("canWrite", canWrite(currentUser, folder)); 148 infos.put("canDelete", canDelete(currentUser, folder)); 149 infos.put("canAssignRights", canAssignRights(currentUser, folder)); 150 } 151 152 return infos; 153 } 154 155 private String _getRelativePath (TraversableSource root, TraversableSource file) 156 { 157 String relPath = StringUtils.substringAfter(file.getURI(), root.getURI()); 158 return StringUtils.removeEnd(relPath, "/"); 159 } 160 161 /** 162 * Get the extraction properties 163 * @param relDefinitionFilePath the relative fiel path 164 * @return the extraction properties 165 * @throws Exception if an error occured 166 */ 167 @Callable (rights = Callable.NO_CHECK_REQUIRED) // required for bus message 168 public Map<String, Object> getExtractionProperties(String relDefinitionFilePath) throws Exception 169 { 170 FileSource root = (FileSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR); 171 FileSource fileSource = (FileSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR + relDefinitionFilePath); 172 Extraction extraction = _definitionReader.readExtractionDefinitionFile(fileSource.getFile()); 173 return getExtractionProperties(extraction, root, fileSource, true); 174 } 175 176 /** 177 * Get extraction properties 178 * @param extraction the extraction 179 * @param root the root of extraction definitions 180 * @param file the source of the extraction 181 * @param withRights true to include rights information 182 * @return The extraction properties 183 */ 184 public Map<String, Object> getExtractionProperties(Extraction extraction, TraversableSource root, TraversableSource file, boolean withRights) 185 { 186 Map<String, Object> infos = new HashMap<>(); 187 188 infos.put("name", file.getName()); 189 infos.put("path", _getRelativePath(root, file)); 190 191 infos.put("descriptionId", extraction.getDescriptionId()); 192 193 UserIdentity author = extraction.getAuthor(); 194 infos.put("author", _userHelper.user2json(author)); 195 196 if (withRights) 197 { 198 UserIdentity currentUser = _userProvider.getUser(); 199 infos.put("canRead", canRead(currentUser, file)); 200 infos.put("canWrite", canWrite(currentUser, file)); 201 infos.put("canDelete", canDelete(currentUser, file)); 202 infos.put("canAssignRights", canAssignRights(currentUser, file)); 203 } 204 205 return infos; 206 } 207 208 /** 209 * Check if a folder has a descendant in read access for a given user 210 * @param userIdentity the user 211 * @param folder the source of the extraction container 212 * @return <code>true</code> if the folder has a descendant in read access, <code>false</code> otherwise 213 */ 214 public Boolean hasAnyReadableDescendant(UserIdentity userIdentity, TraversableSource folder) 215 { 216 try 217 { 218 if (folder.exists()) 219 { 220 for (TraversableSource child : (Collection<TraversableSource>) folder.getChildren()) 221 { 222 if (child.isCollection()) 223 { 224 if (canRead(userIdentity, child) || hasAnyReadableDescendant(userIdentity, child)) 225 { 226 return true; 227 } 228 } 229 else if (child.getName().endsWith(".xml") && canRead(userIdentity, child)) 230 { 231 return true; 232 } 233 } 234 } 235 236 return false; 237 } 238 catch (SourceException e) 239 { 240 throw new RuntimeException("Cannot list child elements of " + folder.getURI(), e); 241 } 242 } 243 244 /** 245 * Check if a folder have descendant in write access for a given user 246 * @param userIdentity the user identity 247 * @param folder the source of the extraction container 248 * @return true if the user have write right for at least one child of this container 249 */ 250 public Boolean hasAnyWritableDescendant(UserIdentity userIdentity, TraversableSource folder) 251 { 252 return hasAnyWritableDescendant(userIdentity, folder, false); 253 } 254 255 /** 256 * Check if a folder have descendant in write access for a given user 257 * @param userIdentity the user identity 258 * @param folder the source of the extraction container 259 * @param ignoreExtraction true to ignore extraction file from search (rights will check only on containers) 260 * @return true if the user have write right for at least one child of this container 261 */ 262 public Boolean hasAnyWritableDescendant(UserIdentity userIdentity, TraversableSource folder, boolean ignoreExtraction) 263 { 264 try 265 { 266 if (folder.exists()) 267 { 268 for (TraversableSource child : (Collection<TraversableSource>) folder.getChildren()) 269 { 270 if (child.isCollection()) 271 { 272 if (canWrite(userIdentity, child) || hasAnyWritableDescendant(userIdentity, child)) 273 { 274 return true; 275 } 276 } 277 else if (!ignoreExtraction && child.getName().endsWith(".xml") && canWrite(userIdentity, child)) 278 { 279 return true; 280 } 281 } 282 } 283 284 return false; 285 } 286 catch (SourceException e) 287 { 288 throw new RuntimeException("Cannot list child elements of " + folder.getURI(), e); 289 } 290 } 291 292 /** 293 * Checks if a folder has descendants in the right assignment access for a given user 294 * @param userIdentity the user 295 * @param folder the source of the extraction container 296 * @return <code>true</code> if the folder has descendant, <code>false</code> otherwise 297 */ 298 public boolean hasAnyAssignableDescendant(UserIdentity userIdentity, TraversableSource folder) 299 { 300 try 301 { 302 if (folder.exists()) 303 { 304 for (TraversableSource child : (Collection<TraversableSource>) folder.getChildren()) 305 { 306 if (child.isCollection()) 307 { 308 if (canAssignRights(userIdentity, child) || hasAnyAssignableDescendant(userIdentity, child)) 309 { 310 return true; 311 } 312 } 313 else if (child.getName().endsWith(".xml")) 314 { 315 if (canAssignRights(userIdentity, child)) 316 { 317 return true; 318 } 319 } 320 } 321 } 322 323 return false; 324 } 325 catch (SourceException e) 326 { 327 throw new RuntimeException("Cannot list child elements of " + folder.getURI(), e); 328 } 329 } 330 331 /** 332 * Check if a user has read rights on an extraction container or file 333 * @param userIdentity the user 334 * @param source the source of the extraction container or file 335 * @return <code>true</code> if the user has read rights on an extraction container, <code>false</code> otherwise 336 */ 337 public boolean canRead(UserIdentity userIdentity, TraversableSource source) 338 { 339 return _rightManager.hasReadAccess(userIdentity, source) || canWrite(userIdentity, source); 340 } 341 342 /** 343 * Check if a user has write rights on an extraction container or an extraction 344 * @param userIdentity the user 345 * @param source the source of the extraction file or extration container 346 * @return <code>true</code> if the user has write rights on an extraction container, <code>false</code> otherwise 347 */ 348 public boolean canWrite(UserIdentity userIdentity, TraversableSource source) 349 { 350 return canWrite(userIdentity, source, false); 351 } 352 353 /** 354 * Determines if the user can rename an extraction container 355 * @param userIdentity the user 356 * @param folder the extraction container 357 * @return true if the user can delete the extraction container 358 */ 359 public boolean canRename(UserIdentity userIdentity, TraversableSource folder) 360 { 361 try 362 { 363 return !_isRoot(folder) // is not root 364 && canWrite(userIdentity, folder) // has write access 365 && canWrite(userIdentity, (TraversableSource) folder.getParent()); // has write access on parent 366 } 367 catch (SourceException e) 368 { 369 throw new RuntimeException("Unable to determine user rights on the extraction container " + folder.getURI(), e); 370 } 371 } 372 373 /** 374 * Determines if the user can delete an extraction container or the extraction file 375 * @param userIdentity the user 376 * @param source the extraction container or the extraction file 377 * @return true if the user can delete the extraction container 378 */ 379 public boolean canDelete(UserIdentity userIdentity, TraversableSource source) 380 { 381 try 382 { 383 return !_isRoot(source) // is not root 384 && canWrite(userIdentity, (TraversableSource) source.getParent()) // has write access on parent 385 && canWrite(userIdentity, source, true); // has write access on itselft and each descendant 386 } 387 catch (SourceException e) 388 { 389 throw new RuntimeException("Unable to determine user rights on extraction container or file at uri " + source.getURI(), e); 390 } 391 } 392 393 /** 394 * Check if a user has write access on an extraction container 395 * @param userIdentity the user user identity 396 * @param source the extraction container or the extraction file 397 * @param recursively true to check write access on all descendants recursively 398 * @return true if the user has write access on the extraction container 399 */ 400 public boolean canWrite(UserIdentity userIdentity, TraversableSource source, boolean recursively) 401 { 402 boolean hasRight = _rightManager.hasRight(userIdentity, ExtractionConstants.MODIFY_EXTRACTION_RIGHT_ID, source) == RightResult.RIGHT_ALLOW; 403 if (!hasRight) 404 { 405 return false; 406 } 407 408 try 409 { 410 if (recursively && source.isCollection()) 411 { 412 for (TraversableSource child : (Collection<TraversableSource>) source.getChildren()) 413 { 414 hasRight = hasRight && canWrite(userIdentity, child); 415 416 if (!hasRight) 417 { 418 return false; 419 } 420 } 421 } 422 423 return hasRight; 424 } 425 catch (SourceException e) 426 { 427 throw new RuntimeException("Unable to determine user rights on extraction container " + source.getURI(), e); 428 } 429 } 430 431 /** 432 * Check if a user can edit rights on an extraction container or an extraction file 433 * @param userIdentity the user 434 * @param source the source of the extraction container or file 435 * @return true if the user can edit rights on an extraction container or file 436 */ 437 public boolean canAssignRights(UserIdentity userIdentity, TraversableSource source) 438 { 439 try 440 { 441 return _rightManager.hasRight(userIdentity, "Runtime_Rights_Rights_Handle", "/cms") == RightResult.RIGHT_ALLOW 442 || !_isRoot(source) // is not root 443 && canWrite(userIdentity, (TraversableSource) source.getParent()) // has write access on parent 444 && canWrite(userIdentity, source, true); // has write access on itselft and each descendant 445 } 446 catch (SourceException e) 447 { 448 throw new RuntimeException("Unable to determine the user rights on the extraction container or file at uri " + source.getURI(), e); 449 } 450 } 451 452 /** 453 * Determines if the extraction container is the root node 454 * @param folder the extraction container 455 * @return true if is root 456 */ 457 protected boolean _isRoot(TraversableSource folder) 458 { 459 return trimLastFileSeparator(_root.getURI()).equals(trimLastFileSeparator(folder.getURI())); 460 } 461 462 /** 463 * Get the path for rights of an extraction container or file 464 * @param source the source of extraction container or file 465 * @return the path for rights 466 */ 467 public String getExtractionRightPath(TraversableSource source) 468 { 469 String rootURI = trimLastFileSeparator(_root.getURI()); 470 String sourceURI = source.getURI(); 471 472 if (!sourceURI.startsWith(rootURI)) 473 { 474 // The source is an extraction source 475 return null; 476 } 477 478 // Get only the part after the root folder to get the relative path 479 String relPath = StringUtils.substringAfter(trimLastFileSeparator(sourceURI), rootURI); 480 481 // In some case, relPath can start with a /, we need to trim it to test if it is an empty path corresponding to the root 482 if (relPath.startsWith("/")) 483 { 484 relPath = StringUtils.substringAfter(relPath, "/"); 485 } 486 487 return StringUtils.isEmpty(relPath) ? ExtractionAccessController.ROOT_CONTEXT : ExtractionAccessController.ROOT_CONTEXT + "/" + relPath; 488 } 489 490 /** 491 * Get the source corresponding to the right context of an extraction container or file 492 * @param rightContext The rights context such as '/extraction-dir/path/to/file 493 * @return the resolved source file or null if the given context is not an extraction context 494 * @throws IOException if an error occured 495 */ 496 public TraversableSource getExtractionSource(String rightContext) throws IOException 497 { 498 if (rightContext.startsWith(ExtractionAccessController.ROOT_CONTEXT)) 499 { 500 String relPath = StringUtils.substringAfter(rightContext, ExtractionAccessController.ROOT_CONTEXT); 501 String fileUri = ExtractionConstants.DEFINITIONS_DIR + relPath; 502 return (TraversableSource) _sourceResolver.resolveURI(fileUri); 503 } 504 505 return null; 506 } 507 508 /** 509 * Copy rights from one context to another one 510 * @param sourceContext the source context 511 * @param targetContext the target context 512 */ 513 public void copyRights(String sourceContext, String targetContext) 514 { 515 // Get the mapping between users and profiles 516 Map<UserIdentity, Map<UserOrGroup, Set<String>>> profilesForUsers = _profileAssignmentStorageEP.getProfilesForUsers(sourceContext, null); 517 // Copy allowed user assignment profiles to new context 518 profilesForUsers.entrySet() 519 .forEach(entry -> _copyAllowedUsers(entry.getKey(), entry.getValue().get(UserOrGroup.ALLOWED), targetContext)); 520 // Copy denied user assignment profiles to new context 521 profilesForUsers.entrySet() 522 .forEach(entry -> _copyDeniedUsers(entry.getKey(), entry.getValue().get(UserOrGroup.DENIED), targetContext)); 523 524 // Get the mapping between groups and profiles 525 Map<GroupIdentity, Map<UserOrGroup, Set<String>>> profilesForGroups = _profileAssignmentStorageEP.getProfilesForGroups(sourceContext, null); 526 // Copy allowed group assignment profiles to new context 527 profilesForGroups.entrySet() 528 .forEach(entry -> _copyAllowedGroups(entry.getKey(), entry.getValue().get(UserOrGroup.ALLOWED), targetContext)); 529 // Copy denied group assignment profiles to new context 530 profilesForGroups.entrySet() 531 .forEach(entry -> _copyDeniedGroups(entry.getKey(), entry.getValue().get(UserOrGroup.DENIED), targetContext)); 532 533 // Get the mapping between anonymous or any connected user and profiles 534 Map<AnonymousOrAnyConnectedKeys, Set<String>> profilesForAnonymousOrAnyConnectedUser = _profileAssignmentStorageEP.getProfilesForAnonymousAndAnyConnectedUser(sourceContext); 535 // Copy allowed anonymous user assignment profiles to new context 536 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED) 537 .forEach(profileId -> _profileAssignmentStorageEP.allowProfileToAnonymous(profileId, targetContext)); 538 // Copy denied anonymous user assignment profiles to new context 539 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED) 540 .forEach(profileId -> _profileAssignmentStorageEP.denyProfileToAnonymous(profileId, targetContext)); 541 // Copy allowed any connected user assignment profiles to new context 542 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED) 543 .forEach(profileId -> _profileAssignmentStorageEP.allowProfileToAnyConnectedUser(profileId, targetContext)); 544 // Copy denied any connected user assignment profiles to new context 545 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED) 546 .forEach(profileId -> _profileAssignmentStorageEP.denyProfileToAnyConnectedUser(profileId, targetContext)); 547 } 548 549 private void _copyAllowedUsers(UserIdentity userIdentity, Set<String> profiles, String context) 550 { 551 profiles.forEach(profile -> _profileAssignmentStorageEP.allowProfileToUser(userIdentity, profile, context)); 552 } 553 554 private void _copyDeniedUsers(UserIdentity userIdentity, Set<String> profiles, String context) 555 { 556 profiles.forEach(profile -> _profileAssignmentStorageEP.denyProfileToUser(userIdentity, profile, context)); 557 } 558 559 private void _copyAllowedGroups(GroupIdentity groupIdentity, Set<String> profiles, String context) 560 { 561 profiles.forEach(profile -> _profileAssignmentStorageEP.allowProfileToGroup(groupIdentity, profile, context)); 562 } 563 564 private void _copyDeniedGroups(GroupIdentity groupIdentity, Set<String> profiles, String context) 565 { 566 profiles.forEach(profile -> _profileAssignmentStorageEP.denyProfileToGroup(groupIdentity, profile, context)); 567 } 568 569 /** 570 * Delete rights from a context 571 * @param context the context 572 */ 573 public void deleteRights(String context) 574 { 575 // Get the mapping between users and profiles 576 Map<UserIdentity, Map<UserOrGroup, Set<String>>> profilesForUsers = _profileAssignmentStorageEP.getProfilesForUsers(context, null); 577 // Copy allowed user assignment profiles to new context 578 profilesForUsers.entrySet() 579 .forEach(entry -> _removeAllowedUsers(entry.getKey(), entry.getValue().get(UserOrGroup.ALLOWED), context)); 580 // Copy denied user assignment profiles to new context 581 profilesForUsers.entrySet() 582 .forEach(entry -> _removeDeniedUsers(entry.getKey(), entry.getValue().get(UserOrGroup.DENIED), context)); 583 584 // Get the mapping between groups and profiles 585 Map<GroupIdentity, Map<UserOrGroup, Set<String>>> profilesForGroups = _profileAssignmentStorageEP.getProfilesForGroups(context, null); 586 // Copy allowed group assignment profiles to new context 587 profilesForGroups.entrySet() 588 .forEach(entry -> _removeAllowedGroups(entry.getKey(), entry.getValue().get(UserOrGroup.ALLOWED), context)); 589 // Copy denied group assignment profiles to new context 590 profilesForGroups.entrySet() 591 .forEach(entry -> _removeDeniedGroups(entry.getKey(), entry.getValue().get(UserOrGroup.DENIED), context)); 592 593 // Get the mapping between anonymous or any connected user and profiles 594 Map<AnonymousOrAnyConnectedKeys, Set<String>> profilesForAnonymousOrAnyConnectedUser = _profileAssignmentStorageEP.getProfilesForAnonymousAndAnyConnectedUser(context); 595 // Copy allowed anonymous user assignment profiles to new context 596 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED) 597 .forEach(profileId -> _profileAssignmentStorageEP.removeAllowedProfileFromAnonymous(profileId, context)); 598 // Copy denied anonymous user assignment profiles to new context 599 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED) 600 .forEach(profileId -> _profileAssignmentStorageEP.removeDeniedProfileFromAnonymous(profileId, context)); 601 // Copy allowed any connected user assignment profiles to new context 602 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED) 603 .forEach(profileId -> _profileAssignmentStorageEP.removeAllowedProfileFromAnyConnectedUser(profileId, context)); 604 // Copy denied any connected user assignment profiles to new context 605 profilesForAnonymousOrAnyConnectedUser.get(AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED) 606 .forEach(profileId -> _profileAssignmentStorageEP.removeDeniedProfileFromAnyConnectedUser(profileId, context)); 607 } 608 609 private void _removeAllowedUsers(UserIdentity userIdentity, Set<String> profiles, String context) 610 { 611 profiles.forEach(profile -> _profileAssignmentStorageEP.removeAllowedProfileFromUser(userIdentity, profile, context)); 612 } 613 614 private void _removeDeniedUsers(UserIdentity userIdentity, Set<String> profiles, String context) 615 { 616 profiles.forEach(profile -> _profileAssignmentStorageEP.removeDeniedProfileFromUser(userIdentity, profile, context)); 617 } 618 619 private void _removeAllowedGroups(GroupIdentity groupIdentity, Set<String> profiles, String context) 620 { 621 profiles.forEach(profile -> _profileAssignmentStorageEP.removeAllowedProfileFromGroup(groupIdentity, profile, context)); 622 } 623 624 private void _removeDeniedGroups(GroupIdentity groupIdentity, Set<String> profiles, String context) 625 { 626 profiles.forEach(profile -> _profileAssignmentStorageEP.removeDeniedProfileFromGroup(groupIdentity, profile, context)); 627 } 628 629 /** 630 * Move an extraction file or folder inside a given directory 631 * 632 * @param srcRelPath The relative URI of file/folder to move 633 * @param targetRelPath The target relative URI of file/folder to move 634 * @return a result map with the name and uri of moved file in case of 635 * success. 636 * @throws IOException If an error occurred manipulating the source 637 */ 638 @Callable (rights = ExtractionConstants.MODIFY_EXTRACTION_RIGHT_ID) 639 public Map<String, Object> moveOrRenameExtractionDefinitionFile(String srcRelPath, String targetRelPath) throws IOException 640 { 641 Map<String, Object> result = new HashMap<>(); 642 643 FileSource srcFile = null; 644 FileSource targetFile = null; 645 try 646 { 647 srcFile = (FileSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR + srcRelPath); 648 targetFile = (FileSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR + targetRelPath); 649 650 String sourceContext = ExtractionAccessController.ROOT_CONTEXT + "/" + srcRelPath; 651 String targetContext = ExtractionAccessController.ROOT_CONTEXT + "/" + targetRelPath; 652 653 result = _moveOrRenameSource(srcFile, targetFile, sourceContext, targetContext); 654 655 if (result.containsKey("uri")) 656 { 657 String newURI = (String) result.get("uri"); 658 String path = newURI.substring(_root.getURI().length()); 659 result.put("path", path); 660 } 661 } 662 finally 663 { 664 _sourceResolver.release(srcFile); 665 _sourceResolver.release(targetFile); 666 } 667 668 return result; 669 } 670 671 /** 672 * Move a file or folder 673 * 674 * @param sourceFile The file/folder to move 675 * @param targetFile The target file 676 * @param sourceContext the source context 677 * @param targetContext the target context 678 * @return a result map with the name and uri of moved file in case of 679 * success. 680 * @throws IOException If an error occurred manipulating the source 681 */ 682 private Map<String, Object> _moveOrRenameSource(FileSource sourceFile, FileSource targetFile, String sourceContext, String targetContext) throws IOException 683 { 684 Map<String, Object> result = new HashMap<>(); 685 686 // Check if the user try to move files outside the root folder 687 if (!StringUtils.startsWith(sourceFile.getURI(), _root.getURI()) || !StringUtils.startsWith(targetFile.getURI(), _root.getURI())) 688 { 689 result.put("success", false); 690 result.put("error", "no-exists"); 691 692 getLogger().error("User '{}' tried to move parameter file outside of the root extraction directory.", _currentUserProvider.getUser()); 693 694 return result; 695 } 696 697 if (!sourceFile.exists()) 698 { 699 result.put("success", false); 700 result.put("error", "no-exists"); 701 return result; 702 } 703 704 if (targetFile.exists()) 705 { 706 // If both files are equals, there is no need to rename or move it 707 if (sourceFile.getFile().equals(targetFile.getFile())) 708 { 709 result.put("success", true); 710 result.put("name", targetFile.getName()); 711 result.put("uri", targetFile.getURI()); 712 return result; 713 } 714 else 715 { 716 result.put("success", false); 717 result.put("error", "already-exists"); 718 return result; 719 } 720 } 721 722 copyRightsRecursively(sourceContext, targetContext, sourceFile); 723 if (sourceFile.getFile().isFile()) 724 { 725 FileUtils.moveFile(sourceFile.getFile(), targetFile.getFile()); 726 } 727 else 728 { 729 FileUtils.moveDirectory(sourceFile.getFile(), targetFile.getFile()); 730 } 731 deleteRightsRecursively(sourceContext, targetFile); 732 733 result.put("success", true); 734 result.put("name", targetFile.getName()); 735 result.put("uri", targetFile.getURI()); 736 737 return result; 738 } 739 740 /** 741 * Copy rights from one context to another one 742 * @param sourceContext the source context 743 * @param targetContext the target context 744 * @param file the source of the file to copy 745 */ 746 public void copyRightsRecursively(String sourceContext, String targetContext, TraversableSource file) 747 { 748 copyRights(sourceContext, targetContext); 749 if (file.isCollection()) 750 { 751 try 752 { 753 for (TraversableSource child : (Collection<TraversableSource>) file.getChildren()) 754 { 755 copyRightsRecursively(sourceContext + "/" + child.getName(), targetContext + "/" + child.getName(), child); 756 } 757 } 758 catch (SourceException e) 759 { 760 throw new RuntimeException("Cannot list child elements of " + file.getURI(), e); 761 } 762 } 763 } 764 765 /** 766 * Copy rights from one context to another one 767 * @param context the context 768 * @param file the source of the file to copy 769 */ 770 public void deleteRightsRecursively(String context, TraversableSource file) 771 { 772 deleteRights(context); 773 if (file.isCollection()) 774 { 775 try 776 { 777 for (TraversableSource child : (Collection<TraversableSource>) file.getChildren()) 778 { 779 deleteRightsRecursively(context + "/" + child.getName(), child); 780 } 781 } 782 catch (SourceException e) 783 { 784 throw new RuntimeException("Cannot list child elements of " + file.getURI(), e); 785 } 786 } 787 } 788 789 /** 790 * Get the author of extraction 791 * @param extractionPath the path of the extraction 792 * @return the author 793 */ 794 public UserIdentity getAuthor(FileSource extractionPath) 795 { 796 return _getExtractionAuthorCache().get(extractionPath, path -> _getUserIdentityByExtractionFile(path)); 797 798 } 799 800 private UserIdentity _getUserIdentityByExtractionFile(FileSource extractionPath) 801 { 802 try 803 { 804 Extraction extraction = _definitionReader.readExtractionDefinitionFile(extractionPath.getFile()); 805 return extraction.getAuthor(); 806 } 807 catch (Exception e) 808 { 809 throw new RuntimeException("Cannot read extraction " + extractionPath, e); 810 } 811 } 812 813 private Cache<FileSource, UserIdentity> _getExtractionAuthorCache() 814 { 815 return this._cacheManager.get(EXTRACTION_AUTHOR_CACHE); 816 } 817 818 /** 819 * Remove the last separator from the uri if it has any 820 * @param uri the uri 821 * @return the uri without any ending separator 822 */ 823 public static String trimLastFileSeparator(String uri) 824 { 825 return StringUtils.endsWith(uri, "/") ? StringUtils.substringBeforeLast(uri, "/") : uri; 826 } 827 828 /** 829 * Get the path of all children that match the provided value. 830 * @param path the path to the extraction to consider as root 831 * @param value the value 832 * @return the list of path 833 */ 834 @Callable(rights = {"Runtime_Rights_Rights_Handle", "Extraction_Rights_ExecuteExtraction"}) // assignment for the tree in assignment tool 835 public List<String> getFilteredPath(String path, String value) 836 { 837 try 838 { 839 TraversableSource currentSrc = (TraversableSource) _sourceResolver.resolveURI(ExtractionConstants.DEFINITIONS_DIR + (path.length() > 0 ? "/" + path : "")); 840 841 List<String> result = _fileHelper.filterSources(currentSrc, value); 842 return result.stream() 843 .map(this::_toRelativePath) 844 .toList(); 845 } 846 catch (IOException e) 847 { 848 getLogger().error("Failed to filter extraction definition at path '" + path + "'", e); 849 return List.of(); 850 } 851 } 852 853 private String _toRelativePath(String absoluteURI) 854 { 855 // the root URI has a trailing slash or not depending on the existence of the folder at start time 856 // always remove the trailing slash so that we have a consistent behavior 857 return StringUtils.substringAfter(trimLastFileSeparator(absoluteURI), trimLastFileSeparator(_root.getURI())); 858 } 859}