Skip to content

Commit be7ceaa

Browse files
fix: allow users to set password first time (RocketChat#36627)
Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
1 parent 7a3a5d5 commit be7ceaa

5 files changed

Lines changed: 118 additions & 5 deletions

File tree

.changeset/thick-ravens-flow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@rocket.chat/meteor": patch
3+
---
4+
5+
fixes an issue where using `/v1/users.updateOwnBasicInfo`, the user was not be able to set the password (not change), even when required

apps/meteor/app/lib/server/functions/saveUser/validateUserEditing.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export async function validateUserEditing(userId: IUser['_id'], userData: Update
4545
if (
4646
isEditingField(user.username, userData.username) &&
4747
!settings.get('Accounts_AllowUsernameChange') &&
48-
(!canEditOtherUserInfo || (editingMyself && user.username))
48+
(editingMyself ? user.username : !canEditOtherUserInfo)
4949
) {
5050
throw new MeteorError('error-action-not-allowed', 'Edit username is not allowed', {
5151
method: 'insertOrUpdateUser',
@@ -87,7 +87,11 @@ export async function validateUserEditing(userId: IUser['_id'], userData: Update
8787
});
8888
}
8989

90-
if (userData.password && !settings.get('Accounts_AllowPasswordChange') && (!canEditOtherUserPassword || editingMyself)) {
90+
if (
91+
userData.password &&
92+
!settings.get('Accounts_AllowPasswordChange') &&
93+
(editingMyself ? user.services?.password || !user.requirePasswordChange : !canEditOtherUserPassword)
94+
) {
9195
throw new MeteorError('error-action-not-allowed', 'Edit user password is not allowed', {
9296
method: 'insertOrUpdateUser',
9397
action: 'Update_user',

apps/meteor/tests/e2e/containers/saml/config/simplesamlphp/authsources.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,17 @@
3737
'email' => 'samluser4@example.com',
3838
'role' => 'saml-role',
3939
),
40+
'samlusernoname:password' => array(
41+
'uid' => array('5'),
42+
'cn' => 'Saml User No Username',
43+
'eduPersonAffiliation' => array('group2'),
44+
'email' => 'samlusernoname@example.com',
45+
),
46+
'samlusernoname2:password' => array(
47+
'uid' => array('6'),
48+
'cn' => 'Saml User No Username 2',
49+
'eduPersonAffiliation' => array('group2'),
50+
'email' => 'samlusernoname2@example.com',
51+
),
4052
),
4153
);

apps/meteor/tests/e2e/fixtures/userStates.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ export const Users = {
125125
samluser1: generateContext('samluser1'),
126126
samluser2: generateContext('samluser2'),
127127
samluser4: generateContext('samluser4'),
128+
samlusernoname: generateContext('custom_saml_username'),
129+
samlusernoname2: generateContext('custom_saml_username2'),
128130
userForSamlMerge: generateContext('user_for_saml_merge'),
129131
userForSamlMerge2: generateContext('user_for_saml_merge2'),
130132
admin: generateContext('rocketchat.internal.admin.test'),

apps/meteor/tests/e2e/saml.spec.ts

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,19 @@ const resetTestData = async ({ api, cleanupOnly = false }: { api?: any; cleanupO
2525
// This is needed because those tests will modify this data and running them a second time would trigger different code paths
2626
const connection = await MongoClient.connect(constants.URL_MONGODB);
2727

28-
const usernamesToDelete = [Users.userForSamlMerge, Users.userForSamlMerge2, Users.samluser1, Users.samluser2, Users.samluser4].map(
29-
({ data: { username } }) => username,
30-
);
28+
const usernamesToDelete = [
29+
...[
30+
Users.userForSamlMerge,
31+
Users.userForSamlMerge2,
32+
Users.samluser1,
33+
Users.samluser2,
34+
Users.samluser4,
35+
Users.samlusernoname,
36+
Users.samlusernoname2,
37+
].map(({ data: { username } }) => username),
38+
'custom_saml_username',
39+
'custom_saml_username2',
40+
];
3141
await connection
3242
.db()
3343
.collection('users')
@@ -50,6 +60,7 @@ const resetTestData = async ({ api, cleanupOnly = false }: { api?: any; cleanupO
5060

5161
const settings = [
5262
{ _id: 'Accounts_AllowAnonymousRead', value: false },
63+
{ _id: 'Accounts_AllowUsernameChange', value: true },
5364
{ _id: 'SAML_Custom_Default_logout_behaviour', value: 'SAML' },
5465
{ _id: 'SAML_Custom_Default_immutable_property', value: 'EMail' },
5566
{ _id: 'SAML_Custom_Default_mail_overwrite', value: false },
@@ -229,6 +240,27 @@ test.describe('SAML', () => {
229240
});
230241
};
231242

243+
const doLoginStepWithUsernameSelection = async (page: Page, username: string) => {
244+
await test.step('expect successful login without username to show username selection screen', async () => {
245+
await poRegistration.btnLoginWithSaml.click();
246+
// Redirect to Idp
247+
await expect(page).toHaveURL(/.*\/simplesaml\/module.php\/core\/loginuserpass.php.*/);
248+
249+
// Fill username and password
250+
await page.getByLabel('Username').fill(username);
251+
await page.getByLabel('Password').fill('password');
252+
await page.locator('role=button[name="Login"]').click();
253+
254+
// Should redirect to username selection screen
255+
await expect(poRegistration.username).toBeVisible();
256+
});
257+
};
258+
259+
const doUsernameSelection = async (customUsername: string) => {
260+
await poRegistration.username.fill(customUsername);
261+
await poRegistration.btnRegisterConfirmUsername.click();
262+
};
263+
232264
const doLogoutStep = async (page: Page) => {
233265
await test.step('logout', async () => {
234266
await page.getByRole('button', { name: 'User menu' }).click();
@@ -465,6 +497,64 @@ test.describe('SAML', () => {
465497
await page2.close();
466498
});
467499

500+
test('Login - User without username can set initial username during first login', async ({ page, api }) => {
501+
await doLoginStepWithUsernameSelection(page, 'samlusernoname2');
502+
503+
await test.step('expect to be redirected to the username selection page and set the username', async () => {
504+
await expect(poRegistration.btnRegisterConfirmUsername).toBeVisible();
505+
await doUsernameSelection('custom_saml_username2');
506+
});
507+
508+
await test.step('expect to be redirected to the homepage after succesful login', async () => {
509+
await expect(page).toHaveURL('/home');
510+
await expect(page.getByRole('button', { name: 'User menu' })).toBeVisible();
511+
});
512+
513+
await test.step('expect user data to have been created with custom username', async () => {
514+
const user = await getUserInfo(api, 'custom_saml_username2');
515+
516+
expect(user).toBeDefined();
517+
expect(user?.username).toBe('custom_saml_username2');
518+
expect(user?.name).toBe('Saml User No Username 2');
519+
expect(user?.emails).toBeDefined();
520+
expect(user?.emails?.[0].address).toBe('samlusernoname2@example.com');
521+
});
522+
});
523+
524+
test.describe('Login - Username selection when allow username change is disabled', () => {
525+
test.beforeAll(async ({ api }) => {
526+
await expect((await setSettingValueById(api, 'Accounts_AllowUsernameChange', false)).status()).toBe(200);
527+
});
528+
529+
test.afterAll(async ({ api }) => {
530+
await expect((await setSettingValueById(api, 'Accounts_AllowUsernameChange', true)).status()).toBe(200);
531+
});
532+
533+
test('User without username can set initial username when username changes are disabled', async ({ page, api }) => {
534+
await doLoginStepWithUsernameSelection(page, 'samlusernoname');
535+
536+
await test.step('expect to be redirected to the username selection page and set the username', async () => {
537+
await expect(poRegistration.btnRegisterConfirmUsername).toBeVisible();
538+
await doUsernameSelection('custom_saml_username');
539+
});
540+
541+
await test.step('expect to be redirected to the homepage after succesful login', async () => {
542+
await expect(page).toHaveURL('/home');
543+
await expect(page.getByRole('button', { name: 'User menu' })).toBeVisible();
544+
});
545+
546+
await test.step('expect user data to have been created with the custom username', async () => {
547+
const user = await getUserInfo(api, 'custom_saml_username');
548+
549+
expect(user).toBeDefined();
550+
expect(user?.username).toBe('custom_saml_username');
551+
expect(user?.name).toBe('Saml User No Username');
552+
expect(user?.emails).toBeDefined();
553+
expect(user?.emails?.[0].address).toBe('samlusernoname@example.com');
554+
});
555+
});
556+
});
557+
468558
test.fixme('User Merge - By Custom Identifier', async () => {
469559
// Test user merge with a custom identifier configured in the fieldmap
470560
});

0 commit comments

Comments
 (0)