001/*
002 *  Copyright 2017 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.core.impl.right;
017
018import java.util.Collections;
019import java.util.HashMap;
020import java.util.List;
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.collections.CollectionUtils;
030
031import org.ametys.core.cache.AbstractCacheManager;
032import org.ametys.core.cache.Cache;
033import org.ametys.core.group.GroupIdentity;
034import org.ametys.core.right.AccessController;
035import org.ametys.core.right.ProfileAssignmentStorageExtensionPoint;
036import org.ametys.core.right.RightManager;
037import org.ametys.core.right.RightProfilesDAO;
038import org.ametys.core.user.UserIdentity;
039import org.ametys.plugins.core.impl.cache.AbstractCacheKey;
040import org.ametys.runtime.i18n.I18nizableTextParameter;
041import org.ametys.runtime.i18n.I18nizableText;
042import org.ametys.runtime.plugin.component.AbstractLogEnabled;
043
044/**
045 * This class delegates all it can to the profile assignment storage extension point
046 */
047public abstract class AbstractProfileStorageBasedAccessController extends AbstractLogEnabled implements AccessController, Component, Serviceable, Initializable
048{
049    /**
050     * The knd of cache to get/set
051     */
052    protected enum CacheKind
053    {
054        /** for anonymous user */
055        ANONYMOUS,
056        /** for any connected user */
057        ANY_CONNECTED_USER,
058        /** for users */
059        USERS,
060        /** for user */
061        USER,
062        /** for groups */
063        GROUPS
064    }
065    
066    /** The instance of ObjectUserIdentity for anonymous */
067    protected static final UserIdentity __ANONYMOUS_USER_IDENTITY = null; 
068    /** The instance of ObjectUserIdentity for any connected user */
069    protected static final UserIdentity __ANY_CONTECTED_USER_IDENTITY = new UserIdentity(null, null);   
070
071    /** The extension point for the profile assignment storages */
072    protected ProfileAssignmentStorageExtensionPoint _profileAssignmentStorageEP;
073    /** The right profile DAO */
074    protected RightProfilesDAO _rightProfileDAO;
075    /** Cache Manager */
076    protected AbstractCacheManager _cacheManager;
077    
078    private final String _cache1 = RightManager.ROLE + "$" + this.getClass().getName() + "$Cache-1";
079    
080    private final String _cache2 = RightManager.ROLE + "$" + this.getClass().getName() + "$Cache-2";
081
082    
083    @Override
084    public void service(ServiceManager manager) throws ServiceException
085    {
086        _rightProfileDAO = (RightProfilesDAO) manager.lookup(RightProfilesDAO.ROLE);
087        _profileAssignmentStorageEP = (ProfileAssignmentStorageExtensionPoint) manager.lookup(ProfileAssignmentStorageExtensionPoint.ROLE);
088        _cacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE);
089    }
090
091    public void initialize() throws Exception
092    {
093        if (!_cacheManager.hasCache(_cache1))
094        {
095            _cacheManager.createRequestCache(_cache1, 
096                    _buildI18n("PLUGINS_CORE_RIGHT_CACHE_1_LABEL"),
097                    _buildI18n("PLUGINS_CORE_RIGHT_CACHE_1_DESCRIPTION"),
098                    true);
099        }
100
101        if (!_cacheManager.hasCache(_cache2))
102        {
103            _cacheManager.createRequestCache(_cache2, 
104                    _buildI18n("PLUGINS_CORE_RIGHT_CACHE_2_LABEL"),
105                    _buildI18n("PLUGINS_CORE_RIGHT_CACHE_2_DESCRIPTION"),
106                    true);
107        }
108       
109    }
110
111    private I18nizableText _buildI18n(String i18Key)
112    {
113        String catalogue = "plugin.core";
114        I18nizableText className = new I18nizableText(this.getClass().getSimpleName());
115        Map<String, I18nizableTextParameter> params = Map.of("id", className);
116        return new I18nizableText(catalogue, i18Key, params);
117    }
118    
119    public Map<String, AccessResult> getPermissionByRight(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
120    {
121        Map<String, AccessResult> rights = new HashMap<>();
122        
123        Map<String, AccessResult> permissionsByProfile = _profileAssignmentStorageEP.getPermissionsByProfile(user, userGroups, _convertContext(object));
124        
125        // Convert profile <-> accessresult to right <-> accessresult
126        for (String profileId : permissionsByProfile.keySet())
127        {
128            List<String> rights2 = _rightProfileDAO.getRights(profileId);
129            for (String rightId : rights2)
130            {
131                rights.put(rightId, AccessResult.merge(permissionsByProfile.get(profileId), rights.get(rightId)));
132            }
133        }
134
135        return rights;
136    }
137    
138    public AccessResult getPermission(UserIdentity user, Set<GroupIdentity> userGroups, String rightId, Object object)
139    {
140        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
141        if (profilesIds == null || profilesIds.isEmpty())
142        {
143            // No need for cache...
144            return AccessResult.UNKNOWN;
145        }
146        else
147        {
148            Object convertedObject = _convertContext(object);
149            return _getPermission(user, userGroups, profilesIds, object, convertedObject);
150        }
151    }
152    public AccessResult getReadAccessPermission(UserIdentity user, Set<GroupIdentity> userGroups, Object object)
153    {
154        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
155        // Some converted context may change so cache must be done with the convertedObject
156        Object convertedObject = _convertContext(object);
157        return _getPermission(user, userGroups, profilesIds, object, convertedObject);
158    }
159    /**
160     * Works for getPermission or getReadAccessPermission
161     * @param user The use
162     * @param userGroups The groups
163     * @param profilesIds The profiles
164     * @param object The original context
165     * @param convertedObject The converted context
166     * @return the computed result
167     */
168    protected AccessResult _getPermission(UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profilesIds, Object object, Object convertedObject)
169    {
170        @SuppressWarnings("unchecked")
171        Map<UserIdentity, AccessResult> cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USER);
172        if (cacheResult != null && cacheResult.containsKey(user))
173        {
174            return cacheResult.get(user);
175        }
176
177        Map<String, AccessResult> permissions = _profileAssignmentStorageEP.getPermissions(user, userGroups, profilesIds, convertedObject);
178        AccessResult result = AccessResult.merge(permissions.values());
179        cacheResult = cacheResult == null ? new HashMap<>() : cacheResult;
180        cacheResult.put(user, result);
181        _putInSecondCache(profilesIds, convertedObject, cacheResult, CacheKind.USER);
182        return result;
183    }
184
185    public AccessResult getPermissionForAnonymous(String rightId, Object object)
186    {
187        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
188        if (profilesIds == null || profilesIds.isEmpty())
189        {
190            // No need for cache...
191            return AccessResult.UNKNOWN;
192        }
193        else
194        {
195            // Some converted context may change so cache must be done with the convertedObject
196            Object convertedObject = _convertContext(object);
197            return _getPermissionForAnonymous(profilesIds, object, convertedObject);
198        }
199    }
200    public AccessResult getReadAccessPermissionForAnonymous(Object object)
201    {
202        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
203        // Some converted context may change so cache must be done with the convertedObject
204        Object convertedObject = _convertContext(object);
205        return _getPermissionForAnonymous(profilesIds, object, convertedObject);
206    }
207    /**
208     * Works for getPermissionForAnonymous and getReadAccessPermissionForAnonymous
209     * @param profilesIds The profiles ids
210     * @param object The context
211     * @param convertedObject The converted context
212     * @return The access result
213     */
214    protected AccessResult _getPermissionForAnonymous(Set<String> profilesIds, Object object, Object convertedObject)
215    {
216        // Try to retrieve in second cache
217        AccessResult cacheResult = (AccessResult) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.ANONYMOUS);
218        if (cacheResult != null)
219        {
220            return cacheResult;
221        }
222        
223        AccessResult result = _profileAssignmentStorageEP.getPermissionForAnonymous(profilesIds, convertedObject);
224        _putInSecondCache(profilesIds, convertedObject, result, CacheKind.ANONYMOUS);
225        return result;
226    }
227    
228    public AccessResult getPermissionForAnyConnectedUser(String rightId, Object object)
229    {
230        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
231        if (profilesIds == null || profilesIds.isEmpty())
232        {
233            // No need for cache...
234            return AccessResult.UNKNOWN;
235        }
236        else
237        {
238            // Some converted context may change so cache must be done with the convertedObject
239            Object convertedObject = _convertContext(object);
240            return _getPermissionForAnyConnectedUser(profilesIds, object, convertedObject);
241        }
242    }
243    public AccessResult getReadAccessPermissionForAnyConnectedUser(Object object)
244    {
245        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
246        // Some converted context may change so cache must be done with the convertedObject
247        Object convertedObject = _convertContext(object);
248        return _getPermissionForAnyConnectedUser(profilesIds, object, convertedObject);
249    }
250    /**
251     * Works for getPermissionForAnyConnectedUser and getReadAccessPermissionForAnyConnectedUser
252     * @param profilesIds The profiles ids
253     * @param object The context
254     * @param convertedObject The converted context
255     * @return the access result
256     */
257    protected AccessResult _getPermissionForAnyConnectedUser(Set<String> profilesIds, Object object, Object convertedObject)
258    {
259        // Try to retrieve in second cache
260        AccessResult cacheResult = (AccessResult) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.ANY_CONNECTED_USER);
261        if (cacheResult != null)
262        {
263            return cacheResult;
264        }
265        
266        AccessResult result = _profileAssignmentStorageEP.getPermissionForAnyConnectedUser(profilesIds, convertedObject);
267        _putInSecondCache(profilesIds, convertedObject, result, CacheKind.ANY_CONNECTED_USER);
268        return result;
269    }
270
271    public Map<UserIdentity, AccessResult> getPermissionByUser(String rightId, Object object)
272    {
273        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
274        if (profilesIds == null || profilesIds.isEmpty())
275        {
276            // No need for cache...
277            return Collections.EMPTY_MAP;
278        }
279        else
280        {
281            // Some converted context may change so cache must be done with the convertedObject
282            Object convertedObject = _convertContext(object);
283            return _getPermissionByUser(profilesIds, object, convertedObject);
284        }
285    }
286    public Map<UserIdentity, AccessResult> getReadAccessPermissionByUser(Object object)
287    {
288        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
289        // Some converted context may change so cache must be done with the convertedObject
290        Object convertedObject = _convertContext(object);
291        return _getPermissionByUser(profilesIds, object, convertedObject);
292    }
293    /**
294     * Works for getPermissionByUser and getReadAccessPermissionByUser
295     * @param profilesIds The profiles ids
296     * @param object The context
297     * @param convertedObject The converted context
298     * @return The users and their access results
299     */
300    protected Map<UserIdentity, AccessResult> _getPermissionByUser(Set<String> profilesIds, Object object, Object convertedObject)
301    {
302        // Try to retrieve in second cache
303        @SuppressWarnings("unchecked")
304        Map<UserIdentity, AccessResult> cacheResult = (Map<UserIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.USERS);
305        if (cacheResult != null)
306        {
307            return cacheResult;
308        }
309        
310        Map<UserIdentity, AccessResult> result = _profileAssignmentStorageEP.getPermissionsByUser(profilesIds, convertedObject);
311        _putInSecondCache(profilesIds, convertedObject, result, CacheKind.USERS);
312        return result;
313    }
314    
315    public Map<GroupIdentity, AccessResult> getPermissionByGroup(String rightId, Object object)
316    {
317        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
318        if (profilesIds == null || profilesIds.isEmpty())
319        {
320            // No need for cache...
321            return Collections.EMPTY_MAP;
322        }
323        else
324        {
325            // Some converted context may change so cache must be done with the convertedObject
326            Object convertedObject = _convertContext(object);
327            return _getPermissionByGroup(profilesIds, object, convertedObject);
328        }
329    }
330    public Map<GroupIdentity, AccessResult> getReadAccessPermissionByGroup(Object object)
331    {
332        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
333        // Some converted context may change so cache must be done with the convertedObject
334        Object convertedObject = _convertContext(object);
335        return _getPermissionByGroup(profilesIds, object, convertedObject);
336    }
337    /**
338     * Works for getPermissionByGroup and getReadAccessPermissionByGroup
339     * @param profilesIds The profiles ids
340     * @param object The context
341     * @param convertedObject The converted context
342     * @return The users and their access results
343     */
344    protected Map<GroupIdentity, AccessResult> _getPermissionByGroup(Set<String> profilesIds, Object object, Object convertedObject)
345    {
346        // Try to retrieve in second cache
347        @SuppressWarnings("unchecked")
348        Map<GroupIdentity, AccessResult> cacheResult = (Map<GroupIdentity, AccessResult>) _hasRightResultInSecondCache(convertedObject, profilesIds, CacheKind.GROUPS);
349        if (cacheResult != null)
350        {
351            return cacheResult;
352        }
353        
354        Map<GroupIdentity, AccessResult> result = _profileAssignmentStorageEP.getPermissionsByGroup(profilesIds, convertedObject);
355        _putInSecondCache(profilesIds, convertedObject, result, CacheKind.GROUPS);
356        return result;
357    }
358
359    /**
360     * For methods getXXXXPermissionYYY allow to have a modification of the context before transfering it to the profile assignment storage extension point
361     * The default implemenation keep the context as it is
362     * @param initialContext The right context that is supported
363     * @return the context modified
364     */
365    protected Object _convertContext(Object initialContext)
366    {
367        return initialContext;
368    }
369    
370    public boolean hasAnonymousAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
371    {
372        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
373        return _hasAnonymousAnyPermissionOnWorkspace(workspacesContexts, profilesIds);
374    }
375    public boolean hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
376    {
377        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
378        return _hasAnonymousAnyPermissionOnWorkspace(workspacesContexts, profilesIds);
379    }
380    private boolean _hasAnonymousAnyPermissionOnWorkspace(Set<Object> workspacesContexts, Set<String> profilesIds)
381    {
382        // Try to retrieve in first cache (the one which manages non-null contexts)
383        Boolean cacheResult = _hasRightResultInFirstCache(__ANONYMOUS_USER_IDENTITY, profilesIds, workspacesContexts);
384        if (cacheResult != null)
385        {
386            return cacheResult;
387        }
388
389        // Otherwise continue
390        Set<? extends Object> rootContexts = _convertWorkspaceToRootRightContexts(workspacesContexts);
391        
392        Set<String> determinedProfiles = profilesIds;
393        boolean rightResult = false;
394        
395        if (CollectionUtils.isNotEmpty(rootContexts))
396        {
397            Set<String> hasAnonymousAnyPermission = _profileAssignmentStorageEP.hasAnonymousAnyPermission(rootContexts, profilesIds);
398            if (!hasAnonymousAnyPermission.isEmpty())
399            {
400                rightResult = true;
401                determinedProfiles = hasAnonymousAnyPermission;
402            }
403        }
404        
405        _putInFirstCache(__ANONYMOUS_USER_IDENTITY, determinedProfiles, workspacesContexts, rightResult);
406        return rightResult;
407    }
408
409    public boolean hasAnyConnectedUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts)
410    {
411        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
412        return _hasAnyConnectedUserAnyPermissionOnWorkspace(workspacesContexts, profilesIds);
413    }
414    public boolean hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, String rightId)
415    {
416        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
417        return _hasAnyConnectedUserAnyPermissionOnWorkspace(workspacesContexts, profilesIds);
418    }
419    private boolean _hasAnyConnectedUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, Set<String> profilesIds)
420    {
421        // Try to retrieve in first cache (the one which manages non-null contexts)
422        Boolean cacheResult = _hasRightResultInFirstCache(__ANY_CONTECTED_USER_IDENTITY, profilesIds, workspacesContexts);
423        if (cacheResult != null)
424        {
425            return cacheResult;
426        }
427
428        // Otherwise continue
429        Set<? extends Object> rootContexts = _convertWorkspaceToRootRightContexts(workspacesContexts);
430        
431        Set<String> determinedProfiles = profilesIds;
432        boolean rightResult = false;
433        
434        if (CollectionUtils.isNotEmpty(rootContexts))
435        {
436            Set<String> hasAnyConnectedUserAnyPermission = _profileAssignmentStorageEP.hasAnyConnectedUserAnyPermission(rootContexts, profilesIds);
437            if (!hasAnyConnectedUserAnyPermission.isEmpty())
438            {
439                rightResult = true;
440                determinedProfiles = hasAnyConnectedUserAnyPermission;
441            }
442        }
443        
444        _putInFirstCache(__ANY_CONTECTED_USER_IDENTITY, determinedProfiles, workspacesContexts, rightResult);
445        return rightResult;        
446    }
447    
448    public boolean hasUserAnyReadAccessPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups)
449    {
450        Set<String> profilesIds = Collections.singleton(RightManager.READER_PROFILE_ID);
451        return _hasUserAnyPermissionOnWorkspace(workspacesContexts, user, userGroups, profilesIds);
452    }
453    public boolean hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, String rightId)
454    {
455        Set<String> profilesIds = _rightProfileDAO.getProfilesWithRight(rightId);
456        return _hasUserAnyPermissionOnWorkspace(workspacesContexts, user, userGroups, profilesIds);
457    }
458    private boolean _hasUserAnyPermissionOnWorkspace(Set<Object> workspacesContexts, UserIdentity user, Set<GroupIdentity> userGroups, Set<String> profilesIds)
459    {
460        // Try to retrieve in first cache (the one which manages non-null contexts)
461        Boolean cacheResult = _hasRightResultInFirstCache(user, profilesIds, workspacesContexts);
462        if (cacheResult != null)
463        {
464            return cacheResult;
465        }
466
467        // Otherwise continue
468        Set<? extends Object> rootContexts = _convertWorkspaceToRootRightContexts(workspacesContexts);
469        
470        Set<String> determinedProfiles = profilesIds;
471        boolean rightResult = false;
472        
473        if (CollectionUtils.isNotEmpty(rootContexts))
474        {
475            Set<String> hasUserAnyPermission = _profileAssignmentStorageEP.hasUserAnyPermission(rootContexts, user, userGroups, profilesIds);
476            if (!hasUserAnyPermission.isEmpty())
477            {
478                rightResult = true;
479                determinedProfiles = hasUserAnyPermission;
480            }
481        }
482        
483        _putInFirstCache(user, determinedProfiles, workspacesContexts, rightResult);
484        return rightResult;         
485    }
486    
487    /**
488     * Get the current workspaces contexts and turn it into root contexts in order to allow methods hasXXXAnyPermissionOnWorkspace to work
489     * @param workspacesContexts The workspace contexts. Such as '/${WorkspaceName}', '/admin'
490     * @return A null or empty set if the current AccessController does not apply to any workspace context, or the root object where ProfileAssignmentStorageExtension should start looking at to find any permission
491     */
492    protected abstract Set<? extends Object> _convertWorkspaceToRootRightContexts(Set<Object> workspacesContexts);
493    
494    
495    
496    
497
498    
499    /**
500     * Seek in cache
501     * @param userIdentity The user identity or AbstractProfileStorageBasedAccessController.__ANONYMOUS_USER_IDENTITY or AbstractProfileStorageBasedAccessController.__ANY_CONTECTED_USER_IDENTITY
502     * @param profilesIds The profiles identifiers
503     * @param object The context
504     * @return true or false if in cache. null otherwise
505     */
506    protected Boolean _hasRightResultInFirstCache(UserIdentity userIdentity, Set<String> profilesIds, Object object)
507    {
508        // On the contrary of the other cache, this one is split between profiles, as we check for the first true result (no negativity)
509        
510        if (profilesIds == null || profilesIds.isEmpty())
511        {
512            // No need for cache...
513            return false;
514        }
515        
516        Cache<Cache1Key, Boolean> mapCache = _cacheManager.get(_cache1);
517        if (mapCache != null)
518        {
519            int negativeProfiles = 0;
520            
521            for (String profileId: profilesIds)
522            {
523                Cache1Key key = Cache1Key.of(userIdentity, profileId, object);
524                if (mapCache.hasKey(key))
525                {
526                    if (mapCache.get(key))
527                    {
528                        getLogger().debug("Find entry in cache for [{}, {}, {}] => true", userIdentity, profilesIds, object);
529                        return true;
530                    }
531                    else
532                    {
533                        negativeProfiles++;
534                    }
535                }
536            }
537            
538            if (negativeProfiles == profilesIds.size())
539            {
540                // All required profiles were negative in cache
541                return false;
542            }  
543        }
544        getLogger().debug("Did not find entry in cache for [{}, {}, {}]", userIdentity, profilesIds, object);
545        return null;
546    }
547    
548    /**
549     * Add to cache
550     * @param userIdentity The user identity or AbstractProfileStorageBasedAccessController.__ANONYMOUS_USER_IDENTITY or AbstractProfileStorageBasedAccessController.__ANY_CONTECTED_USER_IDENTITY
551     * @param profilesIds The profiles identifiers
552     * @param object The context
553     * @param rightResult The cache value. true if hasXXX or false otherwise.
554     */
555    protected void _putInFirstCache(UserIdentity userIdentity, Set<String> profilesIds, Object object, boolean rightResult)
556    {
557        // On the contrary of the other cache, this one is split between profiles, as we check for the first true result (no negativity)
558
559        Cache<Cache1Key, Boolean> mapCache = _cacheManager.get(_cache1);
560        if (mapCache != null)
561        {
562            for (String profileId: profilesIds)
563            {
564                mapCache.put(Cache1Key.of(userIdentity, profileId, object), rightResult);
565            }   
566        }
567    }
568
569    /**
570     * Seek in cache
571     * @param object The context
572     * @param profilesIds The set of profile ids to consider
573     * @return The cached result per group. null otherwise
574     * @param key The kind of cache to use
575     */
576    protected Object _hasRightResultInSecondCache(Object object, Set<String> profilesIds, CacheKind key)
577    {
578        Cache<Cache2Key, Object> mapCache = _cacheManager.get(_cache2);
579
580        Object cacheResult = mapCache.get(Cache2Key.of(profilesIds, object, key));
581        if (cacheResult != null)
582        {
583            getLogger().debug("Find entry in cache for [{}, {}, {}] => {}", profilesIds, object, key, cacheResult);
584            return cacheResult;
585        }
586
587        getLogger().debug("Did not find entry in cache for [{}, {}, {}]", profilesIds, object, key);
588        return null;
589    }
590
591    /**
592     * Add to cache
593     * @param profilesIds The profiles ids to consider
594     * @param object The context
595     * @param result The result
596     * @param key The kind of cache to use
597     */
598    protected void _putInSecondCache(Set<String> profilesIds, Object object, Object result, CacheKind key)
599    {
600        Cache<Cache2Key, Object> mapCache = _cacheManager.get(_cache2);
601
602        if (mapCache != null)
603        {
604            mapCache.put(Cache2Key.of(profilesIds, object, key), result);
605        }
606        
607    }
608
609    static class Cache1Key extends AbstractCacheKey
610    {
611        Cache1Key(UserIdentity userIdentity, String profileId, Object object)
612        {
613            super(userIdentity, profileId, object);
614        }
615
616        static Cache1Key of(UserIdentity userIdentity, String profileId, Object object)
617        {
618            return new Cache1Key(userIdentity, profileId, object);
619        } 
620        
621    }
622    static class Cache2Key extends AbstractCacheKey
623    {
624        Cache2Key(Set<String> profileIds, Object object, CacheKind cacheKind)
625        {
626            super(profileIds, object, cacheKind);
627        }
628
629        static Cache2Key of(Set<String> profileIds, Object object, CacheKind cacheKind)
630        {
631            return new Cache2Key(profileIds, object, cacheKind);
632        } 
633        
634    }
635   
636}