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