001/* 002 * Copyright 2012 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.web.usermanagement; 017 018import java.util.HashMap; 019import java.util.Map; 020 021import org.apache.avalon.framework.parameters.Parameters; 022import org.apache.avalon.framework.service.ServiceException; 023import org.apache.avalon.framework.service.ServiceManager; 024import org.apache.cocoon.acting.ServiceableAction; 025import org.apache.cocoon.environment.ObjectModelHelper; 026import org.apache.cocoon.environment.Redirector; 027import org.apache.cocoon.environment.Request; 028import org.apache.cocoon.environment.SourceResolver; 029import org.apache.commons.lang.StringUtils; 030 031import org.ametys.core.user.CurrentUserProvider; 032import org.ametys.core.user.UserIdentity; 033import org.ametys.runtime.authentication.AuthorizationRequiredException; 034import org.ametys.runtime.i18n.I18nizableText; 035import org.ametys.runtime.parameter.Errors; 036import org.ametys.web.renderingcontext.RenderingContext; 037import org.ametys.web.renderingcontext.RenderingContextHandler; 038 039import com.google.common.collect.ArrayListMultimap; 040import com.google.common.collect.Multimap; 041 042/** 043 * Handle the lost password and change password actions. 044 */ 045public class UserPasswordAction extends ServiceableAction 046{ 047 /** The user signup manager. */ 048 protected UserSignupManager _userSignupManager; 049 050 /** The rendering context handler. */ 051 protected RenderingContextHandler _renderingContextHandler; 052 053 /** The current user provider */ 054 protected CurrentUserProvider _currentUserProvider; 055 056 @Override 057 public void service(ServiceManager serviceManager) throws ServiceException 058 { 059 super.service(serviceManager); 060 _userSignupManager = (UserSignupManager) serviceManager.lookup(UserSignupManager.ROLE); 061 _renderingContextHandler = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE); 062 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 063 } 064 065 @Override 066 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 067 { 068 Request request = ObjectModelHelper.getRequest(objectModel); 069 String siteName = (String) request.getAttribute("site"); 070 String language = (String) request.getAttribute("sitemapLanguage"); 071 RenderingContext renderingContext = _renderingContextHandler.getRenderingContext(); 072 073 Map<String, String> results = new HashMap<>(); 074 075 UserIdentity foUser = _currentUserProvider.getUser(); 076 077 boolean lostPassword = "true".equals(request.getParameter("lost-password")); // (1) when unconnected user ask for a new password 078 boolean changePassword = "true".equals(request.getParameter("change-password")); // (2) when connected user ask for change password 079 boolean submitPassword = "true".equals(request.getParameter("pwd-submit")); // (3) when connected or unconnected user submit a new password 080 081 String mode = request.getParameter("mode"); 082 String login = request.getParameter("login"); 083 String population = request.getParameter("population"); 084 String token = request.getParameter("token"); 085 086 boolean reinitPassword = StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(login) && StringUtils.isNotEmpty(population); 087 088 Multimap<String, I18nizableText> errors = ArrayListMultimap.create(); 089 090 try 091 { 092 if ("lostpassword".equals(mode)) 093 { 094 // Unconnected user clicked on "lost password" => display the form to request a new token. 095 results.put("step", "lost-password"); 096 } 097 else if (lostPassword) 098 { 099 // Unconnected user entered his email/login and population to request a new token by email 100 results.put("step", "lost-password-change"); 101 resetPassword(request, siteName, language, errors); 102 if (errors.isEmpty()) 103 { 104 results.put("status", "success"); 105 } 106 } 107 else if (changePassword) 108 { 109 // Connected user asks for a new token by email 110 results.put("step", "change-password-change"); 111 resetConnectedUserPassword(request, siteName, language, errors); 112 if (errors.isEmpty()) 113 { 114 results.put("status", "success"); 115 } 116 } 117 else if (submitPassword) 118 { 119 // Connected or unconnected user submitted a new password 120 results.put("step", "user-update"); 121 changeUserPassword(request, siteName, token, errors); 122 if (errors.isEmpty()) 123 { 124 results.put("status", "success"); 125 } 126 } 127 else if (reinitPassword) 128 { 129 // User clicked on email link to display the password form. 130 // If a token is provided, check it. 131 results.put("step", "password"); 132 checkPasswordToken(request, siteName, login, token, population, errors); 133 if (errors.isEmpty()) 134 { 135 results.put("status", "success"); 136 } 137 } 138 else if (foUser != null) 139 { 140 // Default behavior: if user is connected, display the form to request a new token with the connected user. 141 results.put("step", "change-password"); 142 } 143 else if (renderingContext == RenderingContext.FRONT) 144 { 145 // Send a 401 to force the user to authenticate. 146 throw new AuthorizationRequiredException(); 147 } 148 } 149 catch (UserManagementException e) 150 { 151 errors.put("global", new I18nizableText("general-error")); 152 results.put("step", "no-form"); 153 154 getLogger().error("An error occurred resetting a user password.", e); 155 } 156 157 request.setAttribute("errors", errors); 158 159 return results; 160 } 161 162 /** 163 * Reset a user's sign-up request. 164 * @param request the user request. 165 * @param siteName the site name. 166 * @param language the language. 167 * @param errors the Map to fill with errors to display to the user. 168 * @throws UserManagementException if an error occurs. 169 */ 170 protected void resetPassword(Request request, String siteName, String language, Multimap<String, I18nizableText> errors) throws UserManagementException 171 { 172 String email = request.getParameter("email"); 173 String populationId = request.getParameter("population"); 174 175 int status = _userSignupManager.resetPassword(siteName, language, email, populationId); 176 177 if (status == UserSignupManager.LOST_PASSWORD_USER_UNKNOWN) 178 { 179 errors.put("global", new I18nizableText("email-unknown")); 180 } 181 } 182 183 /** 184 * Reset a connected user password. 185 * @param request the user request. 186 * @param siteName the site name. 187 * @param language the language. 188 * @param errors the Map to fill with errors to display to the user. 189 * @throws UserManagementException if an error occurs. 190 */ 191 protected void resetConnectedUserPassword(Request request, String siteName, String language, Multimap<String, I18nizableText> errors) throws UserManagementException 192 { 193 UserIdentity foUser = _currentUserProvider.getUser(); 194 String login; 195 String populationId; 196 197 if (foUser == null) 198 { 199 errors.put("global", new I18nizableText("not-connected")); 200 login = null; 201 populationId = null; 202 } 203 else 204 { 205 login = foUser.getLogin(); 206 populationId = foUser.getPopulationId(); 207 } 208 209 int status = _userSignupManager.resetPassword(siteName, language, login, populationId); 210 211 if (status == UserSignupManager.LOST_PASSWORD_USER_UNKNOWN) 212 { 213 errors.put("global", new I18nizableText("email-unknown")); 214 } 215 } 216 217 /** 218 * Check that a token is valid. 219 * @param request the user request. 220 * @param siteName the site name. 221 * @param login the user login. 222 * @param token the sign-up token that was sent to the user. 223 * @param populationId The id of the population 224 * @param errors the Map to fill with errors to display to the user. 225 * @throws UserManagementException if an error occurs. 226 */ 227 protected void checkPasswordToken(Request request, String siteName, String login, String token, String populationId, Multimap<String, I18nizableText> errors) throws UserManagementException 228 { 229 int tokenStatus = _userSignupManager.checkPasswordToken(siteName, login, token, populationId); 230 231 if (tokenStatus == UserSignupManager.SIGNUP_TOKEN_UNKNOWN) 232 { 233 errors.put("global", new I18nizableText("error-token-unknown")); 234 } 235 else if (tokenStatus == UserSignupManager.SIGNUP_TOKEN_EXPIRED) 236 { 237 errors.put("global", new I18nizableText("error-token-expired")); 238 } 239 } 240 241 /** 242 * Sign-up the user: create a real user from his temporary information. 243 * @param request the user request. 244 * @param siteName the site name. 245 * @param token the sign-up token that was sent to the user. 246 * @param errors the Map to fill with errors to display to the user. 247 * @throws UserManagementException if an error occurs. 248 */ 249 protected void changeUserPassword(Request request, String siteName, String token, Multimap<String, I18nizableText> errors) throws UserManagementException 250 { 251 // Get the login either from the request or from the connected user. 252 String login = request.getParameter("login"); 253 String population = request.getParameter("population"); 254 UserIdentity foUser; 255 if (StringUtils.isEmpty(login) || StringUtils.isEmpty(population)) 256 { 257 foUser = _currentUserProvider.getUser(); 258 } 259 else 260 { 261 foUser = new UserIdentity(login, population); 262 } 263 264 if (foUser == null) 265 { 266 errors.put("global", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_NO_USER")); 267 return; 268 } 269 270 String password = request.getParameter("password"); 271 String passwordConfirmation = request.getParameter("password-confirmation"); 272 273 // First validation. 274 if (StringUtils.isBlank(password)) 275 { 276 errors.put("password", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_PASSWORD_EMPTY")); 277 } 278 else if (!password.equals(passwordConfirmation)) 279 { 280 errors.put("password", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_PASSWORD_CONFIRMATION_DOESNT_MATCH")); 281 } 282 283 // Full validation. 284 Map<String, Errors> inputErrors = _userSignupManager.validatePassword(siteName, password, foUser.getLogin(), foUser.getPopulationId()); 285 for (String field : inputErrors.keySet()) 286 { 287 errors.putAll(field, inputErrors.get(field).getErrors()); 288 } 289 290 if (errors.isEmpty()) 291 { 292 // Validation passed: effectively change the password. 293 int status = _userSignupManager.changeUserPassword(siteName, foUser.getLogin(), token, password, foUser.getPopulationId()); 294 295 if (status == UserSignupManager.SIGNUP_TOKEN_UNKNOWN) 296 { 297 errors.put("global", new I18nizableText("error-token-unknown")); 298 } 299 else if (status == UserSignupManager.SIGNUP_TOKEN_EXPIRED) 300 { 301 errors.put("global", new I18nizableText("error-token-expired")); 302 } 303 } 304 } 305 306}