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