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