001/*
002 *  Copyright 2018 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.Arrays;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Objects;
024import java.util.Optional;
025import java.util.Set;
026import java.util.stream.Collectors;
027
028import org.apache.avalon.framework.activity.Initializable;
029import org.apache.avalon.framework.configuration.Configuration;
030import org.apache.avalon.framework.configuration.ConfigurationException;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.commons.lang3.StringUtils;
034import org.apache.ibatis.session.SqlSession;
035
036import org.ametys.core.cache.AbstractCacheManager;
037import org.ametys.core.cache.Cache;
038import org.ametys.core.datasource.AbstractMyBatisDAO;
039import org.ametys.core.group.GroupIdentity;
040import org.ametys.core.right.ModifiableProfileAssignmentStorage;
041import org.ametys.core.right.ProfileAssignmentStorage;
042import org.ametys.core.user.UserIdentity;
043import org.ametys.runtime.i18n.I18nizableText;
044
045/**
046 * Jdbc implementation of {@link ProfileAssignmentStorage} which stores profile assignments in database.
047 * This only supports String objects as contexts.
048 */
049public class JdbcProfileAssignmentStorage extends AbstractMyBatisDAO implements ModifiableProfileAssignmentStorage, Initializable
050{   
051
052    private static final String __JDBC_PROFILE_ASSIGNMENT_STORAGE_CACHE = JdbcProfileAssignmentStorage.class.getName() + "$storage";
053    
054    private static final String __JDBC_PROFILE_ASSIGNMENT_INHERITANCE_CACHE = JdbcProfileAssignmentStorage.class.getName() + "$inheritance";
055    
056    /** The handled context */
057    protected String _supportedContext;
058    
059    private AbstractCacheManager _cacheManager;
060
061    @Override
062    public void service(ServiceManager manager) throws ServiceException
063    {
064        super.service(manager);
065        _cacheManager = (AbstractCacheManager) _manager.lookup(AbstractCacheManager.ROLE);
066    }
067      
068    @Override
069    public synchronized void initialize() throws Exception
070    {
071        if (!_cacheManager.hasCache(__JDBC_PROFILE_ASSIGNMENT_STORAGE_CACHE))
072        {
073            _cacheManager.createMemoryCache(
074                    __JDBC_PROFILE_ASSIGNMENT_STORAGE_CACHE,
075                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_RIGHTS_JDBC_PROFILE_ASSIGNMENT_STORAGE_CACHE_LABEL"),
076                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_RIGHTS_JDBC_PROFILE_ASSIGNMENT_STORAGE_CACHE_DESCRIPTION"),
077                    true, 
078                    null);
079        }
080
081        if (!_cacheManager.hasCache(__JDBC_PROFILE_ASSIGNMENT_INHERITANCE_CACHE))
082        {
083            _cacheManager.createMemoryCache(
084                    __JDBC_PROFILE_ASSIGNMENT_INHERITANCE_CACHE,
085                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_RIGHTS_JDBC_PROFILE_ASSIGNMENT_INHERITANCE_CACHE_LABEL"),
086                    new I18nizableText("plugin.admin", "PLUGINS_ADMIN_RIGHTS_JDBC_PROFILE_ASSIGNMENT_INHERITANCE_CACHE_DESCRIPTION"),
087                    true, 
088                    null);
089        }
090    }
091    
092    @Override
093    public void configure(Configuration configuration) throws ConfigurationException
094    {
095        super.configure(configuration);
096        _supportedContext = configuration.getChild("context").getValue();
097    }
098    
099    /**
100     * Dump the SQL database in an cached object
101     * @param context The context to seek
102     * @return The database
103     */
104    protected synchronized Database _getFullData(Object context)
105    {
106        String rootContext = "/" + StringUtils.split((String) context, '/')[0];
107     
108        return _getStorageCache().get(rootContext, key -> 
109        {
110
111            try (SqlSession session = getSession())
112            {
113                Map<String, Object> parameters = new HashMap<>();
114                parameters.put("context", rootContext);
115                
116                List<Map<String, String>> profiles = session.selectList("ProfilesAssignment.getAllProfiles", parameters);
117                Database db = new Database(profiles);
118                
119                return db;
120            }
121        });
122        
123    }
124    
125    /**
126     * Clear the cache of _getFullData
127     */
128    protected synchronized void _clearCache()
129    {
130        _getStorageCache().invalidateAll();
131    }
132    
133    /**
134     * Clear the cache of _getFullData
135     * @param context The context to seek
136     */
137    protected synchronized void _clearCache(Object context)
138    {
139        String rootContext = "/" + StringUtils.split((String) context, '/')[0];
140    
141        if (_getStorageCache().hasKey(rootContext))
142        {
143            _getStorageCache().invalidate(rootContext);
144        }
145    }
146    
147    /**
148     * Get the full inheritances from SQL database in an cached object
149     * @return The inheritances
150     */
151    protected synchronized Cache<String, Boolean> _getInheritances()
152    {
153        Cache<String, Boolean> inheritanceCache = _getInheritanceCache();
154        if (!inheritanceCache.isInitialized())
155        {
156            try (SqlSession session = getSession())
157            {
158                List<Map<String, Object>> inheritances = session.selectList("ProfilesAssignment.getInheritances", new HashMap<>());
159                
160                inheritances.stream().forEach(result -> 
161                {
162                    inheritanceCache.put((String) result.get("context"), (Boolean) result.get("disallow"));
163                });
164            }
165        }
166        
167        return inheritanceCache;
168    }
169    
170    /**
171     * Clear the cache of _getFullData
172     */
173    protected synchronized void _clearInheritanceCache()
174    {
175        _getInheritanceCache().invalidateAll();
176    }
177    
178    /* -------------- */
179    /* HAS PERMISSION */
180    /* -------------- */
181    
182    /**
183     * Get the object context with prefix if necessary
184     * @param context The context object
185     * @return The prefixed object
186     */
187    protected Object getObjectWithPrefix (Object context)
188    {
189        return context;
190    }
191    
192    /**
193     * Get the prefix for object context 
194     * @return The prefix. Can be null if no prefix is necessary
195     */
196    protected String getPrefix ()
197    {
198        return null;
199    }
200
201    public Set<String> hasUserAnyAllowedProfile(Set<? extends Object> rootContexts, UserIdentity user, Set<String> profileIds)
202    {
203        String prefix = getPrefix();
204        Set<String> stringPrefixContexts = rootContexts.stream().filter(String.class::isInstance).map(context -> (prefix != null ? prefix : "") + (String) context).collect(Collectors.toSet());
205
206        Set<String> anyAllowedProfiles = new HashSet<>();
207        for (String stringPrefixContext : stringPrefixContexts)
208        {
209            Set<String> profiles = _getFullData(stringPrefixContext)
210                .getAlloweUserData()
211                .entrySet().stream()
212                .filter(entry -> _equalsOrStartsWith(entry.getKey(), stringPrefixContext))
213                .map(Map.Entry::getValue)
214                .map(m -> m.get(user))
215                .filter(Objects::nonNull)
216                .flatMap(Set::stream)
217                .collect(Collectors.toSet());
218            
219            anyAllowedProfiles.addAll(profiles);
220        }
221        
222        for (String profileId : profileIds)
223        {
224            if (anyAllowedProfiles.contains(profileId))
225            {
226                return anyAllowedProfiles;
227            }
228        }
229        return Set.of();
230    }
231
232    public Set<String> hasGroupAnyAllowedProfile(Set<? extends Object> rootContexts, Set<GroupIdentity> groups, Set<String> profileIds)
233    {
234        String prefix = getPrefix();
235        Set<String> stringPrefixContexts = rootContexts.stream().filter(String.class::isInstance).map(context -> (prefix != null ? prefix : "") + (String) context).collect(Collectors.toSet());
236
237        Set<String> anyAllowedProfiles = new HashSet<>();
238        for (String stringPrefixContext : stringPrefixContexts)
239        {
240            _getFullData(stringPrefixContext)
241                .getAlloweGroupData()
242                .entrySet()
243                .stream()
244                .filter(entry -> _equalsOrStartsWith(entry.getKey(), stringPrefixContext))
245                .map(Map.Entry::getValue)
246                .forEach(profilesByGroup -> 
247                {
248                    for (GroupIdentity group : groups)
249                    {
250                        Set<String> profiles = profilesByGroup.get(group);
251                        if (profiles != null)
252                        {
253                            anyAllowedProfiles.addAll(profiles);
254                        }
255                    }
256                });
257        }
258        
259        for (String profileId : profileIds)
260        {
261            if (anyAllowedProfiles.contains(profileId))
262            {
263                return anyAllowedProfiles;
264            }
265        }
266        
267        return Set.of();
268    }
269
270    public Set<String> hasAnyConnectedAnyAllowedProfile(Set<? extends Object> rootContexts, Set<String> profileIds)
271    {
272        String prefix = getPrefix();
273        Set<String> stringPrefixContexts = rootContexts.stream().filter(String.class::isInstance).map(context -> (prefix != null ? prefix : "") + (String) context).collect(Collectors.toSet());
274
275        Set<String> anyAllowedProfiles = new HashSet<>();
276        for (String stringPrefixContext : stringPrefixContexts)
277        {
278            Set<String> profiles = _getFullData(stringPrefixContext)
279                .getAllowedAnyConnected()
280                .entrySet().stream()
281                .filter(entry -> _equalsOrStartsWith(entry.getKey(), stringPrefixContext))
282                .map(Map.Entry::getValue)
283                .flatMap(Set::stream)
284                .collect(Collectors.toSet());
285            
286            anyAllowedProfiles.addAll(profiles);
287        }
288        
289        for (String profileId : profileIds)
290        {
291            if (anyAllowedProfiles.contains(profileId))
292            {
293                return anyAllowedProfiles;
294            }
295        }
296        return Set.of();
297    }
298    
299    public Set<String> hasAnonymousAnyAllowedProfile(Set<? extends Object> rootContexts, Set<String> profileIds)
300    {
301        String prefix = getPrefix();
302        Set<String> stringPrefixContexts = rootContexts.stream().filter(String.class::isInstance).map(context -> (prefix != null ? prefix : "") + (String) context).collect(Collectors.toSet());
303        
304        Set<String> anyAllowedProfiles = new HashSet<>();
305        for (String stringPrefixContext : stringPrefixContexts)
306        {
307            Set<String> profiles = _getFullData(stringPrefixContext)
308                .getAllowedAnonymous()
309                .entrySet().stream()
310                .filter(entry -> _equalsOrStartsWith(entry.getKey(), stringPrefixContext))
311                .map(Map.Entry::getValue)
312                .flatMap(Set::stream)
313                .collect(Collectors.toSet());
314            
315            anyAllowedProfiles.addAll(profiles);
316        }
317        
318        for (String profileId : profileIds)
319        {
320            if (anyAllowedProfiles.contains(profileId))
321            {
322                return anyAllowedProfiles;
323            }
324        }
325        return Set.of();
326    }
327    
328    private static boolean _equalsOrStartsWith(String value, String prefix)
329    {
330        return value.equals(prefix) || value.startsWith(prefix + "/");
331    }
332    
333    /* --------------------------------------- */
334    /* ALLOWED PROFILES FOR ANY CONNECTED USER */
335    /* --------------------------------------- */
336    
337    public Map<UserIdentity, Map<UserOrGroup, Set<String>>> getProfilesForUsers(Object object, UserIdentity user)
338    {
339        Map<UserIdentity, Map<UserOrGroup, Set<String>>> result = new HashMap<>();
340        
341        Map<UserIdentity, Set<String>> allowedProfilesByUser = Optional.ofNullable(_getFullData(object).getAlloweUserData().get(object)).orElse(Map.of());
342        Map<UserIdentity, Set<String>> deniedProfilesByUser = Optional.ofNullable(_getFullData(object).getDeniedUserData().get(object)).orElse(Map.of());
343        
344        Set<UserIdentity> userKeys;
345        if (user != null)
346        {
347            userKeys = Set.of(user);
348        }
349        else
350        {
351            userKeys = new HashSet<>();
352            userKeys.addAll(allowedProfilesByUser.keySet());
353            userKeys.addAll(deniedProfilesByUser.keySet());
354        }
355        
356        for (UserIdentity userKey : userKeys)
357        {
358            result.put(userKey, Map.of(UserOrGroup.ALLOWED, allowedProfilesByUser.containsKey(userKey) ? allowedProfilesByUser.get(userKey) : Set.of(),
359                                     UserOrGroup.DENIED, deniedProfilesByUser.containsKey(userKey) ? deniedProfilesByUser.get(userKey) : Set.of()));
360        }
361        
362        return result;
363    }
364    
365    public void addAllowedProfilesForAnyConnectedUser(Object object, Set<String> profileIds)
366    {
367        _clearCache(object);
368        
369        try (SqlSession session = getSession())
370        {
371            Object prefixedObject = getObjectWithPrefix(object);
372            for (String profileId : profileIds)
373            {
374                Map<String, Object> parameters = new HashMap<>();
375                parameters.put("context", prefixedObject);
376                parameters.put("profileIds", Arrays.asList(profileId));
377                
378                List<Map<String, String>> allowedProfiles = session.selectList("ProfilesAssignment.getAnyConnectedAllowedProfiles", parameters);
379                
380                if (allowedProfiles.isEmpty())
381                {
382                    parameters.put("profileId", profileId);
383                    session.insert("ProfilesAssignment.addAllowedAnyConnected", parameters);
384                }
385                else
386                {
387                    getLogger().debug("Profile {} is already allowed for anyconnected on context {}", profileId, prefixedObject);
388                }
389                
390            }
391            
392            session.commit();
393        }
394    }
395    
396    public void removeAllowedProfilesForAnyConnectedUser(Object object, Set<String> profileIds)
397    {
398        _clearCache(object);
399        
400        try (SqlSession session = getSession(true))
401        {
402            Map<String, Object> parameters = new HashMap<>();
403            parameters.put("context", getObjectWithPrefix(object));
404            parameters.put("profileIds", profileIds);
405            session.delete("ProfilesAssignment.deleteAllowedAnyConnected", parameters);
406        }
407    }
408    
409    
410    /* -------------------------------------- */
411    /* DENIED PROFILES FOR ANY CONNECTED USER */
412    /* -------------------------------------- */
413    
414    public Map<AnonymousOrAnyConnectedKeys, Set<String>> getProfilesForAnonymousAndAnyConnectedUser(Object object)
415    {
416        return Map.of(AnonymousOrAnyConnectedKeys.ANONYMOUS_ALLOWED, Optional.ofNullable(_getFullData(object).getAllowedAnonymous().get(object)).orElse(Set.of()),
417                AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED, Optional.ofNullable(_getFullData(object).getDeniedAnonymous().get(object)).orElse(Set.of()),
418                AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_ALLOWED, Optional.ofNullable(_getFullData(object).getAllowedAnyConnected().get(object)).orElse(Set.of()),
419                AnonymousOrAnyConnectedKeys.ANYCONNECTEDUSER_DENIED, Optional.ofNullable(_getFullData(object).getDeniedAnyConnected().get(object)).orElse(Set.of()));
420    }
421    
422    public void addDeniedProfilesForAnyConnectedUser(Object object, Set<String> profileIds)
423    {
424        _clearCache(object);
425        
426        try (SqlSession session = getSession())
427        {
428            Object prefixedObject = getObjectWithPrefix(object);
429            
430            for (String profileId : profileIds)
431            {
432                Map<String, Object> parameters = new HashMap<>();
433                parameters.put("context", prefixedObject);
434                parameters.put("profileIds", Arrays.asList(profileId));
435                
436                List<Map<String, String>> deniedProfiles = session.selectList("ProfilesAssignment.getAnyConnectedDeniedProfiles", parameters);
437                
438                if (deniedProfiles.isEmpty())
439                {
440                    parameters.put("profileId", profileId);
441                    session.insert("ProfilesAssignment.addDeniedAnyConnected", parameters);
442                }
443                else
444                {
445                    getLogger().debug("Profile {} is already denied for anyconnected on context {}", profileId, prefixedObject);
446                }
447                
448            }
449            
450            session.commit();
451        }
452    }
453    
454    public void removeDeniedProfilesForAnyConnectedUser(Object object, Set<String> profileIds)
455    {
456        _clearCache(object);
457        
458        try (SqlSession session = getSession(true))
459        {
460            Map<String, Object> parameters = new HashMap<>();
461            parameters.put("context", getObjectWithPrefix(object));
462            parameters.put("profileIds", profileIds);
463            session.delete("ProfilesAssignment.deleteDeniedAnyConnected", parameters);
464        }
465    }
466    
467    /* ------------------------------ */
468    /* ALLOWED PROFILES FOR ANONYMOUS */
469    /* ------------------------------ */
470    
471    public void addAllowedProfilesForAnonymous(Object object, Set<String> profileIds)
472    {
473        _clearCache(object);
474        
475        try (SqlSession session = getSession())
476        {
477            Object prefixedObject = getObjectWithPrefix(object);
478            
479            for (String profileId : profileIds)
480            {
481                Map<String, Object> parameters = new HashMap<>();
482                parameters.put("context", prefixedObject);
483                parameters.put("profileIds", Arrays.asList(profileId));
484                
485                List<Map<String, String>> allowedProfiles = session.selectList("ProfilesAssignment.getAnonymousAllowedProfiles", parameters);
486                
487                if (allowedProfiles.isEmpty())
488                {
489                    parameters.put("profileId", profileId);
490                    session.insert("ProfilesAssignment.addAllowedAnonymous", parameters);
491                }
492                else
493                {
494                    getLogger().debug("Profile {} is already allowed for anonymous on context {}", profileId, prefixedObject);
495                }
496            }
497            
498            session.commit();
499        }
500    }
501    
502    public void removeAllowedProfilesForAnonymous(Object object, Set<String> profileIds)
503    {
504        _clearCache(object);
505        
506        try (SqlSession session = getSession(true))
507        {
508            Map<String, Object> parameters = new HashMap<>();
509            parameters.put("context", getObjectWithPrefix(object));
510            parameters.put("profileIds", profileIds);
511            session.delete("ProfilesAssignment.deleteAllowedAnonymous", parameters);
512        }
513    }
514    
515    
516    /* ----------------------------- */
517    /* DENIED PROFILES FOR ANONYMOUS */
518    /* ----------------------------- */
519    
520    public void addDeniedProfilesForAnonymous(Object object, Set<String> profileIds)
521    {
522        _clearCache(object);
523        
524        try (SqlSession session = getSession())
525        {
526            Object prefixedObject = getObjectWithPrefix(object);
527            for (String profileId : profileIds)
528            {
529                Map<String, Object> parameters = new HashMap<>();
530                parameters.put("context", prefixedObject);
531                parameters.put("profileIds", Arrays.asList(profileId));
532                
533                List<Map<String, String>> deniedProfiles = session.selectList("ProfilesAssignment.getAnonymousDeniedProfiles", parameters);
534                
535                if (deniedProfiles.isEmpty())
536                {
537                    parameters.put("profileId", profileId);
538                    session.insert("ProfilesAssignment.addDeniedAnonymous", parameters);
539                }
540                else
541                {
542                    getLogger().debug("Profile {} is already denied for anonymous on context {}", profileId, prefixedObject);
543                }
544            }
545            
546            session.commit();
547        }
548    }
549    
550    public void removeDeniedProfilesForAnonymous(Object object, Set<String> profileIds)
551    {
552        _clearCache(object);
553        
554        try (SqlSession session = getSession(true))
555        {
556            Map<String, Object> parameters = new HashMap<>();
557            parameters.put("context", getObjectWithPrefix(object));
558            parameters.put("profileIds", profileIds);
559            session.delete("ProfilesAssignment.deleteDeniedAnonymous", parameters);
560        }
561    }
562    
563    
564    /* --------------------------- */
565    /* MANAGEMENT OF ALLOWED USERS */
566    /* --------------------------- */
567    
568    public void addAllowedUsers(Set<UserIdentity> users, Object object, String profileId)
569    {
570        _clearCache(object);
571        
572        try (SqlSession session = getSession())
573        {
574            Object prefixedObject = getObjectWithPrefix(object);
575            
576            for (UserIdentity userIdentity : users)
577            {
578                Map<String, Object> parameters = new HashMap<>();
579                parameters.put("login", userIdentity.getLogin());
580                parameters.put("population", userIdentity.getPopulationId());
581                parameters.put("context", prefixedObject);
582                parameters.put("profileIds", Arrays.asList(profileId));
583                
584                List<Map<String, String>> allowedProfiles = session.selectList("ProfilesAssignment.getUserAllowedProfiles", parameters);
585                
586                if (allowedProfiles.isEmpty())
587                {
588                    parameters.put("profileId", profileId);
589                    session.insert("ProfilesAssignment.addAllowedUser", parameters);
590                }
591                else
592                {
593                    getLogger().debug("Login {} has already profile {} on context {}", userIdentity, profileId, prefixedObject);
594                }
595                
596            }
597            
598            session.commit();
599        }
600    }
601    
602    public void removeAllowedUsers(Set<UserIdentity> users, Object object, String profileId)
603    {
604        _clearCache(object);
605        
606        try (SqlSession session = getSession())
607        {
608            for (UserIdentity userIdentity : users)
609            {
610                Map<String, Object> parameters = new HashMap<>();
611                parameters.put("login", userIdentity.getLogin());
612                parameters.put("population", userIdentity.getPopulationId());
613                parameters.put("profileIds", Arrays.asList(profileId));
614                if (object != null)
615                {
616                    parameters.put("context", getObjectWithPrefix(object));
617                }
618                
619                session.delete("ProfilesAssignment.deleteAllowedUser", parameters);
620            }
621            session.commit();
622        }
623    }
624    
625    public void removeAllowedUsers(Set<UserIdentity> users, Object object)
626    {
627        _clearCache(object);
628        
629        try (SqlSession session = getSession())
630        {
631            for (UserIdentity userIdentity : users)
632            {
633                Map<String, Object> parameters = new HashMap<>();
634                parameters.put("login", userIdentity.getLogin());
635                parameters.put("population", userIdentity.getPopulationId());
636                if (object != null)
637                {
638                    parameters.put("context", getObjectWithPrefix(object));
639                }
640                
641                session.delete("ProfilesAssignment.deleteAllowedUser", parameters);
642            }
643            session.commit();
644        }
645    }
646    
647    
648    /* -------------------- */
649    /* MANAGEMENT OF GROUPS */
650    /* -------------------- */
651    
652    public Map<GroupIdentity, Map<UserOrGroup, Set<String>>> getProfilesForGroups(Object object, Set<GroupIdentity> groups)
653    {
654        Map<GroupIdentity, Map<UserOrGroup, Set<String>>> result = new HashMap<>();
655        
656        Map<GroupIdentity, Set<String>> allowedProfilesByGroup = Optional.ofNullable(_getFullData(object).getAlloweGroupData().get(object)).orElse(Map.of());
657        Map<GroupIdentity, Set<String>> deniedProfilesByGroup = Optional.ofNullable(_getFullData(object).getDeniedGroupData().get(object)).orElse(Map.of());
658        
659        Set<GroupIdentity> groupKeys;
660        if (groups != null)
661        {
662            groupKeys = groups;
663        }
664        else
665        {
666            groupKeys = new HashSet<>();
667            groupKeys.addAll(allowedProfilesByGroup.keySet());
668            groupKeys.addAll(deniedProfilesByGroup.keySet());
669        }
670        
671        for (GroupIdentity group : groupKeys)
672        {
673            result.put(group, Map.of(UserOrGroup.ALLOWED, allowedProfilesByGroup.containsKey(group) ? allowedProfilesByGroup.get(group) : Set.of(),
674                                     UserOrGroup.DENIED, deniedProfilesByGroup.containsKey(group) ? deniedProfilesByGroup.get(group) : Set.of()));
675        }
676        
677        return result;
678    }
679    
680    public void addAllowedGroups(Set<GroupIdentity> groups, Object object, String profileId)
681    {
682        _clearCache(object);
683        
684        try (SqlSession session = getSession())
685        {
686            Object prefixedObject = getObjectWithPrefix(object);
687            for (GroupIdentity group : groups)
688            {
689                Map<String, Object> parameters = new HashMap<>();
690                parameters.put("groupId", group.getId());
691                parameters.put("groupDirectory", group.getDirectoryId());
692                parameters.put("context", prefixedObject);
693                parameters.put("profileIds", Arrays.asList(profileId));
694                
695                List<Map<String, String>> allowedProfiles = session.selectList("ProfilesAssignment.getGroupAllowedProfiles", parameters);
696                
697                if (allowedProfiles.isEmpty())
698                {
699                    parameters.put("profileId", profileId);
700                    session.insert("ProfilesAssignment.addAllowedGroup", parameters);
701                }
702                else
703                {
704                    getLogger().debug("Group {} is already allowed for profile {} on context {}", group, profileId, prefixedObject);
705                }
706            }
707            
708            session.commit();
709        }
710    }
711    
712    public void removeAllowedGroups(Set<GroupIdentity> groups, Object object, String profileId)
713    {
714        _clearCache(object);
715        
716        try (SqlSession session = getSession())
717        {
718            for (GroupIdentity group : groups)
719            {
720                Map<String, Object> parameters = new HashMap<>();
721                parameters.put("groupId", group.getId());
722                parameters.put("groupDirectory", group.getDirectoryId());
723                parameters.put("profileIds", Arrays.asList(profileId));
724                if (object != null)
725                {
726                    parameters.put("context", getObjectWithPrefix(object));
727                }
728                
729                session.delete("ProfilesAssignment.deleteAllowedGroup", parameters);
730            }
731            session.commit();
732        }
733    }
734    
735    public void removeAllowedGroups(Set<GroupIdentity> groups, Object object)
736    {
737        _clearCache(object);
738        
739        try (SqlSession session = getSession())
740        {
741            for (GroupIdentity group : groups)
742            {
743                Map<String, Object> parameters = new HashMap<>();
744                parameters.put("groupId", group.getId());
745                parameters.put("groupDirectory", group.getDirectoryId());
746                if (object != null)
747                {
748                    parameters.put("context", getObjectWithPrefix(object));
749                }
750                
751                session.delete("ProfilesAssignment.deleteAllowedGroup", parameters);
752            }
753            session.commit();
754        }
755    }
756    
757    public void addDeniedUsers(Set<UserIdentity> users, Object object, String profileId)
758    {
759        _clearCache(object);
760        
761        try (SqlSession session = getSession())
762        {
763            Object prefixedObject = getObjectWithPrefix(object);
764            for (UserIdentity userIdentity : users)
765            {
766                Map<String, Object> parameters = new HashMap<>();
767                parameters.put("login", userIdentity.getLogin());
768                parameters.put("population", userIdentity.getPopulationId());
769                parameters.put("context", prefixedObject);
770                parameters.put("profileIds", Arrays.asList(profileId));
771                
772                List<Map<String, String>> deniedProfiles = session.selectList("ProfilesAssignment.getUserDeniedProfiles", parameters);
773                
774                if (deniedProfiles.isEmpty())
775                {
776                    parameters.put("profileId", profileId);
777                    session.insert("ProfilesAssignment.addDeniedUser", parameters);
778                }
779                else
780                {
781                    getLogger().debug("Login {} is already denied for profile {} on context {}", userIdentity, profileId, prefixedObject);
782                }
783                
784            }
785            
786            session.commit();
787        }
788    }
789    
790    public void removeDeniedUsers(Set<UserIdentity> users, Object object, String profileId)
791    {
792        _clearCache(object);
793        
794        try (SqlSession session = getSession())
795        {
796            for (UserIdentity userIdentity : users)
797            {
798                Map<String, Object> parameters = new HashMap<>();
799                parameters.put("login", userIdentity.getLogin());
800                parameters.put("population", userIdentity.getPopulationId());
801                parameters.put("profileIds", Arrays.asList(profileId));
802                if (object != null)
803                {
804                    parameters.put("context", getObjectWithPrefix(object));
805                }
806                
807                session.delete("ProfilesAssignment.deleteDeniedUser", parameters);
808            }
809            session.commit();
810        }
811    }
812    
813    public void removeDeniedUsers(Set<UserIdentity> users, Object object)
814    {
815        _clearCache(object);
816        
817        try (SqlSession session = getSession())
818        {
819            for (UserIdentity userIdentity : users)
820            {
821                Map<String, Object> parameters = new HashMap<>();
822                parameters.put("login", userIdentity.getLogin());
823                parameters.put("population", userIdentity.getPopulationId());
824                if (object != null)
825                {
826                    parameters.put("context", getObjectWithPrefix(object));
827                }
828                
829                session.delete("ProfilesAssignment.deleteDeniedUser", parameters);
830            }
831            session.commit();
832        }
833    }
834    
835    public void addDeniedGroups(Set<GroupIdentity> groups, Object object, String profileId)
836    {
837        _clearCache(object);
838        
839        try (SqlSession session = getSession())
840        {
841            Object prefixedObject = getObjectWithPrefix(object);
842            for (GroupIdentity group : groups)
843            {
844                Map<String, Object> parameters = new HashMap<>();
845                parameters.put("groupId", group.getId());
846                parameters.put("groupDirectory", group.getDirectoryId());
847                parameters.put("context", prefixedObject);
848                parameters.put("profileIds", Arrays.asList(profileId));
849                
850                List<Map<String, String>> deniedProfiles = session.selectList("ProfilesAssignment.getGroupDeniedProfiles", parameters);
851                
852                if (deniedProfiles.isEmpty())
853                {
854                    parameters.put("profileId", profileId);
855                    session.insert("ProfilesAssignment.addDeniedGroup", parameters);
856                }
857                else
858                {
859                    getLogger().debug("Group {} is already denied for profile {} on context {}", group, profileId, prefixedObject);
860                }
861            }
862            
863            session.commit();
864        }
865    }
866    
867    public void removeDeniedGroups(Set<GroupIdentity> groups, Object object, String profileId)
868    {
869        _clearCache(object);
870        
871        try (SqlSession session = getSession())
872        {
873            for (GroupIdentity group : groups)
874            {
875                Map<String, Object> parameters = new HashMap<>();
876                parameters.put("groupId", group.getId());
877                parameters.put("groupDirectory", group.getDirectoryId());
878                parameters.put("profileIds", Arrays.asList(profileId));
879                if (object != null)
880                {
881                    parameters.put("context", getObjectWithPrefix(object));
882                }
883                
884                session.delete("ProfilesAssignment.deleteDeniedGroup", parameters);
885            }
886            session.commit();
887        }
888    }
889    
890    public void removeDeniedGroups(Set<GroupIdentity> groups, Object object)
891    {
892        _clearCache(object);
893        
894        try (SqlSession session = getSession())
895        {
896            for (GroupIdentity group : groups)
897            {
898                Map<String, Object> parameters = new HashMap<>();
899                parameters.put("groupId", group.getId());
900                parameters.put("groupDirectory", group.getDirectoryId());
901                if (object != null)
902                {
903                    parameters.put("context", getObjectWithPrefix(object));
904                }
905                
906                session.delete("ProfilesAssignment.deleteDeniedGroup", parameters);
907            }
908            session.commit();
909        }
910    }
911    
912    /* ----------- */
913    /* INHERITANCE */
914    /* ----------- */
915    
916    public void disallowInheritance(Object object, boolean disallow)
917    {
918        try (SqlSession session = getSession())
919        {
920            Object ctxWithPrefix = getObjectWithPrefix(object);
921            
922            Map<String, Object> parameters = new HashMap<>();
923            
924            parameters.put("context", ctxWithPrefix);
925            parameters.put("disallow", disallow);
926            
927            if (_getInheritances().hasKey((String) ctxWithPrefix))
928            {
929                session.insert("ProfilesAssignment.updateInheritance", parameters);
930            }
931            else
932            {
933                session.insert("ProfilesAssignment.insertInheritance", parameters);
934            }
935            session.commit();
936            
937            _clearInheritanceCache();
938        }
939    }
940    
941    public boolean isInheritanceDisallowed(Object object)
942    {
943        Object ctxWithPrefix = getObjectWithPrefix(object);
944        
945        Boolean isDisallowed = _getInheritances().get((String) ctxWithPrefix);
946        return isDisallowed != null && Boolean.TRUE.equals(isDisallowed);
947    }
948    
949    
950    /* ------ */
951    /* REMOVE */
952    /* ------ */
953    
954    public void removeProfile(String profileId)
955    {
956        _clearCache();
957        
958        try (SqlSession session = getSession())
959        {
960            Map<String, Object> parameters = new HashMap<>();
961            parameters.put("profileIds", Arrays.asList(profileId));
962            
963            session.delete("ProfilesAssignment.deleteAllowedUser", parameters);
964            session.delete("ProfilesAssignment.deleteDeniedUser", parameters);
965            session.delete("ProfilesAssignment.deleteAllowedGroup", parameters);
966            session.delete("ProfilesAssignment.deleteDeniedGroup", parameters);
967            session.delete("ProfilesAssignment.deleteAllowedAnonymous", parameters);
968            session.delete("ProfilesAssignment.deleteDeniedAnonymous", parameters);
969            session.delete("ProfilesAssignment.deleteAllowedAnyConnected", parameters);
970            session.delete("ProfilesAssignment.deleteDeniedAnyConnected", parameters);
971            
972            session.commit();
973        }
974    }
975    
976    public void removeUser(UserIdentity user)
977    {
978        _clearCache();
979        
980        try (SqlSession session = getSession())
981        {
982            Map<String, String> parameters = new HashMap<>();
983            parameters.put("login", user.getLogin());
984            parameters.put("population", user.getPopulationId());
985            
986            session.delete("ProfilesAssignment.deleteAllowedUser", parameters);
987            session.delete("ProfilesAssignment.deleteDeniedUser", parameters);
988            
989            session.commit();
990        }
991    }
992    
993    public void removeGroup(GroupIdentity group)
994    {
995        _clearCache();
996        
997        try (SqlSession session = getSession())
998        {
999            Map<String, String> parameters = new HashMap<>();
1000            parameters.put("groupId", group.getId());
1001            parameters.put("groupDirectory", group.getDirectoryId());
1002            
1003            session.delete("ProfilesAssignment.deleteAllowedGroup", parameters);
1004            session.delete("ProfilesAssignment.deleteDeniedGroup", parameters);
1005            
1006            session.commit();
1007        }
1008    }
1009    
1010    /* ------------------------------ */
1011    /* SUPPORT OF OBJECT AND PRIORITY */
1012    /* ------------------------------ */
1013    
1014    public boolean isSupported(Object object)
1015    {
1016        if (object instanceof String)
1017        {
1018            String context = (String) object;
1019            return context.equals(_supportedContext) || context.startsWith(_supportedContext + "/");
1020        }
1021        return false;
1022    }
1023    
1024    public boolean isRootContextSupported(Object rootContext)
1025    {
1026        return isSupported(rootContext);
1027    }
1028    
1029    public int getPriority()
1030    {
1031        return ProfileAssignmentStorage.MIN_PRIORITY;
1032    }
1033    
1034    private static class Database
1035    {
1036        // Context, ProfileId
1037        private Map<String, Set<String>> _allowedAnonymousData;
1038        // Context, ProfileId
1039        private Map<String, Set<String>> _deniedAnonymousData;
1040        // Context, ProfileId
1041        private Map<String, Set<String>> _allowedAnyConnectedData;
1042        // Context, ProfileId
1043        private Map<String, Set<String>> _deniedAnyConnectedData;
1044        // Context, UserIdentity/GroupIdenty ProfileId
1045        private Map<String, Map<UserIdentity, Set<String>>> _allowedUserData;
1046        // Context, UserIdentity/GroupIdenty ProfileId
1047        private Map<String, Map<UserIdentity, Set<String>>> _deniedUserData;
1048        // Context, UserIdentity/GroupIdenty ProfileId
1049        private Map<String, Map<GroupIdentity, Set<String>>> _allowedGroupData;
1050        // Context, UserIdentity/GroupIdenty ProfileId
1051        private Map<String, Map<GroupIdentity, Set<String>>> _deniedGroupData;
1052
1053        Database(List<Map<String, String>> data)
1054        {
1055            _allowedAnonymousData = new HashMap<>();
1056            _deniedAnonymousData = new HashMap<>();
1057            _allowedAnyConnectedData = new HashMap<>();
1058            _deniedAnyConnectedData = new HashMap<>();
1059            _allowedUserData = new HashMap<>();
1060            _deniedUserData = new HashMap<>();
1061            _allowedGroupData = new HashMap<>();
1062            _deniedGroupData = new HashMap<>();
1063            
1064            for (Map<String, String> map : data)
1065            {
1066                String type = map.get("type").trim();
1067                String profileId = map.get("profileId");
1068                String context = map.get("context");
1069
1070                if ("ALLOWED_ANONYMOUS".equals(type) || "DENIED_ANONYMOUS".equals(type) || "ALLOWED_ANYCONNECTED".equals(type) || "DENIED_ANYCONNECTED".equals(type))
1071                {
1072                    _buildAnonymousOrAnyConnectedData(type, profileId, context);
1073                }
1074                else if ("ALLOWED_USER".equals(type) || "DENIED_USER".equals(type))
1075                {
1076                    String targetId = map.get("targetId");
1077                    String targetGroup = map.get("targetGroup");
1078                    UserIdentity user = new UserIdentity(targetId, targetGroup);
1079
1080                    _buildUserData(type, profileId, context, user);
1081                }
1082                else // if ("ALLOWED_GROUP".equals(type) || "DENIED_GROUP".equals(type))
1083                {
1084                    String targetId = map.get("targetId");
1085                    String targetGroup = map.get("targetGroup");
1086                    GroupIdentity group = new GroupIdentity(targetId, targetGroup);
1087                    
1088                    _buildGroupData(type, profileId, context, group);
1089                }
1090            }
1091        }
1092
1093        private void _buildGroupData(String type, String profileId, String context, GroupIdentity group)
1094        {
1095            Map<String, Map<GroupIdentity, Set<String>>> wData;
1096            if ("ALLOWED_GROUP".equals(type))
1097            {
1098                wData = _allowedGroupData;
1099            }
1100            else // if ("DENIED_GROUP".equals(type))
1101            {
1102                wData = _deniedGroupData;
1103            } 
1104
1105            if (!wData.containsKey(context))
1106            {
1107                wData.put(context, new HashMap<>());
1108            }
1109            Map<GroupIdentity, Set<String>> contextMap = wData.get(context);
1110            
1111            if (!contextMap.containsKey(group))
1112            {
1113                contextMap.put(group, new HashSet<>());
1114            }
1115            Set<String> profileSet = contextMap.get(group);
1116            
1117            profileSet.add(profileId);
1118        }
1119
1120        private void _buildUserData(String type, String profileId, String context, UserIdentity user)
1121        {
1122            Map<String, Map<UserIdentity, Set<String>>> wData;
1123            if ("ALLOWED_USER".equals(type))
1124            {
1125                wData = _allowedUserData;
1126            }
1127            else // if ("DENIED_USER".equals(type))
1128            {
1129                wData = _deniedUserData;
1130            } 
1131            
1132            if (!wData.containsKey(context))
1133            {
1134                wData.put(context, new HashMap<>());
1135            }
1136            Map<UserIdentity, Set<String>> contextMap = wData.get(context);
1137            
1138            if (!contextMap.containsKey(user))
1139            {
1140                contextMap.put(user, new HashSet<>());
1141            }
1142            Set<String> profileSet = contextMap.get(user);
1143            
1144            profileSet.add(profileId);
1145        }
1146
1147        private void _buildAnonymousOrAnyConnectedData(String type, String profileId, String context)
1148        {
1149            Map<String, Set<String>> wData;
1150            if ("ALLOWED_ANONYMOUS".equals(type))
1151            {
1152                wData = _allowedAnonymousData;
1153            }
1154            else if ("DENIED_ANONYMOUS".equals(type))
1155            {
1156                wData = _deniedAnonymousData;
1157            } 
1158            else if ("ALLOWED_ANYCONNECTED".equals(type))
1159            {
1160                wData = _allowedAnyConnectedData;
1161            } 
1162            else // if ("DENIED_ANYCONNECTED".equals(type)) 
1163            {
1164                wData = _deniedAnyConnectedData;
1165            } 
1166
1167            if (!wData.containsKey(context))
1168            {
1169                wData.put(context, new HashSet<>());
1170            }
1171            Set<String> profileSet = wData.get(context);
1172            
1173            profileSet.add(profileId);
1174        }
1175
1176        public Map<String, Set<String>> getAllowedAnonymous()
1177        {
1178            return _allowedAnonymousData;
1179        }
1180        public Map<String, Set<String>> getDeniedAnonymous()
1181        {
1182            return _deniedAnonymousData;
1183        }
1184        public Map<String, Set<String>> getAllowedAnyConnected()
1185        {
1186            return _allowedAnyConnectedData;
1187        }
1188        public Map<String, Set<String>> getDeniedAnyConnected()
1189        {
1190            return _deniedAnyConnectedData;
1191        }
1192        public Map<String, Map<UserIdentity, Set<String>>> getAlloweUserData()
1193        {
1194            return _allowedUserData;
1195        }
1196        public Map<String, Map<UserIdentity, Set<String>>> getDeniedUserData()
1197        {
1198            return _deniedUserData;
1199        }
1200        public Map<String, Map<GroupIdentity, Set<String>>> getAlloweGroupData()
1201        {
1202            return _allowedGroupData;
1203        }
1204        public Map<String, Map<GroupIdentity, Set<String>>> getDeniedGroupData()
1205        {
1206            return _deniedGroupData;
1207        }
1208    }
1209
1210    private Cache<String, Database> _getStorageCache()
1211    {
1212        return _cacheManager.get(__JDBC_PROFILE_ASSIGNMENT_STORAGE_CACHE);
1213    }
1214    
1215    private Cache<String, Boolean> _getInheritanceCache()
1216    {
1217        return _cacheManager.get(__JDBC_PROFILE_ASSIGNMENT_INHERITANCE_CACHE);
1218    }
1219}