mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 14:34:02 +00:00
Merge branch 'main' into PM-19741
This commit is contained in:
@@ -29,7 +29,9 @@ export function CipherInfo({ cipher, theme }: CipherInfoProps) {
|
||||
</span>
|
||||
|
||||
${login?.username
|
||||
? html`<span class=${cipherInfoSecondaryTextStyles(theme)}>${login.username}</span>`
|
||||
? html`<span title=${login.username} class=${cipherInfoSecondaryTextStyles(theme)}
|
||||
>${login.username}</span
|
||||
>`
|
||||
: null}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { html, nothing } from "lit";
|
||||
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { themes } from "../../constants/styles";
|
||||
import { spacing, themes } from "../../constants/styles";
|
||||
import { Celebrate, Keyhole, Warning } from "../../illustrations";
|
||||
|
||||
import { NotificationConfirmationMessage } from "./message";
|
||||
@@ -67,7 +67,7 @@ export const iconContainerStyles = (error?: string | boolean) => css`
|
||||
}
|
||||
`;
|
||||
export const notificationConfirmationBodyStyles = ({ theme }: { theme: Theme }) => css`
|
||||
gap: 16px;
|
||||
gap: ${spacing[4]};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
@@ -43,7 +43,7 @@ export function NotificationConfirmationContainer({
|
||||
type,
|
||||
}: NotificationConfirmationContainerProps) {
|
||||
const headerMessage = getHeaderMessage(i18n, type, error);
|
||||
const confirmationMessage = getConfirmationMessage(i18n, itemName, type, error);
|
||||
const confirmationMessage = getConfirmationMessage(i18n, type, error);
|
||||
const buttonText = error ? i18n.newItem : i18n.view;
|
||||
const buttonAria = chrome.i18n.getMessage("notificationViewAria", [itemName]);
|
||||
|
||||
@@ -109,19 +109,13 @@ export const notificationContainerStyles = (theme: Theme) => css`
|
||||
}
|
||||
`;
|
||||
|
||||
function getConfirmationMessage(
|
||||
i18n: I18n,
|
||||
itemName: string,
|
||||
type?: NotificationType,
|
||||
error?: string,
|
||||
) {
|
||||
const loginSaveConfirmation = chrome.i18n.getMessage("loginSaveConfirmation", [itemName]);
|
||||
const loginUpdatedConfirmation = chrome.i18n.getMessage("loginUpdatedConfirmation", [itemName]);
|
||||
|
||||
function getConfirmationMessage(i18n: I18n, type?: NotificationType, error?: string) {
|
||||
if (error) {
|
||||
return i18n.saveFailureDetails;
|
||||
}
|
||||
return type === NotificationTypes.Add ? loginSaveConfirmation : loginUpdatedConfirmation;
|
||||
return type === NotificationTypes.Add
|
||||
? i18n.loginSaveConfirmation
|
||||
: i18n.loginUpdatedConfirmation;
|
||||
}
|
||||
|
||||
function getHeaderMessage(i18n: I18n, type?: NotificationType, error?: string) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { html, nothing } from "lit";
|
||||
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { themes, typography } from "../../constants/styles";
|
||||
import { spacing, themes, typography } from "../../constants/styles";
|
||||
|
||||
export type NotificationConfirmationMessageProps = {
|
||||
buttonAria?: string;
|
||||
@@ -18,15 +18,17 @@ export type NotificationConfirmationMessageProps = {
|
||||
export function NotificationConfirmationMessage({
|
||||
buttonAria,
|
||||
buttonText,
|
||||
itemName,
|
||||
message,
|
||||
messageDetails,
|
||||
handleClick,
|
||||
theme,
|
||||
}: NotificationConfirmationMessageProps) {
|
||||
return html`
|
||||
<div>
|
||||
<div class=${containerStyles}>
|
||||
${message || buttonText
|
||||
? html`
|
||||
<span class=${itemNameStyles(theme)} title=${itemName}> ${itemName} </span>
|
||||
<span
|
||||
title=${message || buttonText}
|
||||
class=${notificationConfirmationMessageStyles(theme)}
|
||||
@@ -57,6 +59,14 @@ export function NotificationConfirmationMessage({
|
||||
`;
|
||||
}
|
||||
|
||||
const containerStyles = css`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: ${spacing[1]};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const baseTextStyles = css`
|
||||
flex-grow: 1;
|
||||
overflow-x: hidden;
|
||||
@@ -74,6 +84,15 @@ export const notificationConfirmationMessageStyles = (theme: Theme) => css`
|
||||
font-weight: 400;
|
||||
`;
|
||||
|
||||
const itemNameStyles = (theme: Theme) => css`
|
||||
${baseTextStyles}
|
||||
|
||||
color: ${themes[theme].text.main};
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
max-width: 300px;
|
||||
`;
|
||||
|
||||
export const notificationConfirmationButtonTextStyles = (theme: Theme) => css`
|
||||
${baseTextStyles}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { html } from "lit";
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { CloseButton } from "../buttons/close-button";
|
||||
import { themes } from "../constants/styles";
|
||||
import { spacing, themes } from "../constants/styles";
|
||||
import { BrandIconContainer } from "../icons/brand-icon-container";
|
||||
|
||||
import { NotificationHeaderMessage } from "./header-message";
|
||||
@@ -47,7 +47,7 @@ const notificationHeaderStyles = ({
|
||||
standalone: boolean;
|
||||
theme: Theme;
|
||||
}) => css`
|
||||
gap: 8px;
|
||||
gap: ${spacing[2]};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
@@ -51,7 +51,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
|
||||
}
|
||||
|
||||
const buttonRowStyles = css`
|
||||
gap: 16px;
|
||||
gap: ${spacing[4]};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -39,7 +39,6 @@ import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/
|
||||
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
@@ -1514,9 +1513,6 @@ export default class MainBackground {
|
||||
}
|
||||
|
||||
nextAccountStatus = await this.authService.getAuthStatus(userId);
|
||||
const forcePasswordReset =
|
||||
(await firstValueFrom(this.masterPasswordService.forceSetPasswordReason$(userId))) !=
|
||||
ForceSetPasswordReason.None;
|
||||
|
||||
await this.systemService.clearPendingClipboard();
|
||||
|
||||
@@ -1524,8 +1520,6 @@ export default class MainBackground {
|
||||
this.messagingService.send("goHome");
|
||||
} else if (nextAccountStatus === AuthenticationStatus.Locked) {
|
||||
this.messagingService.send("locked", { userId: userId });
|
||||
} else if (forcePasswordReset) {
|
||||
this.messagingService.send("update-temp-password", { userId: userId });
|
||||
} else {
|
||||
this.messagingService.send("unlocked", { userId: userId });
|
||||
await this.refreshBadge();
|
||||
|
||||
@@ -160,10 +160,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["/remove-password"]);
|
||||
} else if (msg.command == "update-temp-password") {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["/update-temp-password"]);
|
||||
}
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
<i slot="end" class="bwi bwi-external-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-item>
|
||||
<bit-item *ngIf="!(isNudgeFeatureEnabled$ | async)">
|
||||
<a bit-item-content routerLink="/more-from-bitwarden">
|
||||
{{ "moreFromBitwarden" | i18n }}
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<button type="button" bit-item-content (click)="rate()">
|
||||
{{ "rateExtension" | i18n }}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { DeviceType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { DialogService, ItemModule } from "@bitwarden/components";
|
||||
@@ -47,12 +49,17 @@ export class AboutPageV2Component {
|
||||
private dialogService: DialogService,
|
||||
private environmentService: EnvironmentService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
about() {
|
||||
this.dialogService.open(AboutDialogComponent);
|
||||
}
|
||||
|
||||
protected isNudgeFeatureEnabled$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.PM8851_BrowserOnboardingNudge,
|
||||
);
|
||||
|
||||
async launchHelp() {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "continueToHelpCenter" },
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<bit-item *ngIf="isNudgeFeatureEnabled$ | async">
|
||||
<a bit-item-content routerLink="/download-bitwarden">
|
||||
<i slot="start" class="bwi bwi-mobile" aria-hidden="true"></i>
|
||||
<div class="tw-flex tw-items-center tw-justify-center">
|
||||
@@ -81,7 +81,7 @@
|
||||
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||
</a>
|
||||
</bit-item>
|
||||
<bit-item>
|
||||
<bit-item *ngIf="isNudgeFeatureEnabled$ | async">
|
||||
<a bit-item-content routerLink="/more-from-bitwarden">
|
||||
<i slot="start" class="bwi bwi-filter" aria-hidden="true"></i>
|
||||
{{ "moreFromBitwarden" | i18n }}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { filter, firstValueFrom, Observable, shareReplay, switchMap } from "rxjs
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { BadgeComponent, ItemModule } from "@bitwarden/components";
|
||||
import { NudgeStatus, VaultNudgesService, VaultNudgeType } from "@bitwarden/vault";
|
||||
@@ -49,9 +51,14 @@ export class SettingsV2Component {
|
||||
),
|
||||
);
|
||||
|
||||
protected isNudgeFeatureEnabled$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.PM8851_BrowserOnboardingNudge,
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly vaultNudgesService: VaultNudgesService,
|
||||
private readonly accountService: AccountService,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async dismissBadge(type: VaultNudgeType) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import { UpdateTempPasswordRequest } from "@bitwarden/common/auth/models/request
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@@ -77,6 +78,7 @@ export class LoginCommand {
|
||||
protected logoutCallback: () => Promise<void>,
|
||||
protected kdfConfigService: KdfConfigService,
|
||||
protected ssoUrlService: SsoUrlService,
|
||||
protected masterPasswordService: MasterPasswordServiceAbstraction,
|
||||
) {}
|
||||
|
||||
async run(email: string, password: string, options: OptionValues) {
|
||||
@@ -361,14 +363,14 @@ export class LoginCommand {
|
||||
await this.syncService.fullSync(true);
|
||||
|
||||
// Handle updating passwords if NOT using an API Key for authentication
|
||||
if (
|
||||
response.forcePasswordReset != ForceSetPasswordReason.None &&
|
||||
clientId == null &&
|
||||
clientSecret == null
|
||||
) {
|
||||
if (response.forcePasswordReset === ForceSetPasswordReason.AdminForcePasswordReset) {
|
||||
if (clientId == null && clientSecret == null) {
|
||||
const forceSetPasswordReason = await firstValueFrom(
|
||||
this.masterPasswordService.forceSetPasswordReason$(response.userId),
|
||||
);
|
||||
|
||||
if (forceSetPasswordReason === ForceSetPasswordReason.AdminForcePasswordReset) {
|
||||
return await this.updateTempPassword(response.userId);
|
||||
} else if (response.forcePasswordReset === ForceSetPasswordReason.WeakMasterPassword) {
|
||||
} else if (forceSetPasswordReason === ForceSetPasswordReason.WeakMasterPassword) {
|
||||
return await this.updateWeakPassword(response.userId, password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ export class Program extends BaseProgram {
|
||||
async () => await this.serviceContainer.logout(),
|
||||
this.serviceContainer.kdfConfigService,
|
||||
this.serviceContainer.ssoUrlService,
|
||||
this.serviceContainer.masterPasswordService,
|
||||
);
|
||||
const response = await command.run(email, password, options);
|
||||
this.processResponse(response, true);
|
||||
|
||||
519
apps/desktop/desktop_native/Cargo.lock
generated
519
apps/desktop/desktop_native/Cargo.lock
generated
@@ -147,6 +147,23 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ashpd"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand 0.9.1",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"tokio",
|
||||
"url",
|
||||
"zbus 5.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.12.1"
|
||||
@@ -266,17 +283,6 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-net"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"blocking",
|
||||
"futures-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.3.0"
|
||||
@@ -739,7 +745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
@@ -917,7 +923,7 @@ dependencies = [
|
||||
"oo7",
|
||||
"pin-project",
|
||||
"pkcs8",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"rsa",
|
||||
"russh-cryptovec",
|
||||
"scopeguard",
|
||||
@@ -935,7 +941,7 @@ dependencies = [
|
||||
"widestring",
|
||||
"windows 0.61.1",
|
||||
"windows-future",
|
||||
"zbus",
|
||||
"zbus 4.4.0",
|
||||
"zbus_polkit",
|
||||
"zeroizing-alloc",
|
||||
]
|
||||
@@ -1020,6 +1026,17 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doctest-file"
|
||||
version = "1.0.0"
|
||||
@@ -1166,6 +1183,15 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-err"
|
||||
version = "2.11.0"
|
||||
@@ -1413,6 +1439,145 @@ dependencies = [
|
||||
"windows 0.57.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
@@ -1537,6 +1702,12 @@ version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@@ -1773,7 +1944,7 @@ dependencies = [
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
@@ -1960,35 +2131,33 @@ checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "oo7"
|
||||
version = "0.3.3"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc6ce4692fbfd044ce22ca07dcab1a30fa12432ca2aa5b1294eca50d3332a24"
|
||||
checksum = "6cb23d3ec3527d65a83be1c1795cb883c52cfa57147d42acc797127df56fc489"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"async-fs",
|
||||
"async-io",
|
||||
"async-lock",
|
||||
"async-net",
|
||||
"blocking",
|
||||
"ashpd",
|
||||
"cbc",
|
||||
"cipher",
|
||||
"digest",
|
||||
"endi",
|
||||
"futures-lite",
|
||||
"futures-util",
|
||||
"getrandom 0.3.1",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"md-5",
|
||||
"num",
|
||||
"num-bigint-dig",
|
||||
"pbkdf2",
|
||||
"rand",
|
||||
"rand 0.9.1",
|
||||
"serde",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"zbus",
|
||||
"tokio",
|
||||
"zbus 5.6.0",
|
||||
"zbus_macros 5.6.0",
|
||||
"zeroize",
|
||||
"zvariant",
|
||||
"zvariant 5.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2070,7 +2239,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@@ -2099,6 +2268,12 @@ dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
@@ -2186,7 +2361,7 @@ checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs5",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"spki",
|
||||
]
|
||||
|
||||
@@ -2298,8 +2473,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2309,7 +2494,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2321,6 +2516,15 @@ dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
@@ -2409,7 +2613,7 @@ dependencies = [
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"sha2",
|
||||
"signature",
|
||||
"spki",
|
||||
@@ -2638,7 +2842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2742,7 +2946,7 @@ dependencies = [
|
||||
"bcrypt-pbkdf",
|
||||
"ed25519-dalek",
|
||||
"num-bigint-dig",
|
||||
"rand_core",
|
||||
"rand_core 0.6.4",
|
||||
"rsa",
|
||||
"sha2",
|
||||
"signature",
|
||||
@@ -2752,6 +2956,12 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -2781,6 +2991,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.33.1"
|
||||
@@ -2900,6 +3121,16 @@ dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.1"
|
||||
@@ -2911,8 +3142,10 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -3202,6 +3435,30 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
@@ -3769,6 +4026,18 @@ dependencies = [
|
||||
"wayland-protocols-wlr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.1"
|
||||
@@ -3796,6 +4065,30 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "4.4.0"
|
||||
@@ -3820,7 +4113,7 @@ dependencies = [
|
||||
"hex",
|
||||
"nix 0.29.0",
|
||||
"ordered-stream",
|
||||
"rand",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"sha1",
|
||||
@@ -3829,9 +4122,37 @@ dependencies = [
|
||||
"uds_windows",
|
||||
"windows-sys 0.52.0",
|
||||
"xdg-home",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
"zvariant",
|
||||
"zbus_macros 4.4.0",
|
||||
"zbus_names 3.0.0",
|
||||
"zvariant 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2522b82023923eecb0b366da727ec883ace092e7887b61d3da5139f26b44da58"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
"enumflags2",
|
||||
"event-listener",
|
||||
"futures-core",
|
||||
"futures-lite",
|
||||
"hex",
|
||||
"nix 0.29.0",
|
||||
"ordered-stream",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"windows-sys 0.59.0",
|
||||
"winnow",
|
||||
"zbus_macros 5.6.0",
|
||||
"zbus_names 4.2.0",
|
||||
"zvariant 5.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3844,7 +4165,22 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils",
|
||||
"zvariant_utils 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05d2e12843c75108c00c618c2e8ef9675b50b6ec095b36dc965f2e5aed463c15"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zbus_names 4.2.0",
|
||||
"zvariant 5.5.1",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3855,7 +4191,19 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant",
|
||||
"zvariant 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zbus_names"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"winnow",
|
||||
"zvariant 5.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3868,7 +4216,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"static_assertions",
|
||||
"zbus",
|
||||
"zbus 4.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3892,6 +4240,27 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
@@ -3918,6 +4287,28 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebff5e6b81c1c7dca2d0bd333b2006da48cb37dbcae5a8da888f31fcb3c19934"
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "4.2.0"
|
||||
@@ -3928,7 +4319,22 @@ dependencies = [
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"zvariant_derive",
|
||||
"zvariant_derive 4.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "557e89d54880377a507c94cd5452f20e35d14325faf9d2958ebeadce0966c1b2"
|
||||
dependencies = [
|
||||
"endi",
|
||||
"enumflags2",
|
||||
"serde",
|
||||
"url",
|
||||
"winnow",
|
||||
"zvariant_derive 5.5.1",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3941,7 +4347,20 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils",
|
||||
"zvariant_utils 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_derive"
|
||||
version = "5.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "757779842a0d242061d24c28be589ce392e45350dfb9186dfd7a042a2e19870c"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zvariant_utils 3.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3954,3 +4373,17 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant_utils"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"syn",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
@@ -33,11 +33,11 @@ log = "=0.4.25"
|
||||
napi = "=2.16.15"
|
||||
napi-build = "=2.1.4"
|
||||
napi-derive = "=2.16.13"
|
||||
oo7 = "=0.3.3"
|
||||
oo7 = "=0.4.3"
|
||||
oslog = "=0.2.0"
|
||||
pin-project = "=1.1.10"
|
||||
pkcs8 = "=0.10.2"
|
||||
rand = "=0.8.5"
|
||||
rand = "=0.9.1"
|
||||
rsa = "=0.9.8"
|
||||
russh-cryptovec = "=0.7.3"
|
||||
scopeguard = "=1.2.0"
|
||||
|
||||
@@ -104,6 +104,6 @@ impl super::BiometricTrait for Biometric {
|
||||
|
||||
fn random_challenge() -> [u8; 16] {
|
||||
let mut challenge = [0u8; 16];
|
||||
rand::thread_rng().fill_bytes(&mut challenge);
|
||||
rand::rng().fill_bytes(&mut challenge);
|
||||
challenge
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ impl super::BiometricTrait for Biometric {
|
||||
|
||||
fn random_challenge() -> [u8; 16] {
|
||||
let mut challenge = [0u8; 16];
|
||||
rand::thread_rng().fill_bytes(&mut challenge);
|
||||
rand::rng().fill_bytes(&mut challenge);
|
||||
challenge
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||
@@ -409,17 +408,9 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
const locked =
|
||||
(await this.authService.getAuthStatus(message.userId)) ===
|
||||
AuthenticationStatus.Locked;
|
||||
const forcedPasswordReset =
|
||||
(await firstValueFrom(
|
||||
this.masterPasswordService.forceSetPasswordReason$(message.userId),
|
||||
)) != ForceSetPasswordReason.None;
|
||||
if (locked) {
|
||||
this.modalService.closeAll();
|
||||
await this.router.navigate(["lock"]);
|
||||
} else if (forcedPasswordReset) {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["update-temp-password"]);
|
||||
} else {
|
||||
this.messagingService.send("unlocked");
|
||||
this.loading = true;
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
[bitMenuTriggerFor]="editCollectionMenu"
|
||||
size="small"
|
||||
type="button"
|
||||
aria-haspopup="true"
|
||||
></button>
|
||||
<bit-menu #editCollectionMenu>
|
||||
<ng-container *ngIf="canEditCollection">
|
||||
|
||||
@@ -153,6 +153,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #attachments></ng-template>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
<ng-template #collectionsModal></ng-template>
|
||||
|
||||
@@ -456,7 +456,13 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
return Object.assign(p, partialPermissions);
|
||||
}
|
||||
|
||||
handleDependentPermissions() {
|
||||
async handleDependentPermissions() {
|
||||
const separateCustomRolePermissions = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.SeparateCustomRolePermissions,
|
||||
);
|
||||
if (separateCustomRolePermissions) {
|
||||
return;
|
||||
}
|
||||
// Manage Password Reset (Account Recovery) must have Manage Users enabled
|
||||
if (
|
||||
this.permissionsGroup.value.manageResetPassword &&
|
||||
|
||||
@@ -374,4 +374,3 @@
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-template #resetPasswordTemplate></ng-template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import {
|
||||
@@ -90,9 +90,6 @@ class MembersTableDataSource extends PeopleTableDataSource<OrganizationUserView>
|
||||
templateUrl: "members.component.html",
|
||||
})
|
||||
export class MembersComponent extends BaseMembersComponent<OrganizationUserView> {
|
||||
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
||||
resetPasswordModalRef: ViewContainerRef;
|
||||
|
||||
userType = OrganizationUserType;
|
||||
userStatusType = OrganizationUserStatusType;
|
||||
memberTab = MemberDialogTab;
|
||||
|
||||
@@ -35,5 +35,4 @@
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
<ng-template #editTemplate></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { firstValueFrom, lastValueFrom, map, Observable, switchMap } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
@@ -33,9 +33,6 @@ import { PolicyEditComponent, PolicyEditDialogResult } from "./policy-edit.compo
|
||||
templateUrl: "policies.component.html",
|
||||
})
|
||||
export class PoliciesComponent implements OnInit {
|
||||
@ViewChild("editTemplate", { read: ViewContainerRef, static: true })
|
||||
editModalRef: ViewContainerRef;
|
||||
|
||||
loading = true;
|
||||
organizationId: string;
|
||||
policies: BasePolicy[];
|
||||
|
||||
@@ -93,7 +93,4 @@
|
||||
{{ "purgeVault" | i18n }}
|
||||
</button>
|
||||
</app-danger-zone>
|
||||
|
||||
<ng-template #apiKeyTemplate></ng-template>
|
||||
<ng-template #rotateApiKeyTemplate></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { FormBuilder, Validators } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import {
|
||||
@@ -43,11 +43,6 @@ import { DeleteOrganizationDialogResult, openDeleteOrganizationDialog } from "./
|
||||
templateUrl: "account.component.html",
|
||||
})
|
||||
export class AccountComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("apiKeyTemplate", { read: ViewContainerRef, static: true })
|
||||
apiKeyModalRef: ViewContainerRef;
|
||||
@ViewChild("rotateApiKeyTemplate", { read: ViewContainerRef, static: true })
|
||||
rotateApiKeyModalRef: ViewContainerRef;
|
||||
|
||||
selfHosted = false;
|
||||
canEditSubscription = true;
|
||||
loading = true;
|
||||
|
||||
@@ -51,7 +51,4 @@
|
||||
{{ "deleteAccount" | i18n }}
|
||||
</button>
|
||||
</app-danger-zone>
|
||||
|
||||
<ng-template #viewUserApiKeyTemplate></ng-template>
|
||||
<ng-template #rotateUserApiKeyTemplate></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -272,7 +272,3 @@
|
||||
</ng-container>
|
||||
</bit-section>
|
||||
</bit-container>
|
||||
|
||||
<ng-template #addEdit></ng-template>
|
||||
<ng-template #takeoverTemplate></ng-template>
|
||||
<ng-template #confirmTemplate></ng-template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { lastValueFrom, Observable, firstValueFrom, switchMap } from "rxjs";
|
||||
|
||||
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
|
||||
@@ -44,12 +44,6 @@ import {
|
||||
templateUrl: "emergency-access.component.html",
|
||||
})
|
||||
export class EmergencyAccessComponent implements OnInit {
|
||||
@ViewChild("addEdit", { read: ViewContainerRef, static: true }) addEditModalRef: ViewContainerRef;
|
||||
@ViewChild("takeoverTemplate", { read: ViewContainerRef, static: true })
|
||||
takeoverModalRef: ViewContainerRef;
|
||||
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
|
||||
confirmModalRef: ViewContainerRef;
|
||||
|
||||
loaded = false;
|
||||
canAccessPremium$: Observable<boolean>;
|
||||
trustedContacts: GranteeEmergencyAccess[];
|
||||
|
||||
@@ -51,5 +51,3 @@
|
||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
<ng-template #attachments></ng-template>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
@@ -17,7 +17,6 @@ import { EmergencyViewDialogComponent } from "./emergency-view-dialog.component"
|
||||
providers: [{ provide: CipherFormConfigService, useClass: DefaultCipherFormConfigService }],
|
||||
})
|
||||
export class EmergencyAccessViewComponent implements OnInit {
|
||||
@ViewChild("attachments", { read: ViewContainerRef, static: true })
|
||||
id: EmergencyAccessId | null = null;
|
||||
ciphers: CipherView[] = [];
|
||||
loaded = false;
|
||||
|
||||
@@ -84,8 +84,3 @@
|
||||
</bit-item>
|
||||
</bit-item-group>
|
||||
</bit-container>
|
||||
|
||||
<ng-template #duoTemplate></ng-template>
|
||||
<ng-template #emailTemplate></ng-template>
|
||||
<ng-template #yubikeyTemplate></ng-template>
|
||||
<ng-template #webAuthnTemplate></ng-template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnDestroy, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import {
|
||||
first,
|
||||
firstValueFrom,
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
switchMap,
|
||||
} from "rxjs";
|
||||
|
||||
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
@@ -52,9 +51,6 @@ import { TwoFactorVerifyComponent } from "./two-factor-verify.component";
|
||||
imports: [ItemModule, LooseComponentsModule, SharedModule],
|
||||
})
|
||||
export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("yubikeyTemplate", { read: ViewContainerRef, static: true })
|
||||
yubikeyModalRef: ViewContainerRef;
|
||||
|
||||
organizationId: string;
|
||||
organization: Organization;
|
||||
providers: any[] = [];
|
||||
@@ -62,7 +58,6 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||
recoveryCodeWarningMessage: string;
|
||||
showPolicyWarning = false;
|
||||
loading = true;
|
||||
modal: ModalRef;
|
||||
formPromise: Promise<any>;
|
||||
|
||||
tabbedHeader = true;
|
||||
@@ -283,9 +278,6 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
protected updateStatus(enabled: boolean, type: TwoFactorProviderType) {
|
||||
if (!enabled && this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
this.providers.forEach((p) => {
|
||||
if (p.type === type && enabled !== undefined) {
|
||||
p.enabled = enabled;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Directive, ViewChild, ViewContainerRef, OnDestroy } from "@angular/core";
|
||||
import { Directive, OnDestroy } from "@angular/core";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
lastValueFrom,
|
||||
@@ -37,8 +37,6 @@ import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/se
|
||||
|
||||
@Directive()
|
||||
export class CipherReportComponent implements OnDestroy {
|
||||
@ViewChild("cipherAddEdit", { read: ViewContainerRef, static: true })
|
||||
cipherAddEditModalRef: ViewContainerRef;
|
||||
isAdminConsoleActive = false;
|
||||
|
||||
loading = false;
|
||||
|
||||
@@ -96,5 +96,4 @@
|
||||
</bit-table-scroll>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -108,5 +108,4 @@
|
||||
>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -98,5 +98,4 @@
|
||||
</bit-table-scroll>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -96,5 +96,4 @@
|
||||
</bit-table>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -100,5 +100,4 @@
|
||||
</bit-table-scroll>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
</bit-container>
|
||||
|
||||
@@ -206,4 +206,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #sendAddEdit></ng-template>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<button bitButton buttonType="primary" bitFormButton type="submit">
|
||||
<span>{{ "save" | i18n }}</span>
|
||||
</button>
|
||||
<button bitButton buttonType="secondary" bitDialogClose type="button" data-dismiss="modal">
|
||||
<button bitButton buttonType="secondary" bitDialogClose type="button">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
<div class="tw-m-0 tw-ml-auto">
|
||||
|
||||
@@ -84,10 +84,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #attachments></ng-template>
|
||||
<ng-template #folderAddEdit></ng-template>
|
||||
<ng-template #cipherAddEdit></ng-template>
|
||||
<ng-template #share></ng-template>
|
||||
<ng-template #collectionsModal></ng-template>
|
||||
<ng-template #updateKeyTemplate></ng-template>
|
||||
|
||||
@@ -57,9 +57,6 @@
|
||||
bitMenuItem
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
appA11yTitle="{{ 'options' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-cog bwi-lg" aria-hidden="true"></i>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LoginSuccessHandlerService } from "@bitwarden/auth/common";
|
||||
import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { WebAuthnLoginCredentialAssertionView } from "@bitwarden/common/auth/models/view/webauthn-login/webauthn-login-credential-assertion.view";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -21,7 +20,6 @@ export class BaseLoginViaWebAuthnComponent implements OnInit {
|
||||
protected currentState: State = "assert";
|
||||
|
||||
protected successRoute = "/vault";
|
||||
protected forcePasswordResetRoute = "/update-temp-password";
|
||||
|
||||
constructor(
|
||||
private webAuthnLoginService: WebAuthnLoginServiceAbstraction,
|
||||
@@ -73,11 +71,6 @@ export class BaseLoginViaWebAuthnComponent implements OnInit {
|
||||
await this.loginSuccessHandlerService.run(authResult.userId);
|
||||
}
|
||||
|
||||
if (authResult.forcePasswordReset == ForceSetPasswordReason.AdminForcePasswordReset) {
|
||||
await this.router.navigate([this.forcePasswordResetRoute]);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.router.navigate([this.successRoute]);
|
||||
} catch (error) {
|
||||
if (error instanceof ErrorResponse) {
|
||||
|
||||
@@ -19,7 +19,6 @@ import { AuthRequestType } from "@bitwarden/common/auth/enums/auth-request-type"
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
|
||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||
import { LoginViaAuthRequestView } from "@bitwarden/common/auth/models/view/login-via-auth-request.view";
|
||||
@@ -820,8 +819,6 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy {
|
||||
private async handlePostLoginNavigation(loginResponse: AuthResult) {
|
||||
if (loginResponse.requiresTwoFactor) {
|
||||
await this.router.navigate(["2fa"]);
|
||||
} else if (loginResponse.forcePasswordReset != ForceSetPasswordReason.None) {
|
||||
await this.router.navigate(["update-temp-password"]);
|
||||
} else {
|
||||
await this.handleSuccessfulLoginNavigation(loginResponse.userId);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/mod
|
||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { ClientType, HttpStatusCode } from "@bitwarden/common/enums";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
@@ -307,10 +306,7 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
await this.loginSuccessHandlerService.run(authResult.userId);
|
||||
|
||||
// Determine where to send the user next
|
||||
if (authResult.forcePasswordReset != ForceSetPasswordReason.None) {
|
||||
await this.router.navigate(["update-temp-password"]);
|
||||
return;
|
||||
}
|
||||
// The AuthGuard will handle routing to update-temp-password based on state
|
||||
|
||||
// TODO: PM-18269 - evaluate if we can combine this with the
|
||||
// password evaluation done in the password login strategy.
|
||||
|
||||
@@ -136,11 +136,6 @@ export class NewDeviceVerificationComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
if (authResult.forcePasswordReset) {
|
||||
await this.router.navigate(["/update-temp-password"]);
|
||||
return;
|
||||
}
|
||||
|
||||
this.loginSuccessHandlerService.run(authResult.userId);
|
||||
|
||||
// If verification succeeds, navigate to vault
|
||||
|
||||
@@ -541,14 +541,6 @@ export class SsoComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
private async handleForcePasswordReset(orgIdentifier: string) {
|
||||
await this.router.navigate(["update-temp-password"], {
|
||||
queryParams: {
|
||||
identifier: orgIdentifier,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private async handleSuccessfulLogin() {
|
||||
await this.router.navigate(["lock"]);
|
||||
}
|
||||
|
||||
@@ -575,25 +575,6 @@ export class TwoFactorAuthComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a user needs to reset their password based on certain conditions.
|
||||
* Users can be forced to reset their password via an admin or org policy disallowing weak passwords.
|
||||
* Note: this is different from the 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 that time.
|
||||
*
|
||||
* @param {AuthResult} authResult - The authentication result.
|
||||
* @returns {boolean} Returns true if a password reset is required, false otherwise.
|
||||
*/
|
||||
private isForcePasswordResetRequired(authResult: AuthResult): boolean {
|
||||
const forceResetReasons = [
|
||||
ForceSetPasswordReason.AdminForcePasswordReset,
|
||||
ForceSetPasswordReason.WeakMasterPassword,
|
||||
];
|
||||
|
||||
return forceResetReasons.includes(authResult.forcePasswordReset);
|
||||
}
|
||||
|
||||
showContinueButton() {
|
||||
return (
|
||||
this.selectedProviderType != null &&
|
||||
|
||||
@@ -296,13 +296,9 @@ describe("LoginStrategy", () => {
|
||||
|
||||
const expected = new AuthResult();
|
||||
expected.userId = userId;
|
||||
expected.forcePasswordReset = ForceSetPasswordReason.AdminForcePasswordReset;
|
||||
expected.resetMasterPassword = true;
|
||||
expected.twoFactorProviders = {} as Partial<
|
||||
Record<TwoFactorProviderType, Record<string, string>>
|
||||
>;
|
||||
expected.captchaSiteKey = "";
|
||||
expected.twoFactorProviders = null;
|
||||
expected.captchaSiteKey = "";
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
@@ -316,13 +312,9 @@ describe("LoginStrategy", () => {
|
||||
|
||||
const expected = new AuthResult();
|
||||
expected.userId = userId;
|
||||
expected.forcePasswordReset = ForceSetPasswordReason.AdminForcePasswordReset;
|
||||
expected.resetMasterPassword = false;
|
||||
expected.twoFactorProviders = {} as Partial<
|
||||
Record<TwoFactorProviderType, Record<string, string>>
|
||||
>;
|
||||
expected.captchaSiteKey = "";
|
||||
expected.twoFactorProviders = null;
|
||||
expected.captchaSiteKey = "";
|
||||
expect(result).toEqual(expected);
|
||||
|
||||
expect(masterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith(
|
||||
|
||||
@@ -277,17 +277,7 @@ export abstract class LoginStrategy {
|
||||
|
||||
result.resetMasterPassword = response.resetMasterPassword;
|
||||
|
||||
// Convert boolean to enum and set the state for the master password service to
|
||||
// so we know when we reach the auth guard that we need to guide them properly to admin
|
||||
// password reset.
|
||||
if (response.forcePasswordReset) {
|
||||
result.forcePasswordReset = ForceSetPasswordReason.AdminForcePasswordReset;
|
||||
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.AdminForcePasswordReset,
|
||||
userId,
|
||||
);
|
||||
}
|
||||
await this.processForceSetPasswordReason(response.forcePasswordReset, userId);
|
||||
|
||||
if (response.twoFactorToken != null) {
|
||||
// note: we can read email from access token b/c it was saved in saveAccountInformation
|
||||
@@ -318,6 +308,30 @@ export abstract class LoginStrategy {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if adminForcePasswordReset is true and sets the ForceSetPasswordReason.AdminForcePasswordReset flag in the master password service.
|
||||
* @param adminForcePasswordReset - The admin force password reset flag
|
||||
* @param userId - The user ID
|
||||
* @returns a promise that resolves to a boolean indicating whether the admin force password reset flag was set
|
||||
*/
|
||||
async processForceSetPasswordReason(
|
||||
adminForcePasswordReset: boolean,
|
||||
userId: UserId,
|
||||
): Promise<boolean> {
|
||||
if (!adminForcePasswordReset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the flag in the master password service so we know when we reach the auth guard
|
||||
// that we need to guide them properly to admin password reset.
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.AdminForcePasswordReset,
|
||||
userId,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async createKeyPairForOldAccount(userId: UserId) {
|
||||
try {
|
||||
const userKey = await this.keyService.getUserKeyWithLegacySupport(userId);
|
||||
|
||||
@@ -211,20 +211,18 @@ describe("PasswordLoginStrategy", () => {
|
||||
it("does not force the user to update their master password when there are no requirements", async () => {
|
||||
apiService.postIdentityToken.mockResolvedValueOnce(identityTokenResponseFactory());
|
||||
|
||||
const result = await passwordLoginStrategy.logIn(credentials);
|
||||
await passwordLoginStrategy.logIn(credentials);
|
||||
|
||||
expect(policyService.evaluateMasterPassword).not.toHaveBeenCalled();
|
||||
expect(result.forcePasswordReset).toEqual(ForceSetPasswordReason.None);
|
||||
});
|
||||
|
||||
it("does not force the user to update their master password when it meets requirements", async () => {
|
||||
passwordStrengthService.getPasswordStrength.mockReturnValue({ score: 5 } as any);
|
||||
policyService.evaluateMasterPassword.mockReturnValue(true);
|
||||
|
||||
const result = await passwordLoginStrategy.logIn(credentials);
|
||||
await passwordLoginStrategy.logIn(credentials);
|
||||
|
||||
expect(policyService.evaluateMasterPassword).toHaveBeenCalled();
|
||||
expect(result.forcePasswordReset).toEqual(ForceSetPasswordReason.None);
|
||||
});
|
||||
|
||||
it("forces the user to update their master password on successful login when it does not meet master password policy requirements", async () => {
|
||||
@@ -232,14 +230,13 @@ describe("PasswordLoginStrategy", () => {
|
||||
policyService.evaluateMasterPassword.mockReturnValue(false);
|
||||
tokenService.decodeAccessToken.mockResolvedValue({ sub: userId });
|
||||
|
||||
const result = await passwordLoginStrategy.logIn(credentials);
|
||||
await passwordLoginStrategy.logIn(credentials);
|
||||
|
||||
expect(policyService.evaluateMasterPassword).toHaveBeenCalled();
|
||||
expect(masterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith(
|
||||
ForceSetPasswordReason.WeakMasterPassword,
|
||||
userId,
|
||||
);
|
||||
expect(result.forcePasswordReset).toEqual(ForceSetPasswordReason.WeakMasterPassword);
|
||||
});
|
||||
|
||||
it("forces the user to update their master password on successful 2FA login when it does not meet master password policy requirements", async () => {
|
||||
@@ -257,13 +254,13 @@ describe("PasswordLoginStrategy", () => {
|
||||
|
||||
// First login request fails requiring 2FA
|
||||
apiService.postIdentityToken.mockResolvedValueOnce(token2FAResponse);
|
||||
const firstResult = await passwordLoginStrategy.logIn(credentials);
|
||||
await passwordLoginStrategy.logIn(credentials);
|
||||
|
||||
// Second login request succeeds
|
||||
apiService.postIdentityToken.mockResolvedValueOnce(
|
||||
identityTokenResponseFactory(masterPasswordPolicy),
|
||||
);
|
||||
const secondResult = await passwordLoginStrategy.logInTwoFactor(
|
||||
await passwordLoginStrategy.logInTwoFactor(
|
||||
{
|
||||
provider: TwoFactorProviderType.Authenticator,
|
||||
token: "123456",
|
||||
@@ -272,15 +269,11 @@ describe("PasswordLoginStrategy", () => {
|
||||
"",
|
||||
);
|
||||
|
||||
// First login attempt should not save the force password reset options
|
||||
expect(firstResult.forcePasswordReset).toEqual(ForceSetPasswordReason.None);
|
||||
|
||||
// Second login attempt should save the force password reset options and return in result
|
||||
// Second login attempt should save the force password reset options
|
||||
expect(masterPasswordService.mock.setForceSetPasswordReason).toHaveBeenCalledWith(
|
||||
ForceSetPasswordReason.WeakMasterPassword,
|
||||
userId,
|
||||
);
|
||||
expect(secondResult.forcePasswordReset).toEqual(ForceSetPasswordReason.WeakMasterPassword);
|
||||
});
|
||||
|
||||
it("handles new device verification login with OTP", async () => {
|
||||
@@ -298,7 +291,6 @@ describe("PasswordLoginStrategy", () => {
|
||||
newDeviceOtp: deviceVerificationOtp,
|
||||
}),
|
||||
);
|
||||
expect(result.forcePasswordReset).toBe(ForceSetPasswordReason.None);
|
||||
expect(result.resetMasterPassword).toBe(false);
|
||||
expect(result.userId).toBe(userId);
|
||||
});
|
||||
|
||||
@@ -109,35 +109,8 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
const masterPasswordPolicyOptions =
|
||||
this.getMasterPasswordPolicyOptionsFromResponse(identityResponse);
|
||||
await this.evaluateMasterPasswordIfRequired(identityResponse, credentials, authResult);
|
||||
|
||||
// The identity result can contain master password policies for the user's organizations
|
||||
if (masterPasswordPolicyOptions?.enforceOnLogin) {
|
||||
// If there is a policy active, evaluate the supplied password before its no longer in memory
|
||||
const meetsRequirements = this.evaluateMasterPassword(
|
||||
credentials,
|
||||
masterPasswordPolicyOptions,
|
||||
);
|
||||
if (meetsRequirements) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
if (identityResponse instanceof IdentityTwoFactorResponse) {
|
||||
// Save the flag to this strategy for use in 2fa login as the master password is about to pass out of scope
|
||||
this.cache.next({
|
||||
...this.cache.value,
|
||||
forcePasswordResetReason: ForceSetPasswordReason.WeakMasterPassword,
|
||||
});
|
||||
} else {
|
||||
// Authentication was successful, save the force update password options with the state service
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.WeakMasterPassword,
|
||||
authResult.userId, // userId is only available on successful login
|
||||
);
|
||||
authResult.forcePasswordReset = ForceSetPasswordReason.WeakMasterPassword;
|
||||
}
|
||||
}
|
||||
return authResult;
|
||||
}
|
||||
|
||||
@@ -151,20 +124,6 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||
|
||||
const result = await super.logInTwoFactor(twoFactor);
|
||||
|
||||
// 2FA was successful, save the force update password options with the state service if defined
|
||||
const forcePasswordResetReason = this.cache.value.forcePasswordResetReason;
|
||||
if (
|
||||
!result.requiresTwoFactor &&
|
||||
!result.requiresCaptcha &&
|
||||
forcePasswordResetReason != ForceSetPasswordReason.None
|
||||
) {
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
forcePasswordResetReason,
|
||||
result.userId,
|
||||
);
|
||||
result.forcePasswordReset = forcePasswordResetReason;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -208,13 +167,58 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||
return !response.key;
|
||||
}
|
||||
|
||||
private getMasterPasswordPolicyOptionsFromResponse(
|
||||
response:
|
||||
private async evaluateMasterPasswordIfRequired(
|
||||
identityResponse:
|
||||
| IdentityTokenResponse
|
||||
| IdentityTwoFactorResponse
|
||||
| IdentityDeviceVerificationResponse,
|
||||
): MasterPasswordPolicyOptions {
|
||||
if (response == null || response instanceof IdentityDeviceVerificationResponse) {
|
||||
credentials: PasswordLoginCredentials,
|
||||
authResult: AuthResult,
|
||||
): Promise<void> {
|
||||
// TODO: PM-21084 - investigate if we should be sending down masterPasswordPolicy on the IdentityDeviceVerificationResponse like we do for the IdentityTwoFactorResponse
|
||||
// If the response is a device verification response, we don't need to evaluate the password
|
||||
if (identityResponse instanceof IdentityDeviceVerificationResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The identity result can contain master password policies for the user's organizations
|
||||
const masterPasswordPolicyOptions =
|
||||
this.getMasterPasswordPolicyOptionsFromResponse(identityResponse);
|
||||
|
||||
if (!masterPasswordPolicyOptions?.enforceOnLogin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is a policy active, evaluate the supplied password before its no longer in memory
|
||||
const meetsRequirements = this.evaluateMasterPassword(credentials, masterPasswordPolicyOptions);
|
||||
if (meetsRequirements) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (identityResponse instanceof IdentityTwoFactorResponse) {
|
||||
// Save the flag to this strategy for use in 2fa as the master password is about to pass out of scope
|
||||
this.cache.next({
|
||||
...this.cache.value,
|
||||
forcePasswordResetReason: ForceSetPasswordReason.WeakMasterPassword,
|
||||
});
|
||||
}
|
||||
|
||||
// Authentication was successful, save the force update password options with the state service
|
||||
// if there isn't already a reason set (this would only be AdminForcePasswordReset as that can be set server side
|
||||
// and would have already been processed in the base login strategy processForceSetPasswordReason method)
|
||||
// Note: masterPasswordService.setForceSetPasswordReason will not allow overwriting
|
||||
// AdminForcePasswordReset with any other reason except for None. This is because
|
||||
// an AdminForcePasswordReset will always force a user to update their password to a password that meets the policy.
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.WeakMasterPassword,
|
||||
authResult.userId, // userId is only available on successful login
|
||||
);
|
||||
}
|
||||
|
||||
private getMasterPasswordPolicyOptionsFromResponse(
|
||||
response: IdentityTokenResponse | IdentityTwoFactorResponse,
|
||||
): MasterPasswordPolicyOptions | null {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
return MasterPasswordPolicyOptions.fromResponse(response.masterPasswordPolicy);
|
||||
@@ -246,4 +250,35 @@ export class PasswordLoginStrategy extends LoginStrategy {
|
||||
const [authResult] = await this.startLogIn();
|
||||
return authResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to handle the WeakMasterPassword reason if no other reason is set.
|
||||
* @param authResult - The authentication result
|
||||
* @param userId - The user ID
|
||||
*/
|
||||
override async processForceSetPasswordReason(
|
||||
adminForcePasswordReset: boolean,
|
||||
userId: UserId,
|
||||
): Promise<boolean> {
|
||||
// handle any existing reasons
|
||||
const adminForcePasswordResetFlagSet = await super.processForceSetPasswordReason(
|
||||
adminForcePasswordReset,
|
||||
userId,
|
||||
);
|
||||
|
||||
// If we are already processing an admin force password reset, don't process other reasons
|
||||
if (adminForcePasswordResetFlagSet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have a cached weak password reason from login/logInTwoFactor apply it
|
||||
const cachedReason = this.cache.value.forcePasswordResetReason;
|
||||
if (cachedReason !== ForceSetPasswordReason.None) {
|
||||
await this.masterPasswordService.setForceSetPasswordReason(cachedReason, userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If none of the conditions are met, return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { BehaviorSubject, of } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
@@ -37,10 +37,11 @@ import {
|
||||
AuthRequestServiceAbstraction,
|
||||
InternalUserDecryptionOptionsServiceAbstraction,
|
||||
} from "../abstractions";
|
||||
import { UserDecryptionOptions } from "../models";
|
||||
import { SsoLoginCredentials } from "../models/domain/login-credentials";
|
||||
|
||||
import { identityTokenResponseFactory } from "./login.strategy.spec";
|
||||
import { SsoLoginStrategy } from "./sso-login.strategy";
|
||||
import { SsoLoginStrategy, SsoLoginStrategyData } from "./sso-login.strategy";
|
||||
|
||||
describe("SsoLoginStrategy", () => {
|
||||
let accountService: FakeAccountService;
|
||||
@@ -123,8 +124,11 @@ describe("SsoLoginStrategy", () => {
|
||||
mockVaultTimeoutBSub.asObservable(),
|
||||
);
|
||||
|
||||
const userDecryptionOptions = new UserDecryptionOptions();
|
||||
userDecryptionOptionsService.userDecryptionOptions$ = of(userDecryptionOptions);
|
||||
|
||||
ssoLoginStrategy = new SsoLoginStrategy(
|
||||
null,
|
||||
{} as SsoLoginStrategyData,
|
||||
keyConnectorService,
|
||||
deviceTrustService,
|
||||
authRequestService,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { firstValueFrom, Observable, map, BehaviorSubject } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request";
|
||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
|
||||
@@ -355,4 +356,75 @@ export class SsoLoginStrategy extends LoginStrategy {
|
||||
sso: this.cache.value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to handle SSO-specific ForceSetPasswordReason flags,including TdeOffboarding,
|
||||
* TdeUserWithoutPasswordHasPasswordResetPermission, and SsoNewJitProvisionedUser cases.
|
||||
* @param authResult - The authentication result
|
||||
* @param userId - The user ID
|
||||
*/
|
||||
override async processForceSetPasswordReason(
|
||||
adminForcePasswordReset: boolean,
|
||||
userId: UserId,
|
||||
): Promise<boolean> {
|
||||
// handle any existing reasons
|
||||
const adminForcePasswordResetFlagSet = await super.processForceSetPasswordReason(
|
||||
adminForcePasswordReset,
|
||||
userId,
|
||||
);
|
||||
|
||||
// If we are already processing an admin force password reset, don't process other reasons
|
||||
if (adminForcePasswordResetFlagSet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for TDE-related conditions
|
||||
const userDecryptionOptions = await firstValueFrom(
|
||||
this.userDecryptionOptionsService.userDecryptionOptions$,
|
||||
);
|
||||
|
||||
if (!userDecryptionOptions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for TDE offboarding - user is being offboarded from TDE and needs to set a password
|
||||
if (userDecryptionOptions.trustedDeviceOption?.isTdeOffboarding) {
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.TdeOffboarding,
|
||||
userId,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if user has permission to set password but hasn't yet
|
||||
if (
|
||||
!userDecryptionOptions.hasMasterPassword &&
|
||||
userDecryptionOptions.trustedDeviceOption?.hasManageResetPasswordPermission
|
||||
) {
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.TdeUserWithoutPasswordHasPasswordResetPermission,
|
||||
userId,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for new SSO JIT provisioned user
|
||||
// If a user logs in via SSO but has no master password and no alternative encryption methods
|
||||
// Then they must be a newly provisioned user who needs to set up their encryption
|
||||
if (
|
||||
!userDecryptionOptions.hasMasterPassword &&
|
||||
!userDecryptionOptions.keyConnectorOption?.keyConnectorUrl &&
|
||||
!userDecryptionOptions.trustedDeviceOption
|
||||
) {
|
||||
await this.masterPasswordService.setForceSetPasswordReason(
|
||||
ForceSetPasswordReason.SsoNewJitProvisionedUser,
|
||||
userId,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If none of the conditions are met, return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,6 @@ describe("WebAuthnLoginStrategy", () => {
|
||||
expect(authResult).toBeInstanceOf(AuthResult);
|
||||
expect(authResult).toMatchObject({
|
||||
captchaSiteKey: "",
|
||||
forcePasswordReset: 0,
|
||||
resetMasterPassword: false,
|
||||
twoFactorProviders: null,
|
||||
requiresTwoFactor: false,
|
||||
|
||||
@@ -4,8 +4,6 @@ import { Utils } from "../../../platform/misc/utils";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { TwoFactorProviderType } from "../../enums/two-factor-provider-type";
|
||||
|
||||
import { ForceSetPasswordReason } from "./force-set-password-reason";
|
||||
|
||||
export class AuthResult {
|
||||
userId: UserId;
|
||||
captchaSiteKey = "";
|
||||
@@ -17,7 +15,6 @@ export class AuthResult {
|
||||
* */
|
||||
resetMasterPassword = false;
|
||||
|
||||
forcePasswordReset: ForceSetPasswordReason = ForceSetPasswordReason.None;
|
||||
twoFactorProviders: Partial<Record<TwoFactorProviderType, Record<string, string>>> = null;
|
||||
ssoEmail2FaSessionToken?: string;
|
||||
email: string;
|
||||
|
||||
@@ -31,4 +31,9 @@ export enum ForceSetPasswordReason {
|
||||
* Occurs when TDE is disabled and master password has to be set.
|
||||
*/
|
||||
TdeOffboarding,
|
||||
|
||||
/**
|
||||
* Occurs when a new SSO user is JIT provisioned and needs to set their master password.
|
||||
*/
|
||||
SsoNewJitProvisionedUser,
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export enum FeatureFlag {
|
||||
LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission",
|
||||
SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility",
|
||||
AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner",
|
||||
SeparateCustomRolePermissions = "pm-19917-separate-custom-role-permissions",
|
||||
|
||||
/* Auth */
|
||||
PM9112_DeviceApprovalPersistence = "pm-9112-device-approval-persistence",
|
||||
@@ -83,6 +84,7 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.LimitItemDeletion]: FALSE,
|
||||
[FeatureFlag.SsoExternalIdVisibility]: FALSE,
|
||||
[FeatureFlag.AccountDeprovisioningBanner]: FALSE,
|
||||
[FeatureFlag.SeparateCustomRolePermissions]: FALSE,
|
||||
|
||||
/* Autofill */
|
||||
[FeatureFlag.BlockBrowserInjectionsByDomain]: FALSE,
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
import * as rxjs from "rxjs";
|
||||
|
||||
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { StateService } from "../../../platform/abstractions/state.service";
|
||||
import { StateProvider } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
|
||||
import { MasterPasswordService } from "./master-password.service";
|
||||
|
||||
describe("MasterPasswordService", () => {
|
||||
let sut: MasterPasswordService;
|
||||
|
||||
let stateProvider: MockProxy<StateProvider>;
|
||||
let stateService: MockProxy<StateService>;
|
||||
let keyGenerationService: MockProxy<KeyGenerationService>;
|
||||
let encryptService: MockProxy<EncryptService>;
|
||||
let logService: MockProxy<LogService>;
|
||||
|
||||
const userId = "user-id" as UserId;
|
||||
const mockUserState = {
|
||||
state$: of(null),
|
||||
update: jest.fn().mockResolvedValue(null),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
stateProvider = mock<StateProvider>();
|
||||
stateService = mock<StateService>();
|
||||
keyGenerationService = mock<KeyGenerationService>();
|
||||
encryptService = mock<EncryptService>();
|
||||
logService = mock<LogService>();
|
||||
|
||||
stateProvider.getUser.mockReturnValue(mockUserState as any);
|
||||
|
||||
mockUserState.update.mockReset();
|
||||
|
||||
sut = new MasterPasswordService(
|
||||
stateProvider,
|
||||
stateService,
|
||||
keyGenerationService,
|
||||
encryptService,
|
||||
logService,
|
||||
);
|
||||
});
|
||||
|
||||
describe("setForceSetPasswordReason", () => {
|
||||
it("calls stateProvider with the provided reason and user ID", async () => {
|
||||
const reason = ForceSetPasswordReason.WeakMasterPassword;
|
||||
|
||||
await sut.setForceSetPasswordReason(reason, userId);
|
||||
|
||||
expect(stateProvider.getUser).toHaveBeenCalled();
|
||||
expect(mockUserState.update).toHaveBeenCalled();
|
||||
|
||||
// Call the update function to verify it returns the correct reason
|
||||
const updateFn = mockUserState.update.mock.calls[0][0];
|
||||
expect(updateFn(null)).toBe(reason);
|
||||
});
|
||||
|
||||
it("throws an error if reason is null", async () => {
|
||||
await expect(
|
||||
sut.setForceSetPasswordReason(null as unknown as ForceSetPasswordReason, userId),
|
||||
).rejects.toThrow("Reason is required.");
|
||||
});
|
||||
|
||||
it("throws an error if user ID is null", async () => {
|
||||
await expect(
|
||||
sut.setForceSetPasswordReason(ForceSetPasswordReason.None, null as unknown as UserId),
|
||||
).rejects.toThrow("User ID is required.");
|
||||
});
|
||||
|
||||
it("does not overwrite AdminForcePasswordReset with other reasons except None", async () => {
|
||||
jest
|
||||
.spyOn(sut, "forceSetPasswordReason$")
|
||||
.mockReturnValue(of(ForceSetPasswordReason.AdminForcePasswordReset));
|
||||
|
||||
jest
|
||||
.spyOn(rxjs, "firstValueFrom")
|
||||
.mockResolvedValue(ForceSetPasswordReason.AdminForcePasswordReset);
|
||||
|
||||
await sut.setForceSetPasswordReason(ForceSetPasswordReason.WeakMasterPassword, userId);
|
||||
|
||||
expect(mockUserState.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows overwriting AdminForcePasswordReset with None", async () => {
|
||||
jest
|
||||
.spyOn(sut, "forceSetPasswordReason$")
|
||||
.mockReturnValue(of(ForceSetPasswordReason.AdminForcePasswordReset));
|
||||
|
||||
jest
|
||||
.spyOn(rxjs, "firstValueFrom")
|
||||
.mockResolvedValue(ForceSetPasswordReason.AdminForcePasswordReset);
|
||||
|
||||
await sut.setForceSetPasswordReason(ForceSetPasswordReason.None, userId);
|
||||
|
||||
expect(mockUserState.update).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -148,6 +148,17 @@ export class MasterPasswordService implements InternalMasterPasswordServiceAbstr
|
||||
if (userId == null) {
|
||||
throw new Error("User ID is required.");
|
||||
}
|
||||
|
||||
// Don't overwrite AdminForcePasswordReset with any other reasons other than None
|
||||
// as we must allow a reset when the user has completed admin account recovery
|
||||
const currentReason = await firstValueFrom(this.forceSetPasswordReason$(userId));
|
||||
if (
|
||||
currentReason === ForceSetPasswordReason.AdminForcePasswordReset &&
|
||||
reason !== ForceSetPasswordReason.None
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.stateProvider.getUser(userId, FORCE_SET_PASSWORD_REASON).update((_) => reason);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,8 +124,12 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
* decryption is in progress. The latest decrypted ciphers will be emitted once decryption is complete.
|
||||
*/
|
||||
cipherViews$ = perUserCache$((userId: UserId): Observable<CipherView[] | null> => {
|
||||
return combineLatest([this.encryptedCiphersState(userId).state$, this.localData$(userId)]).pipe(
|
||||
filter(([ciphers]) => ciphers != null), // Skip if ciphers haven't been loaded yor synced yet
|
||||
return combineLatest([
|
||||
this.encryptedCiphersState(userId).state$,
|
||||
this.localData$(userId),
|
||||
this.keyService.cipherDecryptionKeys$(userId, true),
|
||||
]).pipe(
|
||||
filter(([ciphers, _, keys]) => ciphers != null && keys != null), // Skip if ciphers haven't been loaded yor synced yet
|
||||
switchMap(() => this.getAllDecrypted(userId)),
|
||||
);
|
||||
}, this.clearCipherViewsForUser$);
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
bitIconButton="bwi-ellipsis-h"
|
||||
[bitMenuTriggerFor]="overflowMenu"
|
||||
size="small"
|
||||
aria-haspopup
|
||||
></button>
|
||||
<bit-menu #overflowMenu>
|
||||
@for (breadcrumb of overflow; track breadcrumb) {
|
||||
|
||||
@@ -121,8 +121,6 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
showPassword = false;
|
||||
private enforcedMasterPasswordOptions?: MasterPasswordPolicyOptions = undefined;
|
||||
|
||||
forcePasswordResetRoute = "update-temp-password";
|
||||
|
||||
formGroup: FormGroup | null = null;
|
||||
|
||||
// Browser extension properties:
|
||||
@@ -605,8 +603,6 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
ForceSetPasswordReason.WeakMasterPassword,
|
||||
userId,
|
||||
);
|
||||
await this.router.navigate([this.forcePasswordResetRoute]);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// Do not prevent unlock if there is an error evaluating policies
|
||||
|
||||
98
package-lock.json
generated
98
package-lock.json
generated
@@ -87,7 +87,7 @@
|
||||
"@ngtools/webpack": "18.2.12",
|
||||
"@storybook/addon-a11y": "8.5.2",
|
||||
"@storybook/addon-actions": "8.5.2",
|
||||
"@storybook/addon-designs": "8.0.4",
|
||||
"@storybook/addon-designs": "8.2.1",
|
||||
"@storybook/addon-essentials": "8.5.2",
|
||||
"@storybook/addon-interactions": "8.5.2",
|
||||
"@storybook/addon-links": "8.5.2",
|
||||
@@ -162,7 +162,7 @@
|
||||
"prettier": "3.5.3",
|
||||
"prettier-plugin-tailwindcss": "0.6.11",
|
||||
"process": "0.11.10",
|
||||
"remark-gfm": "4.0.0",
|
||||
"remark-gfm": "4.0.1",
|
||||
"rimraf": "6.0.1",
|
||||
"sass": "1.83.4",
|
||||
"sass-loader": "16.0.4",
|
||||
@@ -179,7 +179,7 @@
|
||||
"url": "0.11.4",
|
||||
"util": "0.12.5",
|
||||
"wait-on": "8.0.3",
|
||||
"webpack": "5.97.1",
|
||||
"webpack": "5.99.7",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-server": "5.2.0",
|
||||
"webpack-node-externals": "3.0.0"
|
||||
@@ -10163,9 +10163,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-designs": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-designs/-/addon-designs-8.0.4.tgz",
|
||||
"integrity": "sha512-BrEWks1BRnZis2e8OoE1LhFS+x2d094Tzpbb3jQBve2IfDv/X006RSuy1WyplNxskdYdBESCH45MlRn4lhP5ew==",
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-designs/-/addon-designs-8.2.1.tgz",
|
||||
"integrity": "sha512-orwihs1D5alhh4Qu3BSJKbSgQOdSagvRX/25m5fYZQAaqVErBY0lRR4vCAU/G/STkcdv+MHwIQ5U+0kX5Tm2+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -10175,8 +10175,8 @@
|
||||
"@storybook/blocks": "^8.0.0 || ^8.1.0-0 || ^8.2.0-0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0",
|
||||
"@storybook/components": "^8.0.0 || ^8.1.0-0 || ^8.2.0-0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0",
|
||||
"@storybook/theming": "^8.0.0 || ^8.1.0-0 || ^8.2.0-0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@storybook/blocks": {
|
||||
@@ -31490,9 +31490,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/remark-gfm": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz",
|
||||
"integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
||||
"integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -32259,9 +32259,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
|
||||
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -36640,14 +36640,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.97.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
||||
"version": "5.99.7",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.7.tgz",
|
||||
"integrity": "sha512-CNqKBRMQjwcmKR0idID5va1qlhrqVUKpovi+Ec79ksW8ux7iS1+A6VqzfZXgVYCFRKl7XL5ap3ZoMpwBJxcg0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
@@ -36664,9 +36665,9 @@
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.2.0",
|
||||
"schema-utils": "^4.3.2",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"terser-webpack-plugin": "^5.3.11",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
@@ -37112,39 +37113,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/browserslist": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
@@ -37209,32 +37183,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/websocket-driver": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"@ngtools/webpack": "18.2.12",
|
||||
"@storybook/addon-a11y": "8.5.2",
|
||||
"@storybook/addon-actions": "8.5.2",
|
||||
"@storybook/addon-designs": "8.0.4",
|
||||
"@storybook/addon-designs": "8.2.1",
|
||||
"@storybook/addon-essentials": "8.5.2",
|
||||
"@storybook/addon-interactions": "8.5.2",
|
||||
"@storybook/addon-links": "8.5.2",
|
||||
@@ -124,7 +124,7 @@
|
||||
"prettier": "3.5.3",
|
||||
"prettier-plugin-tailwindcss": "0.6.11",
|
||||
"process": "0.11.10",
|
||||
"remark-gfm": "4.0.0",
|
||||
"remark-gfm": "4.0.1",
|
||||
"rimraf": "6.0.1",
|
||||
"sass": "1.83.4",
|
||||
"sass-loader": "16.0.4",
|
||||
@@ -141,7 +141,7 @@
|
||||
"url": "0.11.4",
|
||||
"util": "0.12.5",
|
||||
"wait-on": "8.0.3",
|
||||
"webpack": "5.97.1",
|
||||
"webpack": "5.99.7",
|
||||
"webpack-cli": "6.0.1",
|
||||
"webpack-dev-server": "5.2.0",
|
||||
"webpack-node-externals": "3.0.0"
|
||||
|
||||
Reference in New Issue
Block a user