<cds-text-label
labelInputID="username"
cdRequiredField="Username"
+ helperText="Username can only contain letters, numbers, '.', '_', '@', '+' or '-'."
+ i18n-helperText
[invalid]="!userForm.controls.username.valid && userForm.controls.username.dirty"
[invalidText]="usernameError"
i18n
<span *ngIf="userForm.showError('username', formDir, 'notUnique')">
<ng-container i18n> The username already exists. </ng-container>
</span>
+ <span
+ *ngIf="userForm.showError('username', formDir, 'pattern')"
+ i18n
+ >
+ Username can only contain letters, numbers, '.', '_', '@', '+' or '-'.
+ </span>
</ng-template>
</div>
<!-- Password -->
formHelper.expectValidChange('username', 'user1');
});
+ it('should reject invalid usernames', () => {
+ formHelper.expectErrorChange('username', '..', 'pattern');
+ formHelper.expectErrorChange('username', '??', 'pattern');
+ formHelper.expectErrorChange('username', 'user/name', 'pattern');
+ });
+
it('should validate password match', () => {
formHelper.setValue('password', 'aaa');
formHelper.expectErrorChange('confirmpassword', 'bbb', 'match');
import { UserFormRoleModel } from './user-form-role.model';
import { UserFormModel } from './user-form.model';
+const DASHBOARD_USERNAME_PATTERN = /^(?!\.+$)[a-zA-Z0-9._@+-]+$/;
+
@Component({
selector: 'cd-user-form',
templateUrl: './user-form.component.html',
{
username: [
'',
- [Validators.required],
+ [Validators.required, Validators.pattern(DASHBOARD_USERNAME_PATTERN)],
[CdValidators.unique(this.userService.validateUserName, this.userService)]
],
name: [''],
permission: 'update',
icon: Icons.edit,
routerLink: () =>
- this.selection.first() && this.urlBuilder.getEdit(this.selection.first().username),
+ this.selection.first() &&
+ this.urlBuilder.getEdit(encodeURIComponent(this.selection.first().username)),
name: this.actionLabels.EDIT
};
const deleteAction: CdTableAction = {
expect(req.request.method).toBe('DELETE');
});
+ it('should URL-encode username on delete', () => {
+ service.delete('??').subscribe();
+ const req = httpTesting.expectOne('api/user/%3F%3F');
+ expect(req.request.method).toBe('DELETE');
+ });
+
it('should call update', () => {
const user = new UserFormModel();
user.username = 'user0';
expect(req.request.method).toBe('GET');
});
+ it('should URL-encode username on get', () => {
+ service.get('??').subscribe();
+ const req = httpTesting.expectOne('api/user/%3F%3F');
+ expect(req.request.method).toBe('GET');
+ });
+
it('should call list', () => {
service.list().subscribe();
const req = httpTesting.expectOne('api/user');
}
delete(username: string) {
- return this.http.delete(`api/user/${username}`);
+ return this.http.delete(`api/user/${encodeURIComponent(username)}`);
}
get(username: string) {
}
update(user: UserFormModel) {
- return this.http.put(`api/user/${user.username}`, user);
+ return this.http.put(`api/user/${encodeURIComponent(user.username)}`, user);
}
changePassword(username: string, oldPassword: string, newPassword: string) {
// Note, the specified user MUST be logged in to be able to change
// the password. The backend ensures that the password of another
// user can not be changed, otherwise an error will be thrown.
- return this.http.post(`api/user/${username}/change_password`, {
+ return this.http.post(`api/user/${encodeURIComponent(username)}/change_password`, {
old_password: oldPassword,
new_password: newPassword
});