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.user; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.Comparator; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.activity.Initializable; 029import org.apache.avalon.framework.component.Component; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.commons.lang3.tuple.Pair; 034 035import org.ametys.core.cache.AbstractCacheManager; 036import org.ametys.core.cache.Cache; 037import org.ametys.core.cache.CacheException; 038import org.ametys.core.user.directory.NotUniqueUserException; 039import org.ametys.core.user.directory.StoredUser; 040import org.ametys.core.user.directory.UserDirectory; 041import org.ametys.core.user.population.PopulationContextHelper; 042import org.ametys.core.user.population.UserPopulation; 043import org.ametys.core.user.population.UserPopulationDAO; 044import org.ametys.core.util.LambdaUtils; 045import org.ametys.core.util.LambdaUtils.LambdaException; 046import org.ametys.plugins.core.user.UserHelper; 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.runtime.plugin.component.AbstractLogEnabled; 049 050/** 051 * Component for getting user list and verify the presence of a particular user on a context or for user directory(ies). 052 */ 053public class UserManager extends AbstractLogEnabled implements Component, Serviceable, Initializable 054{ 055 /** Avalon Role */ 056 public static final String ROLE = UserManager.class.getName(); 057 058 private static final String __USER_CACHE_ID = UserHelper.class.getName() + "$userCache"; 059 060 private static final String __USER_CACHE_BY_EMAIL_ID = UserHelper.class.getName() + "$userByEmailCache"; 061 062 /** The DAO for User Population */ 063 protected UserPopulationDAO _userPopulationDAO; 064 /** The helper for the associations population/context */ 065 protected PopulationContextHelper _populationContextHelper; 066 067 private AbstractCacheManager _abstractCacheManager; 068 /** The user factory */ 069 private UserFactory _userFactory; 070 071 @Override 072 public void service(ServiceManager manager) throws ServiceException 073 { 074 _userPopulationDAO = (UserPopulationDAO) manager.lookup(UserPopulationDAO.ROLE); 075 _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE); 076 _abstractCacheManager = (AbstractCacheManager) manager.lookup(AbstractCacheManager.ROLE); 077 _userFactory = (UserFactory) manager.lookup(UserFactory.ROLE); 078 } 079 080 @Override 081 public void initialize() throws Exception 082 { 083 _abstractCacheManager.createRequestCache(__USER_CACHE_ID, 084 new I18nizableText("plugin.core", "PLUGINS_CORE_CACHE_USER_BY_USER_IDENTITY_LABEL"), 085 new I18nizableText("plugin.core", "PLUGINS_CORE_CACHE_USER_BY_USER_IDENTITY_DESCRIPTION"), 086 true); 087 _abstractCacheManager.createRequestCache(__USER_CACHE_BY_EMAIL_ID, 088 new I18nizableText("plugin.core", "PLUGINS_CORE_CACHE_USER_BY_EMAIL_LABEL"), 089 new I18nizableText("plugin.core", "PLUGINS_CORE_CACHE_USER_BY_EMAIL_DESCRIPTION"), 090 true); 091 } 092 093 /** 094 * Get the list of users on some given contexts 095 * @param contexts The contexts 096 * @param checkRight True to check that current user belongs to one of populations on theses contexts or he's an administrator user 097 * @return the collection of users 098 */ 099 public Collection<User> getUsersByContext(Set<String> contexts, boolean checkRight) 100 { 101 List<UserPopulation> userPopulations = _populationContextHelper.getUserPopulationsOnContexts(contexts, false, checkRight).stream() 102 .map(upId -> _userPopulationDAO.getUserPopulation(upId)) 103 .collect(Collectors.toList()); 104 105 return getUsersByPopulations(userPopulations); 106 } 107 108 /** 109 * Get the users for given users' populations 110 * @param userPopulationIds the id of population of users 111 * @return the collection of users 112 */ 113 public Collection<User> getUsersByPopulationIds(List<String> userPopulationIds) 114 { 115 List<User> users = new ArrayList<>(); 116 for (String id : userPopulationIds) 117 { 118 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(id); 119 if (userPopulation != null) 120 { 121 for (User user : getUsers(userPopulation)) 122 { 123 if (!users.contains(user)) 124 { 125 users.add(user); 126 } 127 } 128 } 129 } 130 return users; 131 } 132 133 /** 134 * Get the users for given users' populations 135 * @param userPopulations the population of users 136 * @return the collection of users 137 */ 138 public Collection<User> getUsersByPopulations(List<UserPopulation> userPopulations) 139 { 140 List<User> users = new ArrayList<>(); 141 for (UserPopulation userPopulation : userPopulations) 142 { 143 for (User user : getUsers(userPopulation)) 144 { 145 if (!users.contains(user)) 146 { 147 users.add(user); 148 } 149 } 150 } 151 return users; 152 } 153 154 /** 155 * Gets all the users of a {@link UserPopulation} 156 * @param userPopulationId The ID of user population 157 * @return list of users as Collection of {@link User}s, empty if a problem occurs. 158 */ 159 public Collection<User> getUsers(String userPopulationId) 160 { 161 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(userPopulationId); 162 if (userPopulation != null) 163 { 164 return getUsers(userPopulation); 165 } 166 else 167 { 168 return Collections.EMPTY_LIST; 169 } 170 } 171 172 /** 173 * Gets all the users of a {@link UserPopulation} 174 * @param userPopulation The user population 175 * @return list of users as Collection of {@link User}s, empty if a problem occurs. 176 */ 177 public Collection<User> getUsers(UserPopulation userPopulation) 178 { 179 List<User> users = new ArrayList<>(); 180 181 for (UserDirectory ud : userPopulation.getUserDirectories()) 182 { 183 Collection<User> usersOfUd = _storedUsers2Users(ud.getStoredUsers(), ud); 184 for (User user : usersOfUd) 185 { 186 if (!users.contains(user)) 187 { 188 users.add(user); 189 } 190 } 191 } 192 193 return users; 194 } 195 196 /** 197 * Get a list of users given the parameters 198 * @param contexts The contexts 199 * @param count The limit of users to retrieve 200 * @param offset The number of result to ignore before starting to collect users. 201 * @param parameters A map of additional parameters, see implementation. 202 * @param checkRight True to check that current user belongs to one of populations on theses contexts or he's an administrator user 203 * @param sort true to sort users 204 * @return The list of retrieved {@link User} 205 */ 206 public List<User> getUsersByContext(Set<String> contexts, int count, int offset, Map<String, Object> parameters, boolean checkRight, boolean sort) 207 { 208 List<UserPopulation> userPopulations = _populationContextHelper.getUserPopulationsOnContexts(contexts, false, checkRight).stream() 209 .map(upId -> _userPopulationDAO.getUserPopulation(upId)) 210 .collect(Collectors.toList()); 211 212 return getUsers(userPopulations, count, offset, parameters, sort); 213 } 214 215 /** 216 * Get a list of users given the parameters 217 * @param userPopulations the population of users 218 * @param count The limit of users to retrieve 219 * @param offset The number of result to ignore before starting to collect users. 220 * @param parameters A map of additional parameters, see implementation. 221 * @param sort true to sort users 222 * @return The list of retrieved {@link User} 223 */ 224 public List<User> getUsers(List<UserPopulation> userPopulations, int count, int offset, Map<String, Object> parameters, boolean sort) 225 { 226 List<User> users = new ArrayList<>(); 227 for (UserPopulation userPopulation : userPopulations) 228 { 229 for (User user : getUsers(userPopulation, count + offset, 0, parameters, sort)) 230 { 231 if (!users.contains(user)) 232 { 233 users.add(user); 234 } 235 } 236 } 237 238 int boundedCount = count >= 0 ? count : Integer.MAX_VALUE; 239 int boundedOffset = offset >= 0 ? offset : 0; 240 int toIndex; 241 if (boundedOffset + boundedCount >= 0) 242 { 243 toIndex = Math.min(boundedOffset + boundedCount, users.size()); 244 } 245 else 246 { 247 // particular case where count was initially negative (to say "no limit") and we set it to Integer.MAX_VALUE 248 // so if the offset is strictly positive, the sum overflows 249 toIndex = users.size(); 250 } 251 return users.subList(boundedOffset, toIndex); 252 } 253 254 /** 255 * Gets all the users of a {@link UserPopulation} 256 * @param userPopulationId The ID of user population 257 * @param count The limit of users to retrieve 258 * @param offset The number of result to ignore before starting to collect users. 259 * @param parameters A map of additional parameters, see implementation. 260 * @param sort true to sort users 261 * @return list of users as Collection of {@link User}s, empty if a problem occurs. 262 */ 263 public Collection<User> getUsers(String userPopulationId, int count, int offset, Map<String, Object> parameters, boolean sort) 264 { 265 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(userPopulationId); 266 if (userPopulation != null) 267 { 268 return getUsers(userPopulation, count, offset, parameters, sort); 269 } 270 else 271 { 272 return Collections.EMPTY_LIST; 273 } 274 } 275 276 /** 277 * Gets all the users of a given {@link UserPopulation} and {@link UserDirectory} 278 * @param userPopulationId The ID of user population 279 * @param userDirectoryId The id of the user directory 280 * @param count The limit of users to retrieve 281 * @param offset The number of result to ignore before starting to collect users. 282 * @param parameters A map of additional parameters, see implementation. 283 * @return list of users as Collection of {@link User}s, empty if a problem occurs. 284 */ 285 public Collection<User> getUsersByDirectory(String userPopulationId, String userDirectoryId, int count, int offset, Map<String, Object> parameters) 286 { 287 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(userPopulationId); 288 289 if (userPopulation == null) 290 { 291 return Collections.EMPTY_LIST; 292 } 293 294 UserDirectory ud = userPopulation.getUserDirectory(userDirectoryId); 295 if (ud == null) 296 { 297 throw new IllegalArgumentException("In the population '" + userPopulationId + "' the directory '" + userDirectoryId + "' was referenced but does not exists"); 298 } 299 return _storedUsers2Users(ud.getStoredUsers(count, offset, parameters), ud); 300 } 301 302 /** 303 * Gets all the users of a {@link UserPopulation} 304 * @param userPopulation The users population 305 * @param count The limit of users to retrieve 306 * @param offset The number of result to ignore before starting to collect users. 307 * @param parameters A map of additional parameters, see implementation. 308 * @param sort true to sort users 309 * @return list of users as Collection of {@link User}s, empty if a problem occurs. 310 */ 311 public Collection<User> getUsers(UserPopulation userPopulation, int count, int offset, Map<String, Object> parameters, boolean sort) 312 { 313 Set<User> users = new HashSet<>(); 314 315 for (UserDirectory ud : userPopulation.getUserDirectories()) 316 { 317 users.addAll(_storedUsers2Users(ud.getStoredUsers(count + offset, 0, parameters), ud)); 318 } 319 320 int boundedCount = count >= 0 ? count : Integer.MAX_VALUE; 321 int boundedOffset = offset >= 0 ? offset : 0; 322 int toIndex; 323 if (boundedOffset + boundedCount >= 0) 324 { 325 toIndex = Math.min(boundedOffset + boundedCount, users.size()); 326 } 327 else 328 { 329 // particular case where count was initially negative (to say "no limit") and we set it to Integer.MAX_VALUE 330 // so if the offset is strictly positive, the sum overflows 331 toIndex = users.size(); 332 } 333 334 335 // Use isTrue as sort parameter can be null 336 List<User> usersList = new ArrayList<>(users); 337 if (sort) 338 { 339 usersList.sort(Comparator.comparing(UserManager::_getSortableNameLowerCase)); 340 } 341 342 return usersList.subList(boundedOffset, toIndex); 343 } 344 345 private static String _getSortableNameLowerCase(User user) 346 { 347 return user.getSortableName().toLowerCase(); 348 } 349 350 /** 351 * Gets all the users of a {@link UserPopulation} 352 * @param userPopulation The users population 353 * @param userDirectoryId The id of the user directory 354 * @param count The limit of users to retrieve 355 * @param offset The number of result to ignore before starting to collect users. 356 * @param parameters A map of additional parameters, see implementation. 357 * @return list of users as Collection of {@link User}s, empty if a problem occurs. 358 */ 359 public Collection<User> getUsersByDirectory(UserPopulation userPopulation, String userDirectoryId, int count, int offset, Map<String, Object> parameters) 360 { 361 UserDirectory ud = userPopulation.getUserDirectory(userDirectoryId); 362 return _storedUsers2Users(ud.getStoredUsers(count, offset, parameters), ud); 363 } 364 365 /** 366 * Get a user by his login on some given contexts 367 * @param contexts The contexts 368 * @param login Login of the user to get. Cannot be null. 369 * @param checkRight True to check that current user is authorized to retrieve this user (true if he belongs to one of populations on theses contexts or he's an administrator user) 370 * @return User's information as a {@link User} instance or null if the user login does not exist. 371 */ 372 public User getUserByContext(Set<String> contexts, String login, boolean checkRight) 373 { 374 Set<String> upIds = _populationContextHelper.getUserPopulationsOnContexts(contexts, false, checkRight); 375 for (String upId : upIds) 376 { 377 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(upId); 378 User user = getUser(userPopulation, login); 379 if (user != null) 380 { 381 return user; 382 } 383 } 384 return null; 385 } 386 387 /** 388 * Get the user from its user identity 389 * @param userIdentity The user identity 390 * @return The User or null if the user login does not exist. 391 */ 392 public User getUser (UserIdentity userIdentity) 393 { 394 if (userIdentity == null) 395 { 396 return null; 397 } 398 399 Cache<UserIdentity, User> cache = _getUserCache(); 400 return cache.get(userIdentity, key -> 401 { 402 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(key.getPopulationId()); 403 if (userPopulation != null) 404 { 405 return getUser(userPopulation, key.getLogin()); 406 } 407 else 408 { 409 return null; 410 } 411 }); 412 } 413 414 /** 415 * Get a particular user of the given users population by his login. 416 * @param userPopulationId The ID of user population 417 * @param login Login of the user to get. Cannot be null. 418 * @return User's information as a {@link User} instance or null if the user login does not exist. 419 */ 420 public User getUser(String userPopulationId, String login) 421 { 422 UserIdentity userIdentity = new UserIdentity(login, userPopulationId); 423 return getUser(userIdentity); 424 } 425 426 private Cache<UserIdentity, User> _getUserCache() 427 { 428 return _abstractCacheManager.get(__USER_CACHE_ID); 429 } 430 431 /** 432 * Get a particular user of the given users population by his email. 433 * @param userPopulationIds The IDs of user population 434 * @param email Email of the user to get. Cannot be null. 435 * @return User's information as a {@link User} instance or null if the user login does not exist. 436 * @throws NotUniqueUserException if many users match the given email 437 */ 438 public User getUserByEmail(Set<String> userPopulationIds, String email) throws NotUniqueUserException 439 { 440 if (email == null) 441 { 442 return null; 443 } 444 445 User user = null; 446 for (String populationId : userPopulationIds) 447 { 448 User u = getUserByEmail(populationId, email); 449 if (u != null) 450 { 451 if (user == null) 452 { 453 user = u; 454 } 455 else 456 { 457 throw new NotUniqueUserException("Cannot find user in populations '" + userPopulationIds + "' by email '" + email + "' because 2 or more users match (at least " + UserIdentity.userIdentityToString(u.getIdentity()) + " and " + UserIdentity.userIdentityToString(user.getIdentity()) + ")"); 458 } 459 } 460 } 461 462 return user; 463 } 464 465 /** 466 * Get a particular user of the given users population by his email. 467 * @param userPopulationId The ID of user population 468 * @param email Email of the user to get. Cannot be null. 469 * @return User's information as a {@link User} instance or null if the user login does not exist. 470 * @throws NotUniqueUserException if many users match the given email 471 */ 472 public User getUserByEmail(String userPopulationId, String email) throws NotUniqueUserException 473 { 474 if (email == null) 475 { 476 return null; 477 } 478 479 Cache<Pair<String, String>, User> cache = _getUserByEmailCache(); 480 try 481 { 482 return cache.get(Pair.of(userPopulationId, email), LambdaUtils.wrap(key -> 483 { 484 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(userPopulationId); 485 if (userPopulation != null) 486 { 487 return getUserByEmail(userPopulation, email); 488 } 489 else 490 { 491 return null; 492 } 493 })); 494 } 495 catch (CacheException e) 496 { 497 if (e.getCause() instanceof LambdaException) 498 { 499 if (e.getCause().getCause() instanceof NotUniqueUserException) 500 { 501 throw (NotUniqueUserException) e.getCause().getCause(); 502 } 503 else 504 { 505 throw new RuntimeException(e.getCause().getCause()); 506 } 507 } 508 throw new RuntimeException(e.getCause()); 509 } 510 } 511 512 private Cache<Pair<String, String>, User> _getUserByEmailCache() 513 { 514 return _abstractCacheManager.get(__USER_CACHE_BY_EMAIL_ID); 515 } 516 517 /** 518 * Get a particular user of the given user population and given user directory by his login. 519 * @param userPopulationId The ID of user population 520 * @param userDirectoryId The id of the user directory 521 * @param login Login of the user to get. Cannot be null. 522 * @return User's information as a {@link User} instance or null if the user login does not exist. 523 */ 524 public User getUserByDirectory(String userPopulationId, String userDirectoryId, String login) 525 { 526 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(userPopulationId); 527 if (userPopulation != null) 528 { 529 return getUserByDirectory(userPopulation, userDirectoryId, login); 530 } 531 else 532 { 533 return null; 534 } 535 } 536 537 /** 538 * Get a particular user of the given user population by his login. 539 * @param userPopulation The user population 540 * @param login Login of the user to get. Cannot be null. 541 * @return User's information as a {@link User} instance or null if the user login does not exist. 542 */ 543 public User getUser(UserPopulation userPopulation, String login) 544 { 545 for (UserDirectory ud : userPopulation.getUserDirectories()) 546 { 547 StoredUser storedUser = ud.getStoredUser(login); 548 if (storedUser != null) 549 { 550 return _storedUser2User(storedUser, ud); 551 } 552 } 553 return null; 554 } 555 556 /** 557 * Get a particular user of the given user population by his email. 558 * @param userPopulation The user population 559 * @param email Email of the user to get. Cannot be null. 560 * @return User's information as a {@link User} instance or null if the user login does not exist. 561 * @throws NotUniqueUserException if many users match the given email 562 */ 563 public User getUserByEmail(UserPopulation userPopulation, String email) throws NotUniqueUserException 564 { 565 for (UserDirectory ud : userPopulation.getUserDirectories()) 566 { 567 StoredUser storedUser = ud.getStoredUserByEmail(email); 568 if (storedUser != null) 569 { 570 return _storedUser2User(storedUser, ud); 571 } 572 } 573 return null; 574 } 575 576 /** 577 * Get a particular user of the given user directory by his email. 578 * @param userDirectory The user directory 579 * @param email Email of the user to get. Cannot be null. 580 * @return User's information as a {@link User} instance or null if the user login does not exist. 581 * @throws NotUniqueUserException if many users match the given email 582 */ 583 public User getUserByUserDirectoryAndEmail(UserDirectory userDirectory, String email) throws NotUniqueUserException 584 { 585 StoredUser storedUser = userDirectory.getStoredUserByEmail(email); 586 if (storedUser != null) 587 { 588 return _storedUser2User(storedUser, userDirectory); 589 } 590 591 return null; 592 } 593 594 /** 595 * Get a particular user of the given user population and given user directory by his login. 596 * @param userPopulation The user population 597 * @param userDirectoryId The id of the user directory 598 * @param login Login of the user to get. Cannot be null. 599 * @return User's information as a {@link User} instance or null if the user login does not exist. 600 */ 601 public User getUserByDirectory(UserPopulation userPopulation, String userDirectoryId, String login) 602 { 603 UserDirectory ud = userPopulation.getUserDirectory(userDirectoryId); 604 605 StoredUser storedUser = ud.getStoredUser(login); 606 if (storedUser != null) 607 { 608 return _storedUser2User(storedUser, ud); 609 } 610 611 return null; 612 } 613 614 /** 615 * Get a particular user of the given user directory by his login. 616 * @param userDirectory The user directory 617 * @param login Login of the user to get. Cannot be null. 618 * @return User's information as a {@link User} instance or null if the user login does not exist. 619 */ 620 public User getUserByDirectory(UserDirectory userDirectory, String login) 621 { 622 StoredUser storedUser = userDirectory.getStoredUser(login); 623 if (storedUser != null) 624 { 625 return _storedUser2User(storedUser, userDirectory); 626 } 627 628 return null; 629 } 630 631 /** 632 * Get the user directory the given user belongs to 633 * @param userPopulationId The id of the user population 634 * @param login Login of the user to get. Cannot be null. 635 * @return The user directory the user belongs to. 636 */ 637 public UserDirectory getUserDirectory(String userPopulationId, String login) 638 { 639 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(userPopulationId); 640 if (userPopulation == null) 641 { 642 return null; 643 } 644 645 for (UserDirectory ud : userPopulation.getUserDirectories()) 646 { 647 StoredUser storedUser = ud.getStoredUser(login); 648 if (storedUser != null) 649 { 650 return ud; 651 } 652 } 653 return null; 654 } 655 656 /** 657 * Convert a {@link StoredUser} to a {@link User} 658 * @param storedUser The stored user to convert 659 * @param userDirectory the user directory containing the StoredUser 660 * @return A {@link User} 661 */ 662 private User _storedUser2User(StoredUser storedUser, UserDirectory userDirectory) 663 { 664 return _userFactory.createUser(storedUser, userDirectory); 665 } 666 667 /** 668 * Convert a Collection of {@link StoredUser} from the same user directory to a Collection of {@link User} 669 * @param storedUsers The collection of stored user to convert 670 * @param userDirectory the user directory containing the stored users 671 * @return A {@link User} 672 */ 673 private Collection<User> _storedUsers2Users(Collection<StoredUser> storedUsers, UserDirectory userDirectory) 674 { 675 Collection<User> users = new ArrayList<>(); 676 for (StoredUser storedUser : storedUsers) 677 { 678 users.add(_storedUser2User(storedUser, userDirectory)); 679 } 680 681 return users; 682 } 683}