1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 05:43:41 +00:00

Merge branch 'master' into feature/flexible-collections

This commit is contained in:
Thomas Rittson
2023-09-19 13:18:45 +10:00
committed by GitHub
62 changed files with 347 additions and 214 deletions

View File

@@ -1,40 +1,51 @@
<label class="environment-selector-btn">
<div class="environment-selector-btn">
{{ "loggingInOn" | i18n }}:
<a
<button
type="button"
(click)="toggle(null)"
cdkOverlayOrigin
#trigger="cdkOverlayOrigin"
aria-haspopup="menu"
aria-haspopup="dialog"
aria-controls="cdk-overlay-container"
[ngSwitch]="selectedEnvironment"
>
<label *ngSwitchCase="ServerEnvironmentType.US" class="text-primary">{{
<span *ngSwitchCase="ServerEnvironmentType.US" class="text-primary">{{
"usDomain" | i18n
}}</label>
<label *ngSwitchCase="ServerEnvironmentType.EU" class="text-primary">{{
}}</span>
<span *ngSwitchCase="ServerEnvironmentType.EU" class="text-primary">{{
"euDomain" | i18n
}}</label>
<label *ngSwitchCase="ServerEnvironmentType.SelfHosted" class="text-primary">{{
}}</span>
<span *ngSwitchCase="ServerEnvironmentType.SelfHosted" class="text-primary">{{
"selfHostedServer" | i18n
}}</label>
}}</span>
<i class="bwi bwi-fw bwi-sm bwi-angle-down" aria-hidden="true"></i>
</a>
</label>
</button>
</div>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="trigger"
(backdropClick)="close()"
(detach)="close()"
[cdkConnectedOverlayOpen]="isOpen"
[cdkConnectedOverlayPositions]="overlayPosition"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayBackdropClass]="'cdk-overlay-transparent-backdrop'"
(backdropClick)="isOpen = false"
(detach)="close()"
>
<div class="box-content">
<div class="environment-selector-dialog" [@transformPanel]="'open'" role="dialog">
<div
class="environment-selector-dialog"
[@transformPanel]="'open'"
cdkTrapFocus
cdkTrapFocusAutoCapture
role="dialog"
aria-modal="true"
>
<button
type="button"
class="environment-selector-dialog-item"
(click)="toggle(ServerEnvironmentType.US)"
[attr.aria-pressed]="selectedEnvironment === ServerEnvironmentType.US ? 'true' : 'false'"
>
<i
class="bwi bwi-fw bwi-sm bwi-check"
@@ -51,6 +62,7 @@
type="button"
class="environment-selector-dialog-item"
(click)="toggle(ServerEnvironmentType.EU)"
[attr.aria-pressed]="selectedEnvironment === ServerEnvironmentType.EU ? 'true' : 'false'"
*ngIf="euServerFlagEnabled"
>
<i
@@ -68,6 +80,9 @@
type="button"
class="environment-selector-dialog-item"
(click)="toggle(ServerEnvironmentType.SelfHosted)"
[attr.aria-pressed]="
selectedEnvironment === ServerEnvironmentType.SelfHosted ? 'true' : 'false'
"
>
<i
class="bwi bwi-fw bwi-sm bwi-check"

View File

