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