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.Arrays; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.parameters.Parameters; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.cocoon.acting.ServiceableAction; 029import org.apache.cocoon.environment.ObjectModelHelper; 030import org.apache.cocoon.environment.Redirector; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.environment.SourceResolver; 033import org.apache.commons.lang.StringUtils; 034 035import org.ametys.core.captcha.CaptchaHelper; 036import org.ametys.core.user.UserManager; 037import org.ametys.core.user.directory.ModifiableUserDirectory; 038import org.ametys.core.user.population.UserPopulationDAO; 039import org.ametys.plugins.repository.AmetysObjectResolver; 040import org.ametys.runtime.i18n.I18nizableText; 041import org.ametys.runtime.model.ModelItem; 042import org.ametys.runtime.parameter.Errors; 043import org.ametys.web.URIPrefixHandler; 044import org.ametys.web.WebConstants; 045import org.ametys.web.cache.PageHelper; 046import org.ametys.web.renderingcontext.RenderingContextHandler; 047import org.ametys.web.repository.page.Page; 048import org.ametys.web.repository.page.ZoneItem; 049import org.ametys.web.site.SiteConfigurationExtensionPoint; 050 051import com.google.common.collect.ArrayListMultimap; 052import com.google.common.collect.Multimap; 053 054/** 055 * Handle the user sign-up actions. 056 */ 057public class UserSignupAction extends ServiceableAction 058{ 059 /** The UsersManager standard fields. */ 060 public static final Set<String> STANDARD_FIELDS = new HashSet<>(Arrays.asList("login", "firstname", "lastname", "email", "address", "password")); 061 062 private static final String __SIGNUP_SERVICE_PARAMETER_USERDIRECTORY = "userdirectory"; 063 064 /** The user signup manager. */ 065 protected UserSignupManager _userSignupManager; 066 067 /** The user manager. */ 068 protected UserManager _userManager; 069 070 /** The DAO for user populations */ 071 protected UserPopulationDAO _userPopulationDAO; 072 /** The site configuration EP. */ 073 protected SiteConfigurationExtensionPoint _siteConfiguration; 074 075 /** The Ametys Resolver */ 076 protected AmetysObjectResolver _resolver; 077 078 /** Page helper */ 079 protected PageHelper _pageHelper; 080 /** The rendering context */ 081 protected RenderingContextHandler _renderingContextHandler; 082 /** The URI prefix handler */ 083 protected URIPrefixHandler _prefixHandler; 084 085 @Override 086 public void service(ServiceManager serviceManager) throws ServiceException 087 { 088 super.service(serviceManager); 089 _userSignupManager = (UserSignupManager) serviceManager.lookup(UserSignupManager.ROLE); 090 _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE); 091 _userPopulationDAO = (UserPopulationDAO) serviceManager.lookup(UserPopulationDAO.ROLE); 092 _siteConfiguration = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 093 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 094 _pageHelper = (PageHelper) serviceManager.lookup(PageHelper.ROLE); 095 _renderingContextHandler = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE); 096 _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE); 097 } 098 099 @Override 100 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 101 { 102 Request request = ObjectModelHelper.getRequest(objectModel); 103 String siteName = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SITE_NAME); 104 String language = (String) request.getAttribute(WebConstants.REQUEST_ATTR_SITEMAP_NAME); 105 106 Map<String, String> results = new HashMap<>(); 107 108 String signup = request.getParameter("signup"); 109 String submitPassword = request.getParameter("pwd-submit"); 110 String mode = request.getParameter("mode"); 111 String email = request.getParameter("email"); 112 String firstname = request.getParameter("firstname"); 113 String lastname = request.getParameter("lastname"); 114 String token = request.getParameter("token"); 115 116 Multimap<String, I18nizableText> errors = ArrayListMultimap.create(); 117 118 ZoneItem zoneItem = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM); 119 String userDirectoryParameterValue = zoneItem.getServiceParameters().getValue(__SIGNUP_SERVICE_PARAMETER_USERDIRECTORY); 120 String[] populationAndDirectory = userDirectoryParameterValue.split("#", 2); 121 String populationId = populationAndDirectory[0]; 122 String userDirectoryId = populationAndDirectory[1]; 123 124 try 125 { 126 if ("new-token".equals(mode)) 127 { 128 // Action which requests a new token. 129 results.put("step", "new-token"); 130 resetTempSignup(request, siteName, language, populationId, userDirectoryId, errors); 131 if (errors.isEmpty()) 132 { 133 results.put("status", "success"); 134 } 135 } 136 else if ("true".equals(submitPassword)) 137 { 138 // If the password has been entered, signup the user. 139 results.put("step", "signup"); 140 doSignup(request, parameters, siteName, language, firstname, lastname, email, token, populationId, userDirectoryId, errors); 141 if (errors.isEmpty()) 142 { 143 results.put("status", "success"); 144 } 145 else 146 { 147 results.put("step", "password"); 148 } 149 } 150 else if (StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(email)) 151 { 152 // If a token is provided, check it. 153 results.put("step", "password"); 154 checkToken(request, siteName, email, token, populationId, userDirectoryId, errors); 155 if (errors.isEmpty()) 156 { 157 results.put("status", "success"); 158 } 159 } 160 else if ("true".equals(signup)) 161 { 162 // A signup request has been made. 163 results.put("step", "temp-signup"); 164 temporarySignup(request, parameters, siteName, language, populationId, userDirectoryId, errors); 165 if (errors.isEmpty()) 166 { 167 results.put("status", "success"); 168 } 169 } 170 } 171 catch (Exception e) 172 { 173 errors.put("global", new I18nizableText("general-error")); 174 175 getLogger().error("An error occurred signing a user up.", e); 176 } 177 178 request.setAttribute("errors", errors); 179 180 return results; 181 } 182 183 /** 184 * Store a user's sign-up request. 185 * @param request the user request. 186 * @param parameters the action parameters. 187 * @param siteName the site name. 188 * @param language the language. 189 * @param populationId The id of the population 190 * @param userDirectoryId The id of the user directory of the population 191 * @param errors the Map to fill with errors to display to the user. 192 * @throws UserManagementException if an error occurs. 193 */ 194 protected void temporarySignup(Request request, Parameters parameters, String siteName, String language, String populationId, String userDirectoryId, Multimap<String, I18nizableText> errors) throws UserManagementException 195 { 196 String email = request.getParameter("email"); 197 198 Page page = _resolver.resolveById(request.getParameter("page-id")); 199 200 ModifiableUserDirectory userDirectory = (ModifiableUserDirectory) _userPopulationDAO.getUserPopulation(populationId).getUserDirectory(userDirectoryId); 201 202 Map<String, String> additionalFields = getAdditionalValues(request, userDirectory); 203 204 if (_pageHelper.isCaptchaRequired(page)) 205 { 206 String captchaValue = request.getParameter("captcha"); 207 String captchaKey = request.getParameter("captcha-key"); 208 209 // Validate the input 210 if (!CaptchaHelper.checkAndInvalidate(captchaKey, captchaValue)) 211 { 212 errors.put("captcha", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_FORM_ERROR_INVALID_CAPTCHA")); 213 } 214 } 215 216 if (_userSignupManager.userExists(email, siteName)) 217 { 218 errors.put("global", new I18nizableText("user-email-already-exists")); 219 } 220 221 Map<String, Errors> inputErrors = _userSignupManager.validate(siteName, email, additionalFields); 222 223 for (String field : inputErrors.keySet()) 224 { 225 errors.putAll(field, inputErrors.get(field).getErrors()); 226 } 227 228 if (errors.isEmpty()) 229 { 230 int status = _userSignupManager.temporarySignup(siteName, language, email, populationId, userDirectoryId); 231 232 if (status == UserSignupManager.SIGNUP_ERROR_TEMP_EMAIL_ALREADY_EXISTS) 233 { 234 errors.put("global", new I18nizableText("temp-email-already-exists")); 235 } 236 } 237 } 238 239 /** 240 * Reset a user's sign-up request. 241 * @param request the user request. 242 * @param siteName the site name. 243 * @param language the language. 244 * @param populationId The id of the population 245 * @param userDirectoryId The id of the user directory of the population 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 resetTempSignup(Request request, String siteName, String language, String populationId, String userDirectoryId, Multimap<String, I18nizableText> errors) throws UserManagementException 250 { 251 String email = request.getParameter("email"); 252 253 int status = _userSignupManager.resetTempSignup(siteName, language, email, populationId, userDirectoryId); 254 255 if (status == UserSignupManager.SIGNUP_RESET_ERROR_EMAIL_UNKNOWN) 256 { 257 errors.put("global", new I18nizableText("temp-email-unknown")); 258 } 259 } 260 261 /** 262 * Check that a token is valid. 263 * @param request the user request. 264 * @param siteName the site name. 265 * @param email the user e-mail. 266 * @param token the sign-up token that was sent to the user. 267 * @param populationId The id of the population 268 * @param userDirectoryId The id of the user directory of the population 269 * @param errors the Map to fill with errors to display to the user. 270 * @throws UserManagementException if an error occurs. 271 */ 272 protected void checkToken(Request request, String siteName, String email, String token, String populationId, String userDirectoryId, Multimap<String, I18nizableText> errors) throws UserManagementException 273 { 274 int tokenStatus = _userSignupManager.checkToken(siteName, email, token, populationId, userDirectoryId); 275 276 if (tokenStatus == UserSignupManager.SIGNUP_TOKEN_UNKNOWN) 277 { 278 errors.put("global", new I18nizableText("error-token-unknown")); 279 } 280 else if (tokenStatus == UserSignupManager.SIGNUP_TOKEN_EXPIRED) 281 { 282 errors.put("global", new I18nizableText("error-token-expired")); 283 } 284 } 285 286 /** 287 * Sign-up the user: create a real user from his temporary information. 288 * @param request the user request. 289 * @param parameters the action parameters. 290 * @param siteName the site name. 291 * @param language the current language. 292 * @param firstname the user firstname. 293 * @param lastname the user lastname. 294 * @param email the user e-mail. 295 * @param token the sign-up token that was sent to the user. 296 * @param populationId The id of the population 297 * @param userDirectoryId The id of the user directory of the population 298 * @param errors the Map to fill with errors to display to the user. 299 * @throws UserManagementException if an error occurs. 300 */ 301 protected void doSignup(Request request, Parameters parameters, String siteName, String language, String firstname, String lastname, String email, String token, String populationId, String userDirectoryId, Multimap<String, I18nizableText> errors) throws UserManagementException 302 { 303 String password = request.getParameter("password"); 304 String passwordConfirmation = request.getParameter("password-confirmation"); 305 String tos = request.getParameter("tos"); 306 String tosMode = parameters.getParameter("tos-mode", ""); 307 308 if (StringUtils.isBlank(password)) 309 { 310 errors.put("password", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_PASSWORD_EMPTY")); 311 } 312 else if (!password.equals(passwordConfirmation)) 313 { 314 errors.put("password", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_PASSWORD_CONFIRMATION_DOESNT_MATCH")); 315 } 316 317 if (StringUtils.isBlank(firstname)) 318 { 319 errors.put("firstname", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_FIRSTNAME_EMPTY")); 320 } 321 322 if (StringUtils.isBlank(lastname)) 323 { 324 errors.put("lastname", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_ERROR_LASTNAME_EMPTY")); 325 } 326 327 if (!"NONE".equals(tosMode) && !"true".equals(tos)) 328 { 329 errors.put("tos", new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_FORM_ERROR_TOS_NOT_CHECKED")); 330 } 331 332 if (errors.isEmpty()) 333 { 334 int status = _userSignupManager.signup(siteName, language, firstname, lastname, email, token, password, populationId, userDirectoryId, errors); 335 336 if (status == UserSignupManager.SIGNUP_TOKEN_UNKNOWN) 337 { 338 errors.put("global", new I18nizableText("error-token-unknown")); 339 } 340 else if (status == UserSignupManager.SIGNUP_TOKEN_EXPIRED) 341 { 342 errors.put("global", new I18nizableText("error-token-expired")); 343 } 344 } 345 } 346 347 /** 348 * Get FO user manager's custom field values. 349 * @param request the request. 350 * @param userDirectory the user directory 351 * @return the custom field values. 352 * @throws UserManagementException if an error occurs. 353 */ 354 protected Map<String, String> getAdditionalValues(Request request, ModifiableUserDirectory userDirectory) throws UserManagementException 355 { 356 Map<String, String> additionalFields = new HashMap<>(); 357 358 Collection< ? extends ModelItem> fields = userDirectory.getModelItems(); 359 360 for (ModelItem field : fields) 361 { 362 String fieldId = field.getName(); 363 if (!STANDARD_FIELDS.contains(fieldId)) 364 { 365 String value = request.getParameter(fieldId); 366 367 additionalFields.put(fieldId, value); 368 } 369 } 370 371 return additionalFields; 372 } 373}