@@ -352,7 +352,7 @@ describe("SsoComponent", () => {
describe("Given Trusted Device Encryption is enabled, user doesn't need to set a MP, and forcePasswordReset is required", () => {
[
ForceResetPasswordReason.AdminForcePasswordReset,
ForceResetPasswordReason.WeakMasterPassword,
// ForceResetPasswordReason.WeakMasterPassword, -- not possible in SSO flow as set client side
].forEach((forceResetPasswordReason) => {
const reasonString = ForceResetPasswordReason[forceResetPasswordReason];
let authResult;
@@ -449,7 +449,7 @@ describe("SsoComponent", () => {
describe("Force Master Password Reset scenarios", () => {
[
ForceResetPasswordReason.AdminForcePasswordReset,
ForceResetPasswordReason.WeakMasterPassword,
// ForceResetPasswordReason.WeakMasterPassword, -- not possible in SSO flow as set client side
].forEach((forceResetPasswordReason) => {
const reasonString = ForceResetPasswordReason[forceResetPasswordReason];

View File

@@ -226,9 +226,9 @@ export class SsoComponent {
return await this.handleChangePasswordRequired(orgIdentifier);
}
// Users can be forced to reset their password via an admin or org policy
// disallowing weak passwords
if (authResult.forcePasswordReset !== ForceResetPasswordReason.None) {
// Users enrolled in admin acct recovery can be forced to set a new password after
// having the admin set a temp password for them
if (authResult.forcePasswordReset == ForceResetPasswordReason.AdminForcePasswordReset) {
return await this.handleForcePasswordReset(orgIdentifier);
}

View File

@@ -282,8 +282,10 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
return await this.handleChangePasswordRequired(orgIdentifier);
}
// Users can be forced to reset their password via an admin or org policy
// disallowing weak passwords
// Users can be forced to reset their password via an admin or org policy disallowing weak passwords
// Note: this is different from SSO component login flow as a user can
// login with MP and then have to pass 2FA to finish login and we can actually
// evaluate if they have a weak password at this time.
if (authResult.forcePasswordReset !== ForceResetPasswordReason.None) {
return await this.handleForcePasswordReset(orgIdentifier);
}

View File

@@ -154,8 +154,13 @@ export class AddEditComponent implements OnInit, OnDestroy {
.policyAppliesToActiveUser$(PolicyType.SendOptions, (p) => p.data.disableHideEmail)
.pipe(takeUntil(this.destroy$))
.subscribe((policyAppliesToActiveUser) => {
if ((this.disableHideEmail = policyAppliesToActiveUser)) {
if (
(this.disableHideEmail = policyAppliesToActiveUser) &&
!this.formGroup.controls.hideEmail.value
) {
this.formGroup.controls.hideEmail.disable();
} else {
this.formGroup.controls.hideEmail.enable();
}
});
@@ -207,9 +212,6 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.send = await send.decrypt();
this.type = this.send.type;
this.updateFormValues();
if (this.send.hideEmail) {
this.formGroup.controls.hideEmail.enable();
}
} else {
this.send = new SendView();
this.send.type = this.type;
@@ -425,6 +427,10 @@ export class AddEditComponent implements OnInit, OnDestroy {
"yyyy-MM-ddTHH:mm"
),
});
if (this.send.hideEmail) {
this.formGroup.controls.hideEmail.enable();
}
}
private async handleCopyLinkToClipboard() {

View File

@@ -153,6 +153,7 @@ export abstract class LogInStrategy {
const result = new AuthResult();
result.resetMasterPassword = response.resetMasterPassword;
// Convert boolean to enum
if (response.forcePasswordReset) {
result.forcePasswordReset = ForceResetPasswordReason.AdminForcePasswordReset;
}

View File

@@ -14,6 +14,7 @@ import { DeviceTrustCryptoServiceAbstraction } from "../abstractions/device-trus
import { KeyConnectorService } from "../abstractions/key-connector.service";
import { TokenService } from "../abstractions/token.service";
import { TwoFactorService } from "../abstractions/two-factor.service";
import { ForceResetPasswordReason } from "../models/domain/force-reset-password-reason";
import { SsoLogInCredentials } from "../models/domain/log-in-credentials";
import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request";
import { IdentityTokenResponse } from "../models/response/identity-token.response";
@@ -73,6 +74,11 @@ export class SsoLogInStrategy extends LogInStrategy {
this.email = ssoAuthResult.email;
this.ssoEmail2FaSessionToken = ssoAuthResult.ssoEmail2FaSessionToken;
// Auth guard currently handles redirects for this.
if (ssoAuthResult.forcePasswordReset == ForceResetPasswordReason.AdminForcePasswordReset) {
await this.stateService.setForcePasswordResetReason(ssoAuthResult.forcePasswordReset);
}
return ssoAuthResult;
}

View File

@@ -1,3 +1,7 @@
/*
* This enum is used to determine if a user should be forced to reset their password
* on login (server flag) or unlock via MP (client evaluation).
*/
export enum ForceResetPasswordReason {
/**
* A password reset should not be forced.
@@ -6,12 +10,14 @@ export enum ForceResetPasswordReason {
/**
* Occurs when an organization admin forces a user to reset their password.
* Communicated via server flag.
*/
AdminForcePasswordReset,
/**
* Occurs when a user logs in / unlocks their vault with a master password that does not meet an organization's
* master password policy that is enforced on login/unlock.
* Only set client side b/c server can't evaluate MP.
*/
WeakMasterPassword,
}