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

Refactor user creation logic for clarity #674

Merged
merged 3 commits into from
Feb 11, 2025
Merged
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
Expand Up @@ -975,31 +975,29 @@ public void logUserOut(final HttpServletRequest request, final HttpServletRespon
*/
public RegisteredUserDTO createUserObjectAndSession(final HttpServletRequest request,
final HttpServletResponse response, final RegisteredUser user, final String newPassword,
final boolean rememberMe, final Set<AuthenticationCaveat> sessionCaveats) throws InvalidPasswordException,
MissingRequiredFieldException, SegueDatabaseException,
EmailMustBeVerifiedException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidNameException, UnknownCountryCodeException {
Validate.isTrue(user.getId() == null,
"When creating a new user the user id must not be set.");

if (this.findUserByEmail(user.getEmail()) != null) {
throw new DuplicateAccountException();
}
final boolean rememberMe, final Set<AuthenticationCaveat> sessionCaveats)
throws InvalidPasswordException, MissingRequiredFieldException, SegueDatabaseException, EmailMustBeVerifiedException,
InvalidKeySpecException, NoSuchAlgorithmException, InvalidNameException, UnknownCountryCodeException {

// Ensure nobody registers with Isaac email addresses. Users can change emails to restricted ones by verifying them, however.
if (null != restrictedSignupEmailRegex && restrictedSignupEmailRegex.matcher(user.getEmail()).find()) {
log.warn("User attempted to register with Isaac email address '" + user.getEmail() + "'!");
throw new EmailMustBeVerifiedException("You cannot register with an Isaac email address.");
}
Validate.isTrue(user.getId() == null, "When creating a new user the user id must not be set.");

RegisteredUser userToSave;
MapperFacade mapper = this.dtoMapper;
IPasswordAuthenticator authenticator = (IPasswordAuthenticator) this.registeredAuthProviders.get(AuthenticationProvider.SEGUE);

// We want to map to DTO first to make sure that the user cannot
// change fields that aren't exposed to them
// We want to map via a DTO first to make sure that the user cannot set fields that aren't exposed to them:
RegisteredUserDTO userDtoForNewUser = mapper.map(user, RegisteredUserDTO.class);
RegisteredUser userToSave = mapper.map(userDtoForNewUser, RegisteredUser.class);

// This is a new registration
userToSave = mapper.map(userDtoForNewUser, RegisteredUser.class);
// ----- Validate the user object and details: -----

// Ensure password is valid.
authenticator.ensureValidPassword(newPassword);

// Ensure nobody registers with Isaac email addresses. Users can change emails to restricted ones by verifying them, however.
if (null != restrictedSignupEmailRegex && restrictedSignupEmailRegex.matcher(userToSave.getEmail()).find()) {
log.warn("User attempted to register with Isaac email address '{}'!", user.getEmail());
Dismissed Show dismissed Hide dismissed
throw new EmailMustBeVerifiedException("You cannot register with an Isaac email address.");
}

// Set defaults
// keep teacher role if requested and direct teacher signup allowed
Expand All @@ -1016,16 +1014,15 @@ public RegisteredUserDTO createUserObjectAndSession(final HttpServletRequest req
userToSave.setLastUpdated(new Date());

// Before save we should validate the user for mandatory fields.
if (!this.isUserValid(userToSave)) {
if (!isUserEmailValid(userToSave.getEmail())) {
throw new MissingRequiredFieldException("The email address provided is invalid.");
}

// validate names
if (!isUserNameValid(user.getGivenName())) {
if (!isUserNameValid(userToSave.getGivenName())) {
throw new InvalidNameException("The given name provided is an invalid length or contains forbidden characters.");
}

if (!isUserNameValid(user.getFamilyName())) {
if (!isUserNameValid(userToSave.getFamilyName())) {
throw new InvalidNameException("The family name provided is an invalid length or contains forbidden characters.");
}

Expand All @@ -1034,18 +1031,20 @@ public RegisteredUserDTO createUserObjectAndSession(final HttpServletRequest req
throw new UnknownCountryCodeException("The country provided is not known.");
}

IPasswordAuthenticator authenticator = (IPasswordAuthenticator) this.registeredAuthProviders
.get(AuthenticationProvider.SEGUE);
// Critically, and after other validation, check this is not a duplicate registration attempt:
if (this.findUserByEmail(userToSave.getEmail()) != null) {
throw new DuplicateAccountException();
}

authenticator.createEmailVerificationTokenForUser(userToSave, userToSave.getEmail());
// ----- Save the user object: -----

// FIXME: Before creating the user object, ensure password is valid. This should really be in a transaction.
authenticator.ensureValidPassword(newPassword);
// Create and set a verification token:
authenticator.createEmailVerificationTokenForUser(userToSave, userToSave.getEmail());

// save the user to get the userId
// Save the user to get the userId
RegisteredUser userToReturn = this.database.createOrUpdateUser(userToSave);

// create password for the user
// Save password for the user
authenticator.setOrChangeUsersPassword(userToReturn, newPassword);

// send an email confirmation and set up verification
Expand Down Expand Up @@ -1163,7 +1162,7 @@ public RegisteredUserDTO updateUserObject(final RegisteredUser updatedUser, fina
// Before save we should validate the user for mandatory fields.
// Doing this before the email change code is necessary to ensure that (a) users cannot try and change to an
// invalid email, and (b) that users with an invalid email can change their email to a valid one!
if (!this.isUserValid(userToSave)) {
if (!isUserEmailValid(userToSave.getEmail())) {
throw new MissingRequiredFieldException("The email address provided is invalid.");
}

Expand Down Expand Up @@ -1854,14 +1853,14 @@ private RegisteredUser registerUserWithFederatedProvider(final AuthenticationPro
}

/**
* IsUserValid This function will check that the user object is valid.
* This function will check that the user's email address is valid.
*
* @param userToValidate - the user to validate.
* @param email - the user email to validate.
* @return true if it meets the internal storage requirements, false if not.
*/
private boolean isUserValid(final RegisteredUser userToValidate) {
return userToValidate.getEmail() != null && !userToValidate.getEmail().isEmpty()
&& userToValidate.getEmail().matches(".*(@.+\\.[^.]+|-(facebook|google|twitter)$)");
private static boolean isUserEmailValid(final String email) {
return email != null && !email.isEmpty()
&& email.matches(".*(@.+\\.[^.]+|-(facebook|google|twitter)$)");
}

/**
Expand All @@ -1870,7 +1869,7 @@ private boolean isUserValid(final RegisteredUser userToValidate) {
* @param name - the name to validate.
* @return true if the name is valid, false otherwise.
*/
public static final boolean isUserNameValid(final String name) {
public static boolean isUserNameValid(final String name) {
return null != name && name.length() <= USER_NAME_MAX_LENGTH && !USER_NAME_FORBIDDEN_CHARS_REGEX.matcher(name).find()
&& !name.isEmpty();
}
Expand Down
Loading