001/* 002 * Copyright 2017 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.authentication; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Enumeration; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Optional; 029import java.util.Set; 030import java.util.regex.Pattern; 031import java.util.stream.Collectors; 032 033import org.apache.avalon.framework.activity.Initializable; 034import org.apache.avalon.framework.parameters.Parameters; 035import org.apache.avalon.framework.service.ServiceException; 036import org.apache.avalon.framework.thread.ThreadSafe; 037import org.apache.cocoon.ProcessingException; 038import org.apache.cocoon.acting.ServiceableAction; 039import org.apache.cocoon.environment.ObjectModelHelper; 040import org.apache.cocoon.environment.Redirector; 041import org.apache.cocoon.environment.Request; 042import org.apache.cocoon.environment.Session; 043import org.apache.cocoon.environment.SourceResolver; 044import org.apache.commons.lang3.StringUtils; 045 046import org.ametys.core.ObservationConstants; 047import org.ametys.core.authentication.token.AuthenticationTokenManager; 048import org.ametys.core.observation.Event; 049import org.ametys.core.observation.ObservationManager; 050import org.ametys.core.trace.ForensicLogger; 051import org.ametys.core.user.CurrentUserProvider; 052import org.ametys.core.user.User; 053import org.ametys.core.user.UserIdentity; 054import org.ametys.core.user.UserManager; 055import org.ametys.core.user.directory.WeakPasswordException; 056import org.ametys.core.user.population.PopulationContextHelper; 057import org.ametys.core.user.population.UserPopulation; 058import org.ametys.core.user.population.UserPopulationDAO; 059import org.ametys.core.util.URIUtils; 060import org.ametys.plugins.core.impl.authentication.FormCredentialProvider; 061import org.ametys.plugins.core.user.UserDAO; 062import org.ametys.runtime.authentication.AccessDeniedException; 063import org.ametys.runtime.authentication.AuthorizationRequiredException; 064import org.ametys.runtime.maintenance.MaintenanceAction; 065import org.ametys.runtime.servlet.RuntimeServlet; 066import org.ametys.runtime.servlet.RuntimeServlet.RunMode; 067import org.ametys.runtime.workspace.WorkspaceMatcher; 068 069/** 070 * Cocoon action to perform authentication.<br> 071 * The {@link CredentialProvider} define the authentication method and retrieves {@link Credentials}.<br> 072 * Finally, the Users instance extract the Principal corresponding to the {@link Credentials}. 073 */ 074public class AuthenticateAction extends ServiceableAction implements ThreadSafe, Initializable 075{ 076 /** The request attribute to allow internal action from an internal request. */ 077 public static final String REQUEST_ATTRIBUTE_INTERNAL_ALLOWED = "Runtime:InternalAllowedRequest"; 078 079 /** The request attribute meaning that the request was not authenticated but granted */ 080 public static final String REQUEST_ATTRIBUTE_GRANTED = "Runtime:GrantedRequest"; 081 /** The request attribute name for transmitting the list of user populations */ 082 public static final String REQUEST_ATTRIBUTE_AVAILABLE_USER_POPULATIONS_LIST = "Runtime:UserPopulationsList"; 083 /** The request attribute name for transmitting the currently chosen user population */ 084 public static final String REQUEST_ATTRIBUTE_USER_POPULATION_ID = "Runtime:CurrentUserPopulationId"; 085 /** The request attribute name for transmitting the login page url */ 086 public static final String REQUEST_ATTRIBUTE_LOGIN_URL = "Runtime:RequestLoginURL"; 087 088 /** The session attribute name for storing the identity of the connected user */ 089 public static final String SESSION_USERIDENTITY = "Runtime:UserIdentity"; 090 091 /** Name of the user population HTML field */ 092 public static final String REQUEST_PARAMETER_POPULATION_NAME = "UserPopulation"; 093 /** Name of the credential provider index HTML field */ 094 public static final String REQUEST_PARAMETER_CREDENTIALPROVIDER_INDEX = "CredentialProviderIndex"; 095 096 /** The request attribute name for indicating that the authentication process has been made. */ 097 public static final String REQUEST_ATTRIBUTE_AUTHENTICATED = "Runtime:RequestAuthenticated"; 098 099 /** The request parameter holding the token */ 100 public static final String REQUEST_PARAMETER_TOKEN = "token"; 101 /** The request parameter holding the token context */ 102 public static final String REQUEST_PARAMETER_TOKEN_CONTEXT = "tokenContext"; 103 /** The header parameter that can be set to handle the token */ 104 public static final String HEADER_TOKEN = "X-Ametys-Token"; 105 106 /** The sitemap parameter holding the token */ 107 protected static final String PARAMETERS_PARAMETER_TOKEN = "token"; 108 /** The sitemap parameter holding the token context */ 109 protected static final String PARAMETERS_PARAMETER_TOKEN_CONTEXT = "tokenContext"; 110 /** The request attribute name for transmitting a boolean that tell if there is a list of credential provider to choose */ 111 protected static final String REQUEST_ATTRIBUTE_CREDENTIAL_PROVIDER_LIST = "Runtime:RequestListCredentialProvider"; 112 /** The request attribute name for transmitting the index in the list of chosen credential provider */ 113 protected static final String REQUEST_ATTRIBUTE_CREDENTIAL_PROVIDER_INDEX = "Runtime:RequestCredentialProviderIndex"; 114 /** The request attribute name to know if user population list should be proposed */ 115 protected static final String REQUEST_ATTRIBUTE_SHOULD_DISPLAY_USER_POPULATIONS_LIST = "Runtime:UserPopulationsListDisplay"; 116 /** The request attribute name for transmitting the potential list of user populations to the login screen . */ 117 protected static final String REQUEST_ATTRIBUTE_INVALID_POPULATION = "Runtime:RequestInvalidPopulation"; 118 /** The request attribute name for transmitting the list of contexts */ 119 protected static final String REQUEST_ATTRIBUTE_CONTEXTS = "Runtime:Contexts"; 120 121 /** The session attribute name for storing the credential provider index of the authentication (during connection process) */ 122 protected static final String SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX = "Runtime:ConnectingCredentialProviderIndex"; 123 /** The session attribute name for storing the last known credential provider index of the authentication (during connection process)*/ 124 protected static final String SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX_LASTBLOCKINGKNOWN = "Runtime:ConnectingCredentialProviderIndexLastKnown"; 125 /** The session attribute name for storing the credential provider mode of the authentication: non-blocking=>false, blocking=>true (during connection process) */ 126 protected static final String SESSION_CONNECTING_CREDENTIALPROVIDER_MODE = "Runtime:ConnectingCredentialProviderMode"; 127 /** The session attribute name for storing the id of the user population (during connection process) */ 128 protected static final String SESSION_CONNECTING_USERPOPULATION_ID = "Runtime:ConnectingUserPopulationId"; 129 130 /** The session attribute name for storing the credential provider of the authentication */ 131 protected static final String SESSION_CREDENTIALPROVIDER = "Runtime:CredentialProvider"; 132 /** The session attribute name for storing the credential provider mode of the authentication: non-blocking=>false, blocking=>true */ 133 protected static final String SESSION_CREDENTIALPROVIDER_MODE = "Runtime:CredentialProviderMode"; 134 135 /** The sitemap parameter to set the token mode of the action */ 136 protected static final String SITEMAP_PARAMETER_TOKEN_MODE = "token-mode"; 137 138 /** The DAO for user populations */ 139 protected UserPopulationDAO _userPopulationDAO; 140 /** The user manager */ 141 protected UserManager _userManager; 142 /** The helper for the associations population/context */ 143 protected PopulationContextHelper _populationContextHelper; 144 /** The current user provider */ 145 protected CurrentUserProvider _currentUserProvider; 146 147 /** url requires for authentication */ 148 protected Collection<Pattern> _acceptedUrlPatterns = Arrays.asList(new Pattern[]{Pattern.compile("^plugins/core/authenticate/[0-9]+$")}); 149 150 /** The authentication token manager */ 151 protected AuthenticationTokenManager _authenticateTokenManager; 152 /** The observation manager */ 153 protected ObservationManager _observationManager; 154 155 /** 156 * The token mode of this authentication action 157 */ 158 protected enum TOKEN_MODE 159 { 160 /** In this mode, only the token will be taken in account. If no token is found, authentication will not be considered done */ 161 TOKEN_ONLY, 162 /** In this mode, the token will be taken in account but if no token is found, user will be considered as anonymous and authentication will be considered done */ 163 ALLOW_ANONYMOUS, 164 /** In this default mode, the token will be taken in account, but if no token is found, the authentication process will continue */ 165 DEFAULT 166 } 167 168 @Override 169 public void initialize() throws Exception 170 { 171 _userPopulationDAO = (UserPopulationDAO) manager.lookup(UserPopulationDAO.ROLE); 172 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 173 _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE); 174 175 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 176 try 177 { 178 _authenticateTokenManager = (AuthenticationTokenManager) manager.lookup(AuthenticationTokenManager.ROLE); 179 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 180 } 181 catch (ServiceException e) 182 { 183 // nothing... we are in safe mode, but this is not safe 184 } 185 } 186 187 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 188 { 189 Request request = ObjectModelHelper.getRequest(objectModel); 190 191 if (_preFlightCheck(redirector, resolver, objectModel, source, parameters) || _handleAuthenticationToken(request, parameters)) 192 { 193 // We passed the authentication, let's mark it now 194 request.setAttribute(REQUEST_ATTRIBUTE_AUTHENTICATED, "true"); 195 196 // We passed the authentication (with a user) 197 return EMPTY_MAP; 198 } 199 200 // At this point, the user is still anonymous 201 202 // If only token are authorized for authentication, stop authentication process. There is no user authenticated here. 203 if (_getTokenMode(parameters) != TOKEN_MODE.DEFAULT) 204 { 205 if (_getTokenMode(parameters) == TOKEN_MODE.ALLOW_ANONYMOUS) 206 { 207 request.setAttribute(REQUEST_ATTRIBUTE_AUTHENTICATED, "true"); 208 } 209 return null; 210 } 211 212 // At this point, we already know that the entire process will be executed, whatever the outcome 213 // Set the flag, so that the authentication process won't repeat 214 request.setAttribute(REQUEST_ATTRIBUTE_AUTHENTICATED, "true"); 215 216 // Get population and if possible credential providers 217 List<UserPopulation> chosenUserPopulations = new ArrayList<>(); 218 List<CredentialProvider> credentialProviders = new ArrayList<>(); 219 if (!_prepareUserPopulationsAndCredentialProviders(request, parameters, redirector, chosenUserPopulations, credentialProviders)) 220 { 221 // Let's display the population screen 222 return EMPTY_MAP; 223 } 224 225 // Get the currently running credential provider 226 int runningCredentialProviderIndex = _getCurrentCredentialProviderIndex(request, credentialProviders); 227 request.setAttribute(REQUEST_ATTRIBUTE_CREDENTIAL_PROVIDER_INDEX, runningCredentialProviderIndex); 228 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_URL, getLoginURL(request)); 229 230 // Let's process non-blocking 231 if (!_isCurrentCredentialProviderInBlockingMode(request)) 232 { 233 // if there was no one running, let's start with the first one 234 runningCredentialProviderIndex = Math.max(0, runningCredentialProviderIndex); 235 236 for (; runningCredentialProviderIndex < credentialProviders.size(); runningCredentialProviderIndex++) 237 { 238 CredentialProvider runningCredentialProvider = credentialProviders.get(runningCredentialProviderIndex); 239 if (_process(request, false, runningCredentialProvider, runningCredentialProviderIndex, redirector, chosenUserPopulations)) 240 { 241 // Whatever the user was correctly authenticated or he just required a redirect: let's stop here for the moment 242 return EMPTY_MAP; 243 } 244 } 245 246 // No one matches 247 runningCredentialProviderIndex = -1; 248 } 249 250 _saveLastKnownBlockingCredentialProvider(request, runningCredentialProviderIndex); 251 252 // Let's process the current blocking one or the only existing one 253 if (_shouldRunFirstBlockingCredentialProvider(runningCredentialProviderIndex, credentialProviders, request, chosenUserPopulations)) 254 { 255 CredentialProvider runningCredentialProvider = runningCredentialProviderIndex == -1 ? _getFirstBlockingCredentialProvider(credentialProviders) : credentialProviders.get(runningCredentialProviderIndex); 256 if (_process(request, true, runningCredentialProvider, runningCredentialProviderIndex, redirector, chosenUserPopulations)) 257 { 258 // Whatever the user was correctly authenticated or he just required a redirect: let's stop here for the moment 259 return EMPTY_MAP; 260 } 261 262 throw new AuthorizationRequiredException(); 263 } 264 265 // At this step we have two kind off requests 266 // 1) A secondary request of a blocking cp (such as captcha image...) 267 Integer formerRunningCredentialProviderIndex = (Integer) request.getSession(true).getAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX_LASTBLOCKINGKNOWN); 268 if (formerRunningCredentialProviderIndex != null && credentialProviders.get(formerRunningCredentialProviderIndex).grantAnonymousRequest(true)) 269 { 270 // Anonymous request 271 request.setAttribute(REQUEST_ATTRIBUTE_GRANTED, true); 272 _saveConnectingStateToSession(request, -1, true); 273 return EMPTY_MAP; 274 } 275 276 // 2) Or a main stream request that should display the list of available blocking cp 277 return _displayBlockingList(redirector, request, credentialProviders); 278 } 279 280 /** 281 * Prepare authentication 282 * @param redirector The redirector 283 * @param resolver The source resolver 284 * @param objectModel The object model 285 * @param source The source 286 * @param parameters The action parameters 287 * @return <code>true</code> if a user was authenticated, <code>false</code> otherwise 288 * @throws Exception if failed to prepare the authentication 289 */ 290 protected boolean _preFlightCheck(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 291 { 292 Request request = ObjectModelHelper.getRequest(objectModel); 293 294 return _handleLogout(redirector, objectModel, source, parameters) // Test if user wants to logout 295 || _internalRequest(request) // Test if this request was already authenticated or it the request is marked as an internal one 296 || _acceptedUrl(request) // Test if the url is used for authentication 297 || _validateCurrentlyConnectedUser(request, redirector, parameters) // Test if the currently connected user is still valid 298 || redirector.hasRedirected(); 299 } 300 301 /** 302 * Authenticate a user using the token in request (if configured so) 303 * @param request The request 304 * @param parameters The action parameters 305 * @return true if the user was authenticated 306 */ 307 protected boolean _handleAuthenticationToken(Request request, Parameters parameters) 308 { 309 String token = request.getHeader(HEADER_TOKEN); 310 if (StringUtils.isBlank(token)) 311 { 312 token = parameters.getParameter(PARAMETERS_PARAMETER_TOKEN, _getTokenFromRequest(request)); 313 } 314 315 if (StringUtils.isNotBlank(token)) 316 { 317 String context = parameters.getParameter(PARAMETERS_PARAMETER_TOKEN_CONTEXT, null); 318 UserIdentity userIdentity = _validateToken(token, context); 319 if (userIdentity != null) 320 { 321 // Save user identity 322 _setUserIdentityInSession(request, userIdentity, new UserDAO.ImpersonateCredentialProvider(), true); 323 _validateCurrentlyConnectedUserIsInAuthorizedPopulation(userIdentity, request, parameters); 324 return true; 325 } 326 } 327 328 return false; 329 } 330 331 /** 332 * Get the token from the request 333 * @param request The request 334 * @return The token from the request or null 335 */ 336 protected String _getTokenFromRequest(Request request) 337 { 338 // FIXME RUNTIME-2501 check the parameter is provided in POST, e.g. by seeking if '?token=' and '&token=' are not used in request uri... 339 return request.getParameter(REQUEST_PARAMETER_TOKEN); 340 } 341 342 /** 343 * Validate the given token 344 * @param token The non empty token to validate 345 * @param context the context on which the token should be validated 346 * @return The corresponding user identity or null 347 */ 348 protected UserIdentity _validateToken(String token, String context) 349 { 350 return _authenticateTokenManager != null ? _authenticateTokenManager.validateToken(token, context) : null; 351 } 352 353 private TOKEN_MODE _getTokenMode(Parameters parameters) 354 { 355 return TOKEN_MODE.valueOf(parameters.getParameter(SITEMAP_PARAMETER_TOKEN_MODE, TOKEN_MODE.DEFAULT.toString()).toUpperCase()); 356 } 357 358 private void _saveLastKnownBlockingCredentialProvider(Request request, int runningCredentialProviderIndex) 359 { 360 if (runningCredentialProviderIndex != -1) 361 { 362 request.getSession(true).setAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX_LASTBLOCKINGKNOWN, runningCredentialProviderIndex); 363 } 364 } 365 366 private Map _displayBlockingList(Redirector redirector, Request request, List<CredentialProvider> credentialProviders) throws IOException, ProcessingException, AuthorizationRequiredException 367 { 368 if (credentialProviders.stream().filter(cp -> cp instanceof BlockingCredentialProvider).findFirst().isPresent()) 369 { 370 _saveConnectingStateToSession(request, -1, true); 371 redirector.redirect(false, getLoginURL(request)); 372 return EMPTY_MAP; 373 } 374 else 375 { 376 // No way to login 377 throw new AuthorizationRequiredException(); 378 } 379 } 380 381 @SuppressWarnings("unchecked") 382 private boolean _shouldRunFirstBlockingCredentialProvider(int runningCredentialProviderIndex, List<CredentialProvider> credentialProviders, Request request, List<UserPopulation> chosenUserPopulations) 383 { 384 return runningCredentialProviderIndex >= 0 // There is a running credential provider 385 || credentialProviders.stream().filter(cp -> cp instanceof BlockingCredentialProvider).count() == 1 // There is a single blocking credential provider AND 386 && ( 387 ((List<UserPopulation>) request.getAttribute(REQUEST_ATTRIBUTE_AVAILABLE_USER_POPULATIONS_LIST)).size() == chosenUserPopulations.size() // no population choice screen 388 || _getFirstBlockingCredentialProvider(credentialProviders).requiresNewWindow() // it does not requires a window opening 389 ); 390 } 391 392 private BlockingCredentialProvider _getFirstBlockingCredentialProvider(List<CredentialProvider> credentialProviders) 393 { 394 Optional<CredentialProvider> findFirst = credentialProviders.stream().filter(cp -> cp instanceof BlockingCredentialProvider).findFirst(); 395 if (findFirst.isPresent()) 396 { 397 return (BlockingCredentialProvider) findFirst.get(); 398 } 399 else 400 { 401 return null; 402 } 403 } 404 405 /** 406 * Fill the list of available users populations and credential providers 407 * @param request The request 408 * @param parameters The action parameters 409 * @param redirector The cocoon redirector 410 * @param chosenUserPopulations An empty non-null list to fill with with chosen populations 411 * @param credentialProviders An empty non-null list to fill with chosen credential providers 412 * @return true, if the population was determined, false if a redirection was required to choose 413 * @throws IOException If an error occurred 414 * @throws ProcessingException If an error occurred 415 */ 416 protected boolean _prepareUserPopulationsAndCredentialProviders(Request request, Parameters parameters, Redirector redirector, List<UserPopulation> chosenUserPopulations, List<CredentialProvider> credentialProviders) throws ProcessingException, IOException 417 { 418 // Get contexts 419 List<String> contexts = _getContexts(request, parameters); 420 request.setAttribute(REQUEST_ATTRIBUTE_CONTEXTS, contexts); 421 422 // All user populations for this context 423 List<UserPopulation> availableUserPopulations = _getAvailableUserPopulationsIds(request, contexts).stream().map(_userPopulationDAO::getUserPopulation).collect(Collectors.toList()); 424 request.setAttribute(REQUEST_ATTRIBUTE_AVAILABLE_USER_POPULATIONS_LIST, availableUserPopulations); 425 426 // Chosen population 427 String userPopulationId = _getChosenUserPopulationId(request, availableUserPopulations); 428 request.setAttribute(REQUEST_ATTRIBUTE_USER_POPULATION_ID, userPopulationId); 429 430 chosenUserPopulations.addAll(userPopulationId == null ? availableUserPopulations : Collections.singletonList(_userPopulationDAO.getUserPopulation(userPopulationId))); 431 if (chosenUserPopulations.size() == 0) 432 { 433 String redirection = parameters.getParameter("nocontext-redirection", null); 434 if (redirection == null) 435 { 436 throw new IllegalStateException("There is no populations available for contexts '" + StringUtils.join(contexts, "', '") + "'"); 437 } 438 else 439 { 440 redirector.redirect(false, redirection); 441 return false; 442 } 443 } 444 445 // Get possible credential providers 446 boolean availableCredentialProviders = _hasCredentialProviders(chosenUserPopulations); 447 request.setAttribute(REQUEST_ATTRIBUTE_CREDENTIAL_PROVIDER_LIST, availableCredentialProviders); 448 449 // null means the credential providers cannot be determine without knowing population first 450 if (!availableCredentialProviders) 451 { 452 request.setAttribute(REQUEST_ATTRIBUTE_SHOULD_DISPLAY_USER_POPULATIONS_LIST, true); 453 454 // if we are on this screen after a 'back' button hit, we need to reset connecting information 455 _resetConnectingStateToSession(request); 456 457 // Screen "Where Are You From?" with the list of populations to select 458 if (redirector != null) 459 { 460 redirector.redirect(false, getLoginURL(request)); 461 } 462 return false; 463 } 464 else 465 { 466 credentialProviders.addAll(chosenUserPopulations.get(0).getCredentialProviders()); 467 if (credentialProviders.size() == 0) 468 { 469 throw new IllegalStateException("There is no populations credential provider available for contexts '" + StringUtils.join(contexts, "', '") + "'"); 470 } 471 request.setAttribute(REQUEST_ATTRIBUTE_SHOULD_DISPLAY_USER_POPULATIONS_LIST, userPopulationId == null || _hasCredentialProviders(availableUserPopulations) || credentialProviders.size() == 1 && !credentialProviders.stream().filter(cp -> cp instanceof FormCredentialProvider).findAny().isPresent()); 472 return true; 473 } 474 } 475 476 /** 477 * Get the url for the redirector to display the login screen 478 * @param request The request 479 * @return The url. Cannot be null or empty 480 */ 481 protected String getLoginURL(Request request) 482 { 483 return getLoginURLParameters(request, "cocoon://_plugins/core/login.html"); 484 } 485 486 487 /** 488 * Get the url for the redirector to display the login screen 489 * @param request The request 490 * @param baseURL The url to complete with parameters 491 * @return The url. Cannot be null or empty 492 */ 493 @SuppressWarnings("unchecked") 494 protected String getLoginURLParameters(Request request, String baseURL) 495 { 496 List<String> parameters = new ArrayList<>(); 497 498 Boolean invalidPopulationIds = (Boolean) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_INVALID_POPULATION); 499 parameters.add("invalidPopulationIds=" + (invalidPopulationIds == Boolean.TRUE ? "true" : "false")); 500 501 boolean shouldDisplayUserPopulationsList = (boolean) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_SHOULD_DISPLAY_USER_POPULATIONS_LIST); 502 parameters.add("shouldDisplayUserPopulationsList=" + (shouldDisplayUserPopulationsList ? "true" : "false")); 503 504 List<UserPopulation> usersPopulations = (List<UserPopulation>) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_AVAILABLE_USER_POPULATIONS_LIST); 505 if (usersPopulations != null) 506 { 507 parameters.add("usersPopulations=" + URIUtils.encodeParameter(StringUtils.join(usersPopulations.stream().map(UserPopulation::getId).collect(Collectors.toList()), ","))); 508 } 509 510 String chosenPopulationId = (String) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_USER_POPULATION_ID); 511 if (chosenPopulationId != null) 512 { 513 parameters.add("chosenPopulationId=" + URIUtils.encodeParameter(chosenPopulationId)); 514 } 515 516 boolean availableCredentialProviders = (boolean) request.getAttribute(AuthenticateAction.REQUEST_ATTRIBUTE_CREDENTIAL_PROVIDER_LIST); 517 parameters.add("availableCredentialProviders=" + (availableCredentialProviders ? "true" : "false")); 518 519 Integer credentialProviderIndex = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_CREDENTIAL_PROVIDER_INDEX); 520 parameters.add("credentialProviderIndex=" + String.valueOf(credentialProviderIndex != null ? credentialProviderIndex : -1)); 521 522 List<String> contexts = (List<String>) request.getAttribute(REQUEST_ATTRIBUTE_CONTEXTS); 523 parameters.add("contexts=" + URIUtils.encodeParameter(StringUtils.join(contexts, ","))); 524 525 return baseURL + (baseURL.contains("?") ? "&" : "?") + StringUtils.join(parameters, "&"); 526 } 527 528 /** 529 * Get the url for the redirector to display the logout screen 530 * @param request The request 531 * @return The url. Cannot be null or empty 532 */ 533 protected String getLogoutURL(Request request) 534 { 535 return "cocoon://_plugins/core/logout.html"; 536 } 537 538 /** 539 * Determine if there is a list of credential providers to use 540 * @param userPopulations The list of applicable user populations 541 * @return true if credentialproviders can be used 542 */ 543 protected boolean _hasCredentialProviders(List<UserPopulation> userPopulations) 544 { 545 // Is there only one population or all populations have the same credential provider list as the first one? 546 if (userPopulations.size() == 1 || userPopulations.stream().map(UserPopulation::getCredentialProviders).distinct().count() == 1) 547 { 548 return true; 549 } 550 551 // Cannot determine the list 552 return false; 553 } 554 555 /** 556 * Get the available populations for the given contexts 557 * @param request The request 558 * @param contexts The contexts 559 * @return The non-null list of populations 560 */ 561 protected Set<String> _getAvailableUserPopulationsIds(Request request, List<String> contexts) 562 { 563 return _populationContextHelper.getUserPopulationsOnContexts(contexts, false, false); 564 } 565 566 /** 567 * Get the population for the given context 568 * @param request The request 569 * @param availableUserPopulations The available users populations 570 * @return The chosen population id. Can be null. 571 */ 572 protected String _getChosenUserPopulationId(Request request, List<UserPopulation> availableUserPopulations) 573 { 574 // Get request population choice 575 String userPopulationId = request.getParameter(REQUEST_PARAMETER_POPULATION_NAME); 576 if (userPopulationId == null) 577 { 578 // Get memorized population choice 579 Session session = request.getSession(false); 580 if (session != null) 581 { 582 userPopulationId = (String) session.getAttribute(SESSION_CONNECTING_USERPOPULATION_ID); 583 } 584 } 585 586 // A population choice was already made 587 if (StringUtils.isNotBlank(userPopulationId)) 588 { 589 final String finalUserPopulationId = userPopulationId; 590 if (availableUserPopulations.stream().anyMatch(userPopulation -> userPopulation.getId().equals(finalUserPopulationId))) 591 { 592 return userPopulationId; 593 } 594 else 595 { 596 // Wrong submitted population id 597 request.setAttribute(REQUEST_ATTRIBUTE_INVALID_POPULATION, true); 598 } 599 } 600 601 return null; 602 } 603 604 /** 605 * Try to authenticate with this credential provider in this mode. Delegates to _doProcess 606 * @param request The request 607 * @param runningBlockingkMode false for non-blocking mode, true for blocking mode 608 * @param runningCredentialProvider the Credential provider to test 609 * @param runningCredentialProviderIndex The index of the currently tested credential provider 610 * @param redirector The cocoon redirector 611 * @param userPopulations The list of possible user populations 612 * @return false if we should try with another Credential provider, true otherwise 613 * @throws Exception If an error occurred 614 */ 615 protected boolean _process(Request request, boolean runningBlockingkMode, CredentialProvider runningCredentialProvider, int runningCredentialProviderIndex, Redirector redirector, List<UserPopulation> userPopulations) throws Exception 616 { 617 boolean existingSession = request.getSession(false) != null; 618 _saveConnectingStateToSession(request, runningBlockingkMode ? -1 : runningCredentialProviderIndex, runningBlockingkMode); 619 if (_doProcess(request, runningBlockingkMode, runningCredentialProvider, redirector, userPopulations)) 620 { 621 return true; 622 } 623 if (existingSession) 624 { 625 // A session was created but finally we do not need it 626 request.getSession().invalidate(); 627 } 628 return false; 629 } 630 631 /** 632 * Try to authenticate with this credential provider in this mode 633 * @param request The request 634 * @param runningBlockingkMode false for non-blocking mode, true for blocking mode 635 * @param runningCredentialProvider the Credential provider to test 636 * @param redirector The cocoon redirector 637 * @param userPopulations The list of possible user populations 638 * @return false if we should try with another Credential provider, true otherwise 639 * @throws Exception If an error occurred 640 */ 641 642 protected boolean _doProcess(Request request, boolean runningBlockingkMode, CredentialProvider runningCredentialProvider, Redirector redirector, List<UserPopulation> userPopulations) throws Exception 643 { 644 if (runningCredentialProvider.grantAnonymousRequest(runningBlockingkMode)) 645 { 646 // Anonymous request 647 request.setAttribute(REQUEST_ATTRIBUTE_GRANTED, true); 648 return true; 649 } 650 651 UserIdentity potentialUserIdentity = null; 652 try 653 { 654 655 potentialUserIdentity = runningCredentialProvider.getUserIdentity(runningBlockingkMode, redirector); 656 } 657 catch (WeakPasswordException e) 658 { 659 // User credentials are ok but user use a weak password 660 potentialUserIdentity = e.getUserIdentity(); 661 _handleWeakPassord(request, redirector, e.getUserIdentity()); 662 } 663 664 if (redirector.hasRedirected()) 665 { 666 // getCredentials require a redirection, save state and proceed 667 return true; 668 } 669 else if (potentialUserIdentity == null) 670 { 671 // Let us try another credential provider 672 return false; 673 } 674 675 // Check if user exists 676 UserIdentity userIdentity = _getUserIdentity(userPopulations, potentialUserIdentity, redirector, runningBlockingkMode, runningCredentialProvider); 677 if (redirector.hasRedirected()) 678 { 679 // getCredentials require a redirection, save state and proceed 680 return true; 681 } 682 else if (userIdentity == null) 683 { 684 // Let us try another credential provider 685 return false; 686 } 687 688 // Save user identity 689 _setUserIdentityInSession(request, userIdentity, runningCredentialProvider, runningBlockingkMode); 690 691 // Authentication succeeded 692 runningCredentialProvider.userAllowed(runningBlockingkMode, userIdentity, redirector); 693 694 _logLoginEvent(runningCredentialProvider, userIdentity); 695 696 return true; 697 } 698 699 /** 700 * Handle weak password exception 701 * @param request the request 702 * @param redirector the redirector 703 * @param userIdentity the user identity with a weak password 704 * @throws Exception if an error occurred 705 */ 706 protected void _handleWeakPassord(Request request, Redirector redirector, UserIdentity userIdentity) throws Exception 707 { 708 // Defaut implementation only log that user has a weak password. User will be authenticated 709 ForensicLogger.info("authentication.form.weak.password", Map.of("userIdentity", userIdentity), userIdentity); 710 getLogger().warn("Password of user " + userIdentity + " does not meet the security requirements. User is authenticated despite the risk for security."); 711 } 712 713 /** 714 * Log login event 715 * @param credentialProvider the running credential provider 716 * @param userIdentity the user identity 717 */ 718 protected void _logLoginEvent(CredentialProvider credentialProvider, UserIdentity userIdentity) 719 { 720 Map<String, Object> loginArgs = Map.of("credential-provider", credentialProvider.getCredentialProviderModelId(), "user", userIdentity); 721 ForensicLogger.info("authentication.login", loginArgs, userIdentity); 722 } 723 724 /** 725 * Log logout event 726 * @param userIdentity the user identity 727 */ 728 protected void _logLogoutEvent(UserIdentity userIdentity) 729 { 730 Map<String, Object> logoutArgs = Map.of("user", userIdentity); 731 ForensicLogger.info("authentication.logout", logoutArgs, userIdentity); 732 } 733 734 /** 735 * Reset the connecting information in session 736 * @param request The request 737 */ 738 protected static void _resetConnectingStateToSession(Request request) 739 { 740 Session session = request.getSession(false); 741 if (session != null) 742 { 743 session.removeAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX); 744 session.removeAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_MODE); 745 session.removeAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX_LASTBLOCKINGKNOWN); 746 session.removeAttribute(SESSION_CONNECTING_USERPOPULATION_ID); 747 } 748 } 749 750 /** 751 * When the process end successfully, save the state 752 * @param request The request 753 * @param runningBlockingkMode false for non-blocking mode, true for blocking mode 754 * @param runningCredentialProviderIndex the currently tested credential provider 755 */ 756 protected void _saveConnectingStateToSession(Request request, int runningCredentialProviderIndex, boolean runningBlockingkMode) 757 { 758 Session session = request.getSession(true); 759 session.setAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX, runningCredentialProviderIndex); 760 session.setAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_MODE, runningBlockingkMode); 761 session.setAttribute(SESSION_CONNECTING_USERPOPULATION_ID, request.getAttribute(REQUEST_ATTRIBUTE_USER_POPULATION_ID)); 762 } 763 764 /** 765 * Save user identity in request 766 * @param request The request 767 * @param userIdentity The useridentity to save 768 * @param credentialProvider The credential provider used to connect 769 * @param blockingMode The mode used for the credential provider 770 */ 771 protected void _setUserIdentityInSession(Request request, UserIdentity userIdentity, CredentialProvider credentialProvider, boolean blockingMode) 772 { 773 setUserIdentityInSession(request, userIdentity, credentialProvider, blockingMode); 774 if (_observationManager != null) 775 { 776 Map<String, Object> eventParams = new HashMap<>(); 777 eventParams.put(ObservationConstants.ARGS_USER, userIdentity); 778 _observationManager.notify(new Event(ObservationConstants.EVENT_USER_AUTHENTICATED, UserPopulationDAO.SYSTEM_USER_IDENTITY, eventParams)); 779 } 780 } 781 782 /** 783 * Save user identity in request 784 * @param request The request 785 * @param userIdentity The useridentity to save 786 * @param credentialProvider The credential provider used to connect 787 * @param blockingMode The mode used for the credential provider 788 */ 789 public static void setUserIdentityInSession(Request request, UserIdentity userIdentity, CredentialProvider credentialProvider, boolean blockingMode) 790 { 791 Session session = renewSession(request); 792 _resetConnectingStateToSession(request); 793 session.setAttribute(SESSION_USERIDENTITY, userIdentity); 794 session.setAttribute(SESSION_CREDENTIALPROVIDER, credentialProvider); 795 session.setAttribute(SESSION_CREDENTIALPROVIDER_MODE, blockingMode); 796 } 797 798 /** 799 * Change the session id (for security purposes) 800 * @param request The current request 801 * @return The new session 802 */ 803 public static Session renewSession(Request request) 804 { 805 Session session = request.getSession(true); 806 807 Map<String, Object> attributesCopy = new HashMap<>(); 808 809 Enumeration<String> attributeNames = session.getAttributeNames(); 810 while (attributeNames.hasMoreElements()) 811 { 812 String attributeName = attributeNames.nextElement(); 813 attributesCopy.put(attributeName, session.getAttribute(attributeName)); 814 } 815 816 session.invalidate(); 817 818 session = request.getSession(true); 819 820 for (Entry<String, Object> attribute : attributesCopy.entrySet()) 821 { 822 session.setAttribute(attribute.getKey(), attribute.getValue()); 823 } 824 825 return session; 826 } 827 828 /** 829 * Get the user identity of the connected user from the session 830 * @param request The request 831 * @return The connected useridentity or null 832 */ 833 protected UserIdentity _getUserIdentityFromSession(Request request) 834 { 835 return getUserIdentityFromSession(request); 836 } 837 838 /** 839 * Get the user identity of the connected user from the session 840 * @param request The request 841 * @return The connected useridentity or null 842 */ 843 public static UserIdentity getUserIdentityFromSession(Request request) 844 { 845 Session session = request.getSession(false); 846 if (session != null) 847 { 848 return (UserIdentity) session.getAttribute(SESSION_USERIDENTITY); 849 } 850 return null; 851 } 852 853 /** 854 * Get the credential provider used for the current connection 855 * @param request The request 856 * @return The credential provider used or null 857 */ 858 protected CredentialProvider _getCredentialProviderFromSession(Request request) 859 { 860 return getCredentialProviderFromSession(request); 861 } 862 863 /** 864 * Get the credential provider used for the current connection 865 * @param request The request 866 * @return The credential provider used or null 867 */ 868 public static CredentialProvider getCredentialProviderFromSession(Request request) 869 { 870 Session session = request.getSession(false); 871 if (session != null) 872 { 873 return (CredentialProvider) session.getAttribute(SESSION_CREDENTIALPROVIDER); 874 } 875 return null; 876 } 877 878 /** 879 * Get the credential provider mode used for the current connection 880 * @param request The request 881 * @return The credential provider mode used or null 882 */ 883 protected Boolean _getCredentialProviderModeFromSession(Request request) 884 { 885 return getCredentialProviderModeFromSession(request); 886 } 887 888 /** 889 * Get the credential provider mode used for the current connection 890 * @param request The request 891 * @return The credential provider mode used or null 892 */ 893 public static Boolean getCredentialProviderModeFromSession(Request request) 894 { 895 Session session = request.getSession(false); 896 if (session != null) 897 { 898 return (Boolean) session.getAttribute(SESSION_CREDENTIALPROVIDER_MODE); 899 } 900 return null; 901 } 902 903 /** 904 * If there is a running credential provider, was it in non-blocking or blocking mode? 905 * @param request The request 906 * @return false if non-blocking, true if blocking 907 */ 908 protected boolean _isCurrentCredentialProviderInBlockingMode(Request request) 909 { 910 Integer requestedCredentialParameterIndex = _getCurrentCredentialProviderIndexFromParameter(request); 911 if (requestedCredentialParameterIndex != null && requestedCredentialParameterIndex != -1) 912 { 913 return true; 914 } 915 916 Session session = request.getSession(false); 917 if (session != null) 918 { 919 Boolean mode = (Boolean) session.getAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_MODE); 920 session.removeAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_MODE); 921 if (mode != null) 922 { 923 return mode.booleanValue(); 924 } 925 } 926 return false; 927 } 928 929 /** 930 * Call this to skip the currently used credential provider and proceed to the next one. 931 * Useful for non blocking 932 * @param request The request 933 */ 934 public static void skipCurrentCredentialProvider(Request request) 935 { 936 Session session = request.getSession(); 937 if (session != null) 938 { 939 Integer cpIndex = (Integer) session.getAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX); 940 if (cpIndex != null) 941 { 942 cpIndex++; 943 session.setAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX, cpIndex); 944 } 945 } 946 } 947 948 /** 949 * Get the current credential provider index or -1 if there no running provider FROM REQUEST PARAMETER 950 * @param request The request 951 * @return The credential provider index to use in the availablesCredentialProviders list or -1 or null 952 */ 953 protected Integer _getCurrentCredentialProviderIndexFromParameter(Request request) 954 { 955 // Is the CP requested? 956 String requestedCredentialParameterIndex = request.getParameter(REQUEST_PARAMETER_CREDENTIALPROVIDER_INDEX); 957 if (StringUtils.isNotBlank(requestedCredentialParameterIndex)) 958 { 959 int index = Integer.parseInt(requestedCredentialParameterIndex); 960 return index; 961 } 962 return null; 963 } 964 965 /** 966 * Get the current credential provider index or -1 if there no running provider 967 * @param request The request 968 * @param availableCredentialProviders The list of available credential provider 969 * @return The credential provider index to use in the availablesCredentialProviders list or -1 970 */ 971 protected int _getCurrentCredentialProviderIndex(Request request, List<CredentialProvider> availableCredentialProviders) 972 { 973 // Is the CP requested? 974 Integer requestedCredentialParameterIndex = _getCurrentCredentialProviderIndexFromParameter(request); 975 if (requestedCredentialParameterIndex != null) 976 { 977 if (requestedCredentialParameterIndex < availableCredentialProviders.size()) 978 { 979 return requestedCredentialParameterIndex; 980 } 981 else 982 { 983 return -1; 984 } 985 } 986 987 // Was the CP memorized? 988 Session session = request.getSession(false); 989 if (session != null) 990 { 991 Integer cpIndex = (Integer) session.getAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX); 992 session.removeAttribute(SESSION_CONNECTING_CREDENTIALPROVIDER_INDEX); 993 994 if (cpIndex != null) 995 { 996 return cpIndex; 997 } 998 } 999 1000 // Default value 1001 return -1; 1002 } 1003 1004 /** 1005 * Get the authentication context 1006 * @param request The request 1007 * @param parameters The action parameters 1008 * @return The context 1009 * @throws IllegalArgumentException If there is no context set 1010 */ 1011 protected List<String> _getContexts(Request request, Parameters parameters) 1012 { 1013 String context = parameters.getParameter("context", null); 1014 if (context == null) 1015 { 1016 throw new IllegalArgumentException("The authentication is not parameterized correctly: an authentication context must be specified"); 1017 } 1018 return Collections.singletonList(context); 1019 } 1020 1021 /** 1022 * Determine if the request is internal and do not need authentication 1023 * @param request The request 1024 * @return true to bypass this authentication 1025 */ 1026 protected boolean _internalRequest(Request request) 1027 { 1028 return "true".equals(request.getAttribute(REQUEST_ATTRIBUTE_AUTHENTICATED)) || request.getAttribute(REQUEST_ATTRIBUTE_INTERNAL_ALLOWED) != null; 1029 } 1030 1031 /** 1032 * Determine if the request is one of the authentication process (except the credential providers) 1033 * @param request The request 1034 * @return true to bypass this authentication 1035 */ 1036 protected boolean _acceptedUrl(Request request) 1037 { 1038 // URL without server context and leading slash. 1039 String url = (String) request.getAttribute(WorkspaceMatcher.IN_WORKSPACE_URL); 1040 for (Pattern pattern : _acceptedUrlPatterns) 1041 { 1042 if (pattern.matcher(url).matches()) 1043 { 1044 // Anonymous request 1045 request.setAttribute(REQUEST_ATTRIBUTE_GRANTED, true); 1046 1047 return true; 1048 } 1049 } 1050 1051 return false; 1052 } 1053 1054 /** 1055 * This method ensure that there is a currently connected user and that it is still valid 1056 * @param request The request 1057 * @param redirector The cocoon redirector 1058 * @param parameters The action parameters 1059 * @return true if the user is connected and valid 1060 * @throws Exception if an error occurred 1061 */ 1062 protected boolean _validateCurrentlyConnectedUser(Request request, Redirector redirector, Parameters parameters) throws Exception 1063 { 1064 Session session = request.getSession(false); 1065 UserIdentity userCurrentlyConnected = _getUserIdentityFromSession(request); 1066 CredentialProvider runningCredentialProvider = _getCredentialProviderFromSession(request); 1067 Boolean runningBlockingkMode = _getCredentialProviderModeFromSession(request); 1068 1069 if (runningCredentialProvider == null || userCurrentlyConnected == null || runningBlockingkMode == null || !runningCredentialProvider.isStillConnected(runningBlockingkMode, userCurrentlyConnected, redirector)) 1070 { 1071 if (redirector.hasRedirected()) 1072 { 1073 return true; 1074 } 1075 1076 // There is an invalid connected user 1077 if (session != null && userCurrentlyConnected != null) 1078 { 1079 session.invalidate(); 1080 } 1081 return false; 1082 } 1083 1084 // let us make an exception for the user image url since we need it on the 403 page 1085 if (RuntimeServlet.getRunMode() == RunMode.MAINTENANCE && MaintenanceAction.acceptedUrl(request)) 1086 { 1087 return true; 1088 } 1089 1090 _validateCurrentlyConnectedUserIsInAuthorizedPopulation(userCurrentlyConnected, request, parameters); 1091 1092 return true; 1093 } 1094 1095 /** 1096 * This method is the second part of the process that ensure that there is a currently connected user and that it is still valid 1097 * @param userCurrentlyConnected The user to test 1098 * @param request The request 1099 * @param parameters The action parameters 1100 */ 1101 protected void _validateCurrentlyConnectedUserIsInAuthorizedPopulation(UserIdentity userCurrentlyConnected, Request request, Parameters parameters) 1102 { 1103 if (_getTokenMode(parameters) == TOKEN_MODE.DEFAULT) 1104 { 1105 // we know this is a valid user, but we need to check if the context is correct 1106 List<String> contexts = _getContexts(request, parameters); 1107 // All user populations for this context 1108 Set<String> availableUserPopulationsIds = _getAvailableUserPopulationsIds(request, contexts); 1109 1110 if (!availableUserPopulationsIds.contains(userCurrentlyConnected.getPopulationId())) 1111 { 1112 throw new AccessDeniedException("The user " + userCurrentlyConnected + " cannot be authenticated to the contexts '" + StringUtils.join(contexts, "', '") + "' because its populations are not part of the " + availableUserPopulationsIds.size() + " granted populations."); 1113 } 1114 } 1115 else 1116 { 1117 // In 'token only' mode, check if user is part of the active populations (regardless of the context) 1118 List<String> availableUserPopulationsIds = _userPopulationDAO.getEnabledUserPopulations(false).stream().map(UserPopulation::getId).collect(Collectors.toList()); 1119 1120 if (!availableUserPopulationsIds.contains(userCurrentlyConnected.getPopulationId())) 1121 { 1122 throw new AccessDeniedException("The user " + userCurrentlyConnected + " cannot be authenticated because its populations does not exist or it is disabled."); 1123 } 1124 } 1125 } 1126 1127 /** 1128 * Test if user wants to logout and handle it 1129 * @param redirector The cocoon redirector 1130 * @param objectModel The cocoon object model 1131 * @param source The sitemap source 1132 * @param parameters The sitemap parameters 1133 * @return true if the user was logged out 1134 * @throws Exception if an error occurred 1135 */ 1136 protected boolean _handleLogout(Redirector redirector, Map objectModel, String source, Parameters parameters) throws Exception 1137 { 1138 Request request = ObjectModelHelper.getRequest(objectModel); 1139 if (StringUtils.equals(request.getContextPath() + request.getAttribute(WorkspaceMatcher.WORKSPACE_URI) + "/logout.html", request.getRequestURI()) 1140 || StringUtils.equals("true", parameters.getParameter("logout", "false"))) 1141 { 1142 // The user logs out 1143 UserIdentity currentUser = _currentUserProvider.getUser(); 1144 if (currentUser != null) // user can be null when calling the url with an expired session 1145 { 1146 _currentUserProvider.logout(redirector); 1147 _logLogoutEvent(currentUser); 1148 } 1149 1150 if (!redirector.hasRedirected()) 1151 { 1152 redirector.redirect(false, getLogoutURL(request)); 1153 } 1154 1155 return true; 1156 } 1157 return false; 1158 } 1159 1160 /** 1161 * Check the authentications of the authentication manager 1162 * @param userPopulations The list of available matching populations 1163 * @param redirector The cocoon redirector 1164 * @param runningBlockingkMode false for non-blocking mode, true for blocking mode 1165 * @param runningCredentialProvider The Credential provider to test 1166 * @param potentialUserIdentity A possible user identity. Population can be null. User may not exist either. 1167 * @return The user population matching credentials or null 1168 * @throws Exception If an error occurred 1169 * @throws AccessDeniedException If the user is rejected 1170 */ 1171 protected UserIdentity _getUserIdentity(List<UserPopulation> userPopulations, UserIdentity potentialUserIdentity, Redirector redirector, boolean runningBlockingkMode, CredentialProvider runningCredentialProvider) throws Exception 1172 { 1173 if (potentialUserIdentity.getPopulationId() == null) 1174 { 1175 for (UserPopulation up : userPopulations) 1176 { 1177 User user = _userManager.getUser(up, potentialUserIdentity.getLogin()); 1178 if (_isLoginCaseExact(user, potentialUserIdentity)) 1179 { 1180 return user.getIdentity(); 1181 } 1182 } 1183 } 1184 else 1185 { 1186 User user = _userManager.getUser(potentialUserIdentity.getPopulationId(), potentialUserIdentity.getLogin()); 1187 if (_isLoginCaseExact(user, potentialUserIdentity)) 1188 { 1189 return user.getIdentity(); 1190 } 1191 } 1192 1193 runningCredentialProvider.userNotAllowed(runningBlockingkMode, redirector); 1194 1195 if (getLogger().isWarnEnabled()) 1196 { 1197 getLogger().warn("The user '" + potentialUserIdentity + "' was authenticated by the credential provider '" + runningCredentialProvider.getCredentialProviderModelId() + "' but it does not match any user of the " + userPopulations.size() + " granted populations."); 1198 } 1199 1200 return null; 1201 } 1202 1203 private boolean _isLoginCaseExact(User user, UserIdentity potentialUserIdentity) 1204 { 1205 return user != null 1206 && (user.getUserDirectory().isCaseSensitive() && StringUtils.equals(user.getIdentity().getLogin(), potentialUserIdentity.getLogin()) 1207 || !user.getUserDirectory().isCaseSensitive() && StringUtils.equalsIgnoreCase(user.getIdentity().getLogin(), potentialUserIdentity.getLogin())); 1208 } 1209}