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