Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register user / allow to configured allowed email domains #8186

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2001-2023 Food and Agriculture Organization of the
* Copyright (C) 2001-2024 Food and Agriculture Organization of the
* United Nations (FAO-UN), United Nations World Food Programme (WFP)
* and United Nations Environment Programme (UNEP)
*
Expand Down Expand Up @@ -83,6 +83,7 @@ public class Settings {
public static final String SYSTEM_CSW_CAPABILITY_RECORD_UUID = "system/csw/capabilityRecordUuid";
public static final String SYSTEM_CSW_METADATA_PUBLIC = "system/csw/metadataPublic";
public static final String SYSTEM_USERSELFREGISTRATION_ENABLE = "system/userSelfRegistration/enable";
public static final String SYSTEM_USERSELFREGISTRATION_EMAIL_DOMAINS = "system/userSelfRegistration/domainsAllowed";
public static final String SYSTEM_USERSELFREGISTRATION_RECAPTCHA_ENABLE = "system/userSelfRegistration/recaptcha/enable";
public static final String SYSTEM_USERSELFREGISTRATION_RECAPTCHA_PUBLICKEY = "system/userSelfRegistration/recaptcha/publickey";
public static final String SYSTEM_USERSELFREGISTRATION_RECAPTCHA_SECRETKEY = "system/userSelfRegistration/recaptcha/secretkey";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,16 @@ user_watchlist_message=The following records have been updated:\n\
</div>\n\
</div>
self_registration_disabled=User self-registration is disabled
self_registration_no_valid_mail=The email address is not allowed
recaptcha_not_valid=Recaptcha is not valid
metadata.title.createdFromTemplate=Copy of template %s created at %s
metadata.title.createdFromRecord=Copy of record %s created at %s
username.field.required=Username is required
password.field.length=Password size should be between {min} and {max} characters
password.field.invalid=Password must contain at least 1 uppercase, 1 lowercase, 1 number and 1 symbol. Symbols include: `~!@#$%^&*()-_=+[]{}\\|;:'",.<>/?');
field.required.name=Name is required
field.required.email=Email address is required
field.notvalid.email=Email address is not valid
api.exception.forbidden=Access denied
api.exception.forbidden.description=Access is denied. To access, try again with a user containing more privileges.
api.exception.resourceNotFound=Resource not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,14 @@ user_watchlist_message=Les fiches suivantes ont \u00E9t\u00E9 mises \u00e0 jour
</div>\n\
</div>
self_registration_disabled=La cr\u00E9ation de compte par les utilisateurs est d\u00E9sactiv\u00E9e
self_registration_no_valid_mail=L''adresse email n''est pas autoris\u00E9e
recaptcha_not_valid=Recaptcha invalide
metadata.title.createdFromTemplate=Copie du mod\u00e8le %s cr\u00E9\u00E9e le %s
metadata.title.createdFromRecord=Copie de la fiche %s cr\u00E9\u00E9e le %s
username.field.required=Le nom d''utilisateur est requis
field.required.name=Nom est obligatoire
field.required.email=L''adresse mail est obligatoire
field.notvalid.email=L''adresse mail n''est pas valide
password.field.length=Le mot de passe doit contenir entre {min} et {max} caract\u00E8res
password.field.invalid=Le mot de passe doit contenir a minima 1 lettre majuscule, 1 minuscule, 1 chiffre et 1 symbole (ie. `~!@#$%^&*()-_=+[]{}\\|;:'",.<>/?'));
api.exception.forbidden=L''acc\u00E8s est refus\u00E9
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ See [Configuring Shibboleth](../managing-users-and-groups/authentication-mode.md

Enable the self registration form. See [User Self-Registration](../managing-users-and-groups/user-self-registration.md).

You can configure optionally re-Captcha, to protect you and your users from spam and abuse. And a list of email domains (separated by commas)
that can request an account. If not configured any email address is allowed.

## system/userFeedback

!!! warning "Deprecated"
Expand Down
50 changes: 22 additions & 28 deletions services/src/main/java/org/fao/geonet/api/users/RegisterApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.fao.geonet.api.tools.i18n.LanguageUtils;
import org.fao.geonet.api.users.model.UserRegisterDto;
import org.fao.geonet.api.users.recaptcha.RecaptchaChecker;
import org.fao.geonet.api.users.validation.UserRegisterDtoValidator;
import org.fao.geonet.domain.*;
import org.fao.geonet.kernel.security.SecurityProviderConfiguration;
import org.fao.geonet.kernel.setting.SettingManager;
Expand All @@ -46,7 +47,6 @@
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

Expand All @@ -68,6 +68,9 @@ public class RegisterApi {
@Autowired(required=false)
SecurityProviderConfiguration securityProviderConfiguration;

@Autowired
UserRepository userRepository;

@Autowired
GroupRepository groupRepository;

Expand Down Expand Up @@ -123,39 +126,30 @@ public ResponseEntity<String> registerUser(
}
}

// Validate the user registration
if (bindingResult.hasErrors()) {
List<ObjectError> errorList = bindingResult.getAllErrors();

StringBuilder sb = new StringBuilder();
Iterator<ObjectError> it = errorList.iterator();
while (it.hasNext()) {
sb.append(messages.getString(it.next().getDefaultMessage()));
if (it.hasNext()) {
sb.append(", ");
}
}

return new ResponseEntity<>(sb.toString(), HttpStatus.PRECONDITION_FAILED);
// Validate userDto data
UserRegisterDtoValidator userRegisterDtoValidator = new UserRegisterDtoValidator();
userRegisterDtoValidator.validate(userRegisterDto, bindingResult);
String errorMessage = ApiUtils.processRequestValidation(bindingResult, messages);
if (org.apache.commons.lang.StringUtils.isNotEmpty(errorMessage)) {
return new ResponseEntity<>(errorMessage, HttpStatus.PRECONDITION_FAILED);
}

final UserRepository userRepository = context.getBean(UserRepository.class);
if (userRepository.findOneByEmail(userRegisterDto.getEmail()) != null) {
return new ResponseEntity<>(String.format(
messages.getString("user_with_that_email_found"),
userRegisterDto.getEmail()
), HttpStatus.PRECONDITION_FAILED);
}

if (!userRepository.findByUsernameIgnoreCase(userRegisterDto.getEmail()).isEmpty()) {
// username is ignored and the email is used as username in selfregister
return new ResponseEntity<>(String.format(
messages.getString("user_with_that_username_found"),
userRegisterDto.getEmail()
), HttpStatus.PRECONDITION_FAILED);
String emailDomainsAllowed = settingManager.getValue(Settings.SYSTEM_USERSELFREGISTRATION_EMAIL_DOMAINS);
if (StringUtils.hasLength(emailDomainsAllowed)) {
List<String> emailDomainsAllowedList = Arrays.asList(emailDomainsAllowed.split(","));

String userEmailDomain = userRegisterDto.getEmail().split("@")[1];

if (!emailDomainsAllowedList.contains(userEmailDomain)) {
return new ResponseEntity<>(String.format(
messages.getString("self_registration_no_valid_mail")
), HttpStatus.PRECONDITION_FAILED);
}
}

User user = new User();

user.setName(userRegisterDto.getName());
user.setSurname(userRegisterDto.getSurname());
user.setOrganisation(userRegisterDto.getOrganisation());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2001-2024 Food and Agriculture Organization of the
* United Nations (FAO-UN), United Nations World Food Programme (WFP)
* and United Nations Environment Programme (UNEP)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
* Rome - Italy. email: [email protected]
*/

package org.fao.geonet.api.users.validation;

import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.api.users.model.UserRegisterDto;
import org.fao.geonet.constants.Params;
import org.fao.geonet.repository.UserRepository;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

/**
* Validator for UserRegisterDto.
*
*/
public class UserRegisterDtoValidator implements Validator {
private static final String OWASP_EMAIL_REGEX = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
private static final java.util.regex.Pattern OWASP_EMAIL_PATTERN = java.util.regex.Pattern.compile(OWASP_EMAIL_REGEX);

@Override
public boolean supports(Class<?> clazz) {
return UserRegisterDto.class.isAssignableFrom(clazz);
}

@Override
public void validate(Object target, Errors errors) {
UserRegisterDto userRegisterDto = (UserRegisterDto) target;

ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "field.required", Params.NAME
+ " is required");

ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "field.required", Params.EMAIL
+ " is required");

if (StringUtils.hasLength(userRegisterDto.getEmail()) && !OWASP_EMAIL_PATTERN.matcher(userRegisterDto.getEmail()).matches()) {
errors.rejectValue("email", "field.notvalid", "Email address is not valid");
}

UserRepository userRepository = ApplicationContextHolder.get().getBean(UserRepository.class);
if (userRepository.findOneByEmail(userRegisterDto.getEmail()) != null) {
errors.rejectValue("", "user_with_that_email_found", "A user with this email or username already exists.");
}

if (userRepository.findByUsernameIgnoreCase(userRegisterDto.getEmail()).size() != 0) {
errors.rejectValue("", "user_with_that_username_found", "A user with this email or username already exists.");
}
}
}
2 changes: 2 additions & 0 deletions web-ui/src/main/resources/catalog/locales/en-admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,8 @@
"system/userSelfRegistration": "User self-registration",
"system/userSelfRegistration/enable": "Enable self-registration",
"system/userSelfRegistration/enable-help": "When enabled, make sure a mail server is also configured.",
"system/userSelfRegistration/domainsAllowed": "Email domains allowed",
"system/userSelfRegistration/domainsAllowed-help": "Comma separated list of email domains that can request an account. If not configured, any email address is allowed.",
"system/userSelfRegistration/recaptcha/enable": "Enable re-captcha",
"system/userSelfRegistration/recaptcha/enable-help": "Enabling re-captcha will protect you and your users from spam and abuse. This is highly recommended when you enable feedback or self-registration. Create your re-captcha key on <a href='https://www.google.com/recaptcha/'>https://www.google.com/recaptcha/</a>",
"system/userSelfRegistration/recaptcha/publickey": "Re-captcha public key",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,16 @@ user_watchlist_message=The following records have been updated:\n\
</div>\n\
</div>
self_registration_disabled=User self-registration is disabled
self_registration_no_valid_mail=The email address is not allowed
recaptcha_not_valid=Recaptcha is not valid
metadata.title.createdFromTemplate=Copy of template %s created at %s
metadata.title.createdFromRecord=Copy of record %s created at %s
username.field.required=Username is required
password.field.length=Password size should be between {min} and {max} characters
password.field.invalid=Password must contain at least 1 uppercase, 1 lowercase, 1 number and 1 symbol. Symbols include: `~!@#$%^&*()-_=+[]{}\\|;:'",.<>/?');
field.required.name=Name is required
field.required.email=Email address is required
field.notvalid.email=Email address is not valid
api.exception.forbidden=Access denied
api.exception.forbidden.description=Access is denied. To access, try again with a user containing more privileges.
api.exception.resourceNotFound=Resource not found
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,16 @@ user_watchlist_message=Les fiches suivantes ont \u00E9t\u00E9 mises \u00e0 jour
</div>\n\
</div>
self_registration_disabled=La cr\u00E9ation de compte par les utilisateurs est d\u00E9sactiv\u00E9e
self_registration_no_valid_mail=L''adresse email n''est pas autoris\u00E9e
recaptcha_not_valid=Recaptcha invalide
metadata.title.createdFromTemplate=Copie du mod\u00e8le %s cr\u00E9\u00E9e le %s
metadata.title.createdFromRecord=Copie de la fiche %s cr\u00E9\u00E9e le %s
username.field.required=Le nom d''utilisateur est requis
password.field.length=Le mot de passe doit contenir entre {min} et {max} caract\u00E8res
password.field.invalid=Le mot de passe doit contenir a minima 1 lettre majuscule, 1 minuscule, 1 chiffre et 1 symbole (ie. `~!@#$%^&*()-_=+[]{}\\|;:'",.<>/?'));
field.required.name=Nom est obligatoire
field.required.email=L''adresse mail est obligatoire
field.notvalid.email=L''adresse mail n''est pas valide
api.exception.forbidden=L''acc\u00E8s est refus\u00E9
api.exception.forbidden.description=L''acc\u00E8s est refus\u00E9. Pour y acc\u00E9der, r\u00E9essayez avec un utilisateur disposant de plus de privil\u00E8ges.
api.exception.resourceNotFound=Ressource introuvable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,10 +726,11 @@ INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metada

INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/ui/defaultView', 'default', 0, 10100, 'n');


INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/userSelfRegistration/recaptcha/enable', 'false', 2, 1910, 'n');
INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/userSelfRegistration/recaptcha/publickey', '', 0, 1910, 'n');
INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/userSelfRegistration/recaptcha/secretkey', '', 0, 1910, 'y');

INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/userSelfRegistration/domainsAllowed', '', 0, 1911, 'y');

INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/publication/doi/doienabled', 'false', 2, 100191, 'n');
INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/publication/doi/doiurl', '', 0, 100192, 'n');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
UPDATE Settings SET value='4.4.6' WHERE name='system/platform/version';
UPDATE Settings SET value='SNAPSHOT' WHERE name='system/platform/subVersion';

INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('system/userSelfRegistration/domainsAllowed', '', 0, 1911, 'y');
Loading