From: Sagar Gopale Date: Wed, 8 Jul 2026 14:48:13 +0000 (+0530) Subject: mgr/dashboard: add validation for username X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=cec4a14faa203038f549d6174dd1b674adea4a18;p=ceph.git mgr/dashboard: add validation for username Signed-off-by: Sagar Gopale --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html index 409b84d1f9a..e0e28e9b234 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.html @@ -20,6 +20,8 @@ The username already exists. + + Username can only contain letters, numbers, '.', '_', '@', '+' or '-'. + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts index 850dff1c5a7..55bb829a483 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.spec.ts @@ -103,6 +103,12 @@ describe('UserFormComponent', () => { 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'); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts index 2f33440f2e9..12fb843692d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-form/user-form.component.ts @@ -29,6 +29,8 @@ import { UserFormMode } from './user-form-mode.enum'; 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', @@ -88,7 +90,7 @@ export class UserFormComponent extends CdForm implements OnInit { { username: [ '', - [Validators.required], + [Validators.required, Validators.pattern(DASHBOARD_USERNAME_PATTERN)], [CdValidators.unique(this.userService.validateUserName, this.userService)] ], name: [''], diff --git a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts index 44a8ba82583..44b2e6a7a38 100755 --- a/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/core/auth/user-list/user-list.component.ts @@ -69,7 +69,8 @@ export class UserListComponent implements OnInit { 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 = { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.spec.ts index ba038a72553..8a634cbeb67 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.spec.ts @@ -46,6 +46,12 @@ describe('UserService', () => { 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'; @@ -65,6 +71,12 @@ describe('UserService', () => { 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'); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.ts index d432e318471..dc2792a3281 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/user.service.ts @@ -17,7 +17,7 @@ export class UserService { } delete(username: string) { - return this.http.delete(`api/user/${username}`); + return this.http.delete(`api/user/${encodeURIComponent(username)}`); } get(username: string) { @@ -29,14 +29,14 @@ export class UserService { } 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 });