fix: display name validation (server) (#22927)

## Description

> This PR adds a validation regex for display name, it allows for
Accented characters such as (ä ö ü è ß) etc, and alphanumeric with some
special characters dot (.), apostrophe ('), hyphen (-) and spaces.
> It also allows for chinese characters, eg. 황현미

Fixes #22578

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

- JUnit

### Test Plan
> Add Testsmith test cases links that relate to this PR

### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)

## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag

### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test

---------

Co-authored-by: Anand Srinivasan <anand.a.srinivasan@outlook.com>
(cherry picked from commit 3915334dd9)
This commit is contained in:
Nilansh Bansal 2023-05-04 10:49:45 +05:30 committed by somangshu
parent 3cb8d21c1b
commit 6f69f1935c
4 changed files with 63 additions and 8 deletions

View File

@ -171,6 +171,9 @@ export const USERS_HAVE_ACCESS_TO_ONLY_THIS_APP = () =>
"Users will only have access to this application";
export const NO_USERS_INVITED = () => "You haven't invited any users yet";
export const UPDATE_USER_DETAILS_FAILED = () =>
"Unable to update user details.";
export const CREATE_PASSWORD_RESET_SUCCESS = () => `Your password has been set`;
export const CREATE_PASSWORD_RESET_SUCCESS_LOGIN_LINK = () => `Login`;

View File

@ -21,6 +21,7 @@ import UserApi from "@appsmith/api/UserApi";
import { AUTH_LOGIN_URL, SETUP } from "constants/routes";
import history from "utils/history";
import type { ApiResponse } from "api/ApiResponses";
import type { ErrorActionPayload } from "sagas/ErrorSagas";
import {
validateResponse,
getResponseErrorMessage,
@ -72,6 +73,8 @@ import type { SegmentState } from "reducers/uiReducers/analyticsReducer";
import type FeatureFlags from "entities/FeatureFlags";
import UsagePulse from "usagePulse";
import { isAirgapped } from "@appsmith/utils/airgapHelpers";
import { UPDATE_USER_DETAILS_FAILED } from "ce/constants/messages";
import { createMessage } from "design-system-old/build/constants/messages";
export function* createUserSaga(
action: ReduxActionWithPromise<CreateUserRequest>,
@ -382,9 +385,16 @@ export function* updateUserDetailsSaga(action: ReduxAction<UpdateUserRequest>) {
});
}
} catch (error) {
const payload: ErrorActionPayload = {
show: true,
error: {
message:
(error as Error).message ?? createMessage(UPDATE_USER_DETAILS_FAILED),
},
};
yield put({
type: ReduxActionErrorTypes.UPDATE_USER_DETAILS_ERROR,
payload: (error as Error).message,
payload,
});
}
}

View File

@ -69,6 +69,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import static com.appsmith.server.acl.AclPermission.MANAGE_USERS;
import static com.appsmith.server.helpers.ValidationUtils.LOGIN_PASSWORD_MAX_LENGTH;
@ -100,6 +101,7 @@ public class UserServiceCEImpl extends BaseService<UserRepository, User, String>
private static final String FORGOT_PASSWORD_CLIENT_URL_FORMAT = "%s/user/resetPassword?token=%s";
private static final String INVITE_USER_CLIENT_URL_FORMAT = "%s/user/signup?email=%s";
public static final String INVITE_USER_EMAIL_TEMPLATE = "email/inviteUserTemplate.html";
private static final Pattern ALLOWED_ACCENTED_CHARACTERS_PATTERN = Pattern.compile("^[\\p{L} 0-9 .\'\\-]+$");
@Autowired
public UserServiceCEImpl(Scheduler scheduler,
@ -659,6 +661,14 @@ public class UserServiceCEImpl extends BaseService<UserRepository, User, String>
return Flux.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION));
}
private boolean validateName(String name){
/*
Regex allows for Accented characters and alphanumeric with some special characters dot (.), apostrophe ('),
hyphen (-) and spaces
*/
return ALLOWED_ACCENTED_CHARACTERS_PATTERN.matcher(name).matches();
}
@Override
public Mono<User> updateCurrentUser(final UserUpdateDTO allUpdates, ServerWebExchange exchange) {
List<Mono<Void>> monos = new ArrayList<>();
@ -668,7 +678,12 @@ public class UserServiceCEImpl extends BaseService<UserRepository, User, String>
if (allUpdates.hasUserUpdates()) {
final User updates = new User();
updates.setName(allUpdates.getName());
String inputName = allUpdates.getName();
boolean isValidName = validateName(inputName);
if (!isValidName){
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.NAME));
}
updates.setName(inputName);
updatedUserMono = sessionUserService.getCurrentUser()
.flatMap(user ->
update(user.getEmail(), updates, fieldName(QUser.user.email))

View File

@ -415,12 +415,39 @@ public class UserServiceTest {
@WithUserDetails(value = "api_user")
public void updateNameOfUser() {
UserUpdateDTO updateUser = new UserUpdateDTO();
updateUser.setName("New name of api_user");
updateUser.setName("New name of api user");
StepVerifier.create(userService.updateCurrentUser(updateUser, null))
.assertNext(user -> {
assertNotNull(user);
assertThat(user.getEmail()).isEqualTo("api_user");
assertThat(user.getName()).isEqualTo("New name of api_user");
assertEquals("api_user", user.getEmail());
assertEquals("New name of api user", user.getName());
})
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void updateNameOfUser_WithNotAllowedSpecialCharacter_InvalidName() {
UserUpdateDTO updateUser = new UserUpdateDTO();
updateUser.setName("invalid name@symbol");
StepVerifier.create(userService.updateCurrentUser(updateUser, null))
.expectErrorMatches(throwable ->
throwable instanceof AppsmithException
&&
throwable.getMessage().contains(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.NAME))
)
.verify();
}
@Test
@WithUserDetails(value = "api_user")
public void updateNameOfUser_WithAccentedCharacters_IsValid() {
UserUpdateDTO updateUser = new UserUpdateDTO();
updateUser.setName("ä ö ü è ß Test . '- ðƒ 你好 123'");
StepVerifier.create(userService.updateCurrentUser(updateUser, null))
.assertNext(user -> {
assertNotNull(user);
assertEquals("ä ö ü è ß Test . '- ðƒ 你好 123'", user.getName());
})
.verifyComplete();
}
@ -491,9 +518,9 @@ public class UserServiceTest {
final UserData userData = tuple.getT2();
assertNotNull(user);
assertNotNull(userData);
assertThat(user.getName()).isEqualTo("New name of user here");
assertThat(userData.getRole()).isEqualTo("New role of user");
assertThat(userData.getUseCase()).isEqualTo("New use case");
assertEquals("New name of user here", user.getName());
assertEquals("New role of user", userData.getRole());
assertEquals("New use case", userData.getUseCase());
})
.verifyComplete();
}