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