001/*
002 *  Copyright 2016 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.core.right;
017
018import java.util.Collections;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.commons.lang3.StringUtils;
028import org.apache.ibatis.session.SqlSession;
029
030import org.ametys.core.ObservationConstants;
031import org.ametys.core.datasource.AbstractMyBatisDAO;
032import org.ametys.core.observation.Event;
033import org.ametys.core.observation.ObservationManager;
034import org.ametys.core.user.CurrentUserProvider;
035
036/**
037 * Manages registration of profiles
038 */
039public class RightProfilesDAO extends AbstractMyBatisDAO
040{
041    /** The component role. */
042    public static final String ROLE = RightProfilesDAO.class.getName();
043    
044    private final String _cache = this.getClass().getName() + "$Cache";
045
046    private ServiceManager _smanager;
047
048    private ObservationManager _observationManager;
049
050    private CurrentUserProvider _currentUserProvider;
051    
052    private RightManager _rightManager;
053    
054    @Override
055    public void service(ServiceManager smanager) throws ServiceException
056    {
057        _smanager = smanager;
058        super.service(smanager);
059    }
060    
061    private RightManager _getRightManager()
062    {
063        if (_rightManager == null)
064        {
065            try
066            {
067                _rightManager = (RightManager) _smanager.lookup(RightManager.ROLE);
068            }
069            catch (ServiceException e)
070            {
071                throw new IllegalStateException("Cannot get RightManager for RightProfilesDAO", e);
072            }
073        }
074        return _rightManager;
075    }
076    
077    /**
078     * Get all existing profiles
079     * @return The list for profiles
080     */
081    public List<Profile> getProfiles()
082    {
083        try (SqlSession session = getSession())
084        {
085            return session.selectList("Profiles.getProfiles");
086        }
087    }
088    
089    /**
090     * Get the profiles on a given context
091     * @param context The context. Can be null. If null, the profiles with no context are returned.
092     * @return The list for profiles for this context
093     */
094    public List<Profile> getProfiles(String context)
095    {
096        try (SqlSession session = getSession())
097        {
098            if (context == null)
099            {
100                return session.selectList("Profiles.getProfilesWithNullContext");
101            }
102            else
103            {
104                return session.selectList("Profiles.getProfilesByContext");
105            }
106        }
107    }
108    
109    /**
110     * Get the profile with given identifier
111     * @param id The id of profile to retrieve
112     * @return The profile
113     */
114    public Profile getProfile(String id)
115    {
116        try (SqlSession session = getSession())
117        {
118            return session.selectOne("Profiles.getProfile", id);
119        }
120    }
121    
122    /**
123     * Get all profiles containing the right wth given id
124     * @param rightId The id of right
125     * @return The id of profiles with this right
126     */
127    public Set<String> getProfilesWithRight (String rightId)
128    {
129        Map<String, Set<String>> cache = _getRightManager().getCache(_cache, false);
130        if (cache == null)
131        {
132            // Build the cache with only one SQL query
133            cache = _getRightManager().getCache(_cache, true);
134            if (cache == null)
135            {
136                // When not in a request, cache cannot exists
137                cache = new HashMap<>();
138            }
139            
140            try (SqlSession session = getSession())
141            {
142                List<Map<String, String>> profileRights = session.selectList("Profiles.getProfileRights");
143                
144                for (Map<String, String> profileRight : profileRights)
145                {
146                    String currentProfileId = profileRight.get("profileId");
147                    String currentRightId = profileRight.get("rightId");
148                    
149                    if (cache.containsKey(currentRightId))
150                    {
151                        cache.get(currentRightId).add(currentProfileId);
152                    }
153                    else
154                    {
155                        Set<String> profiles = new HashSet<>();
156                        profiles.add(currentProfileId);
157                        cache.put(currentRightId, profiles);
158                    }
159                }
160            }
161        }
162        
163        if (cache.containsKey(rightId))
164        {
165            return cache.get(rightId);
166        }
167        else
168        {
169            return Collections.EMPTY_SET;
170        }
171    }
172    
173    /**
174     * Creates a new profile with null context. The identifier of the profile will be automatically generated from label.
175     * @param label The label of profile
176     * @return The create profile
177     */
178    public Profile addProfile (String label)
179    {
180        return addProfile(label, null);
181    }
182    
183    /**
184     * Creates a new profile. The identifier of the profile will be automatically generated from label.
185     * @param label The label of profile
186     * @param context The context. Can be null
187     * @return The create profile
188     */
189    public Profile addProfile (String label, String context)
190    {
191        String id = _generateUniqueId(label);
192        Profile profile = new Profile(id, label, context);
193        addProfile(profile);
194        return profile;
195    }
196    
197    private String _generateUniqueId(String label)
198    {
199        // Id generated from name lowercased, trimmed, and spaces and underscores replaced by dashes
200        String value = label.toLowerCase().trim().replaceAll("[\\W_]", "-").replaceAll("-+", "-").replaceAll("^-", "");
201        int i = 2;
202        String suffixedValue = value;
203        while (getProfile(suffixedValue) != null)
204        {
205            suffixedValue = value + i;
206            i++;
207        }
208        
209        return suffixedValue;
210    }
211    
212    /**
213     * Creates a new profile
214     * @param id The unique identifier of profile
215     * @param label The label of profile
216     * @param context The context. Can be null
217     * @return The create profile
218     */
219    public Profile addProfile (String id, String label, String context)
220    {
221        Profile profile = new Profile(id, label, context);
222        addProfile(profile);
223        return profile;
224    }
225    
226    /**
227     * Add a new profile
228     * @param profile The profile to add
229     * @param silent Set to true to not notify observer of this update
230     */
231    public void addProfile (Profile profile, boolean silent)
232    {
233        try (SqlSession session = getSession(true))
234        {
235            session.insert("Profiles.addProfile", profile);
236            
237            if (!silent)
238            {
239                _notifyEvent(profile, ObservationConstants.EVENT_PROFILE_ADDED);
240            }
241        }
242    }
243    
244    /**
245     * Add a new profile
246     * @param profile The profile to add
247     */
248    public void addProfile (Profile profile)
249    {
250        addProfile(profile, false);
251    }
252    
253    /**
254     * Rename a profile
255     * @param profile The profile to rename
256     * @param newLabel The updated label
257     */
258    public void renameProfile (Profile profile, String newLabel)
259    {
260        renameProfile(profile, newLabel, false);
261    }
262    
263    /**
264     * Rename a profile
265     * @param profile The profile to rename
266     * @param newLabel The updated label
267     * @param silent Set to true to not notify observer of this update
268     */
269    public void renameProfile (Profile profile, String newLabel, boolean silent)
270    {
271        try (SqlSession session = getSession(true))
272        {
273            Map<String, Object> params = new HashMap<>();
274            params.put("id", profile.getId());
275            params.put("label", newLabel);
276            session.update("Profiles.renameProfile", params);
277            
278            if (!silent)
279            {
280                _notifyEvent(profile, ObservationConstants.EVENT_PROFILE_UPDATED);
281            }
282        }
283    }
284
285    
286    /**
287     * Get the rights of a profile
288     * @param profileId The profile id
289     * @return The rights
290     */
291    public List<String> getRights (String profileId)
292    {
293        if (StringUtils.isEmpty(profileId))
294        {
295            return Collections.EMPTY_LIST;
296        }
297        else
298        {
299            try (SqlSession session = getSession())
300            {
301                return session.selectList("Profiles.getRights", profileId);
302            }
303        }
304    }
305
306    /**
307     * Get the rights of a profile
308     * @param profile The profile
309     * @return The rights
310     */
311    public List<String> getRights (Profile profile)
312    {
313        if (profile == null)
314        {
315            return Collections.EMPTY_LIST;
316        }
317        else
318        {
319            return getRights(profile.getId());
320        }
321    }
322    
323    /**
324     * Add a right to a profile
325     * @param profile The profile
326     * @param rightId The id of right to add
327     */
328    public void addRight (Profile profile, String rightId)
329    {
330        try (SqlSession session = getSession(true))
331        {
332            _addRight (session, profile, rightId);
333        }
334    }
335    
336    /**
337     * Add a right to a profile
338     * @param profile The profile
339     * @param rightIds The id of rights to add
340     */
341    public void addRights (Profile profile, List<String> rightIds)
342    {
343        try (SqlSession session = getSession())
344        {
345            for (String rightId : rightIds)
346            {
347                _addRight (session, profile, rightId);
348            }
349            
350            session.commit();
351        }
352    }
353    
354    /**
355     * Update the rights of a profile
356     * @param profile The profile
357     * @param rights The rights of the profile
358     */
359    public void updateRights (Profile profile, List<String> rights)
360    {
361        updateRights(profile, rights, false);
362    }
363    
364    /**
365     * Update the rights of a profile
366     * @param profile The profile
367     * @param rights The rights of the profile
368     * @param silent Set to true to not notify observer of this update
369     */
370    public void updateRights (Profile profile, List<String> rights, boolean silent)
371    {
372        try (SqlSession session = getSession())
373        {
374            session.delete("Profiles.deleteProfileRights", profile.getId());
375            
376            if (rights != null)
377            {
378                for (String rightId : rights)
379                {
380                    _addRight (session, profile, rightId);
381                }
382            }
383            
384            session.commit();
385            
386            if (!silent)
387            {
388                _notifyEvent(profile, ObservationConstants.EVENT_PROFILE_UPDATED);
389            }
390        }
391    }
392    
393    private void _addRight (SqlSession session, Profile profile, String rightId)
394    {
395        Map<String, Object> params = new HashMap<>();
396        params.put("profileId", profile.getId());
397        params.put("rightId", rightId);
398        
399        session.insert("Profiles.addRight", params);
400    }
401    
402    /**
403     * Add a right to a profile
404     * @param profile The profile
405     */
406    public void removeRights (Profile profile)
407    {
408        removeRights(profile, false);
409    }
410    
411    /**
412     * Add a right to a profile
413     * @param profile The profile
414     * @param silent Set to true to not notify observer of this update
415     */
416    public void removeRights (Profile profile, boolean silent)
417    {
418        try (SqlSession session = getSession(true))
419        {
420            session.delete("Profiles.deleteProfileRights", profile.getId());
421            
422            if (!silent)
423            {
424                _notifyEvent(profile, ObservationConstants.EVENT_PROFILE_UPDATED);
425            }
426        }
427    }
428    
429    /**
430     * Delete a profile
431     * @param profile The profile to delete
432     */
433    public void deleteProfile (Profile profile)
434    {
435        deleteProfile(profile, false);
436    }
437    
438    /**
439     * Delete a profile
440     * @param profile The profile to delete
441     * @param silent Set to true to not notify observer of this update
442     */
443    public void deleteProfile (Profile profile, boolean silent)
444    {
445        try (SqlSession session = getSession())
446        {
447            session.delete("Profiles.deleteProfile", profile.getId());
448            session.delete("Profiles.deleteProfileRights", profile.getId());
449            
450            session.commit();
451            
452            if (!silent)
453            {
454                _notifyEvent(profile, ObservationConstants.EVENT_PROFILE_DELETED);
455            }
456        }
457    }
458    
459    private void _notifyEvent (Profile profile, String eventId)
460    {
461        try
462        {
463            if (_observationManager == null)
464            {
465                _observationManager = (ObservationManager) _smanager.lookup(ObservationManager.ROLE);
466            }
467            if (_currentUserProvider == null)
468            {
469                _currentUserProvider = (CurrentUserProvider) _smanager.lookup(CurrentUserProvider.ROLE);
470            }
471            
472            Map<String, Object> eventParams = new HashMap<>();
473            eventParams.put(ObservationConstants.ARGS_PROFILE, profile);
474            _observationManager.notify(new Event(eventId, _currentUserProvider.getUser(), eventParams));
475        }
476        catch (ServiceException e)
477        {
478            getLogger().error("Fail to notify observers for event '" + eventId + "'", e);
479        }
480    }
481}