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