mirror of
https://github.com/dani-garcia/vaultwarden.wiki.git
synced 2026-07-20 08:13:33 +00:00
Admin password recovery endpoint change (#7270)
* Admin password recovery endpoint change * Use default to keep compatibility --------- Co-authored-by: Timshel <timshel@users.noreply.github.com>
This commit is contained in:
@@ -40,6 +40,16 @@ test('Invite users', async ({ page }) => {
|
|||||||
await createAccount(test, page, users.user1, mail1Buffer);
|
await createAccount(test, page, users.user1, mail1Buffer);
|
||||||
|
|
||||||
await orgs.create(test, page, 'Test');
|
await orgs.create(test, page, 'Test');
|
||||||
|
|
||||||
|
await test.step(`Set account recovery`, async () => {
|
||||||
|
await orgs.policies(test, page, 'Test');
|
||||||
|
await page.getByRole('button', { name: 'Account recovery' }).click();
|
||||||
|
await page.getByRole('checkbox', { name: 'Turn on' }).check();
|
||||||
|
await page.getByRole('checkbox', { name: 'Require new members' }).check();
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await utils.checkNotification(page, 'Edited policy Account recovery');
|
||||||
|
});
|
||||||
|
|
||||||
await orgs.members(test, page, 'Test');
|
await orgs.members(test, page, 'Test');
|
||||||
await orgs.invite(test, page, 'Test', users.user2.email);
|
await orgs.invite(test, page, 'Test', users.user2.email);
|
||||||
await orgs.invite(test, page, 'Test', users.user3.email, {
|
await orgs.invite(test, page, 'Test', users.user3.email, {
|
||||||
@@ -117,3 +127,27 @@ test('Organization is visible', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'vault: Test', exact: true }).click();
|
await page.getByRole('button', { name: 'vault: Test', exact: true }).click();
|
||||||
await expect(page.getByLabel('Filter: Default collection')).toBeVisible();
|
await expect(page.getByLabel('Filter: Default collection')).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Recover user password', async ({ page }) => {
|
||||||
|
await logUser(test, page, users.user1, mail1Buffer);
|
||||||
|
|
||||||
|
let newPassword = "TotoNewPassword";
|
||||||
|
|
||||||
|
await orgs.members(test, page, 'Test');
|
||||||
|
await test.step(`Rrcover ${users.user2.email}`, async () => {
|
||||||
|
await expect(page.getByRole('heading', { name: 'Members' })).toBeVisible();
|
||||||
|
await page.getByRole('row').filter({hasText: users.user2.email}).getByLabel('Options').click();
|
||||||
|
await page.getByRole('menuitem', { name: 'Recover account' }).click();
|
||||||
|
await page.getByRole('textbox', { name: 'New master password (required)', exact: true }).fill(newPassword);
|
||||||
|
await page.getByRole('textbox', { name: 'Confirm new master password (' }).fill(newPassword);
|
||||||
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
|
await utils.checkNotification(page, 'Password reset success');
|
||||||
|
});
|
||||||
|
|
||||||
|
let user2 = {
|
||||||
|
email: users.user2.email,
|
||||||
|
name: users.user2.name,
|
||||||
|
password: newPassword,
|
||||||
|
};
|
||||||
|
await logUser(test, page, user2, mail2Buffer);
|
||||||
|
});
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ pub fn routes() -> Vec<Route> {
|
|||||||
put_reset_password_enrollment,
|
put_reset_password_enrollment,
|
||||||
get_reset_password_details,
|
get_reset_password_details,
|
||||||
put_reset_password,
|
put_reset_password,
|
||||||
|
put_recover_account,
|
||||||
get_org_export,
|
get_org_export,
|
||||||
post_api_key,
|
post_api_key,
|
||||||
rotate_api_key,
|
rotate_api_key,
|
||||||
@@ -2875,9 +2876,14 @@ struct OrganizationUserResetPasswordEnrollmentRequest {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct OrganizationUserResetPasswordRequest {
|
struct OrganizationUserRecoverAccountRequest {
|
||||||
new_master_password_hash: String,
|
new_master_password_hash: String,
|
||||||
key: String,
|
key: String,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
reset_master_password: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
reset_two_factor: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upstream reports this is the renamed endpoint instead of `/keys`
|
// Upstream reports this is the renamed endpoint instead of `/keys`
|
||||||
@@ -2905,12 +2911,43 @@ async fn get_organization_keys(org_id: OrganizationId, headers: OrgMemberHeaders
|
|||||||
get_organization_public_key(org_id, headers, conn).await
|
get_organization_public_key(org_id, headers, conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will allow to reset 2FA too
|
||||||
|
// https://github.com/bitwarden/clients/blob/web-v2026.4.2/libs/admin-console/src/common/organization-user/models/requests/organization-user-reset-password.request.ts
|
||||||
|
#[put("/organizations/<org_id>/users/<member_id>/recover-account", data = "<data>")]
|
||||||
|
async fn put_recover_account(
|
||||||
|
org_id: OrganizationId,
|
||||||
|
member_id: MembershipId,
|
||||||
|
headers: AdminHeaders,
|
||||||
|
data: Json<OrganizationUserRecoverAccountRequest>,
|
||||||
|
conn: DbConn,
|
||||||
|
nt: Notify<'_>,
|
||||||
|
) -> EmptyResult {
|
||||||
|
let req = data.into_inner();
|
||||||
|
if req.reset_master_password && !req.reset_two_factor {
|
||||||
|
recover_account(org_id, member_id, headers, req, conn, nt).await
|
||||||
|
} else {
|
||||||
|
err!("Unsupported operation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated since `v2026.4.2`
|
||||||
#[put("/organizations/<org_id>/users/<member_id>/reset-password", data = "<data>")]
|
#[put("/organizations/<org_id>/users/<member_id>/reset-password", data = "<data>")]
|
||||||
async fn put_reset_password(
|
async fn put_reset_password(
|
||||||
org_id: OrganizationId,
|
org_id: OrganizationId,
|
||||||
member_id: MembershipId,
|
member_id: MembershipId,
|
||||||
headers: AdminHeaders,
|
headers: AdminHeaders,
|
||||||
data: Json<OrganizationUserResetPasswordRequest>,
|
data: Json<OrganizationUserRecoverAccountRequest>,
|
||||||
|
conn: DbConn,
|
||||||
|
nt: Notify<'_>,
|
||||||
|
) -> EmptyResult {
|
||||||
|
recover_account(org_id, member_id, headers, data.into_inner(), conn, nt).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn recover_account(
|
||||||
|
org_id: OrganizationId,
|
||||||
|
member_id: MembershipId,
|
||||||
|
headers: AdminHeaders,
|
||||||
|
reset_request: OrganizationUserRecoverAccountRequest,
|
||||||
conn: DbConn,
|
conn: DbConn,
|
||||||
nt: Notify<'_>,
|
nt: Notify<'_>,
|
||||||
) -> EmptyResult {
|
) -> EmptyResult {
|
||||||
@@ -2944,8 +2981,6 @@ async fn put_reset_password(
|
|||||||
err!(format!("Error sending user reset password email: {e:#?}"));
|
err!(format!("Error sending user reset password email: {e:#?}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let reset_request = data.into_inner();
|
|
||||||
|
|
||||||
let mut user = user;
|
let mut user = user;
|
||||||
user.set_password(reset_request.new_master_password_hash.as_str(), Some(reset_request.key), true, None, &conn)
|
user.set_password(reset_request.new_master_password_hash.as_str(), Some(reset_request.key), true, None, &conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user