Merge branch 'main' into auth/pm-7392/token-service-add-secure-storage-fallback
This commit is contained in:
commit
fa5db1a469
|
@ -1723,6 +1723,12 @@
|
|||
"biometricsNotSupportedDesc": {
|
||||
"message": "Browser biometrics is not supported on this device."
|
||||
},
|
||||
"biometricsNotUnlockedTitle": {
|
||||
"message": "User locked or logged out"
|
||||
},
|
||||
"biometricsNotUnlockedDesc": {
|
||||
"message": "Please unlock this user in the desktop application and try again."
|
||||
},
|
||||
"biometricsFailedTitle": {
|
||||
"message": "Biometrics failed"
|
||||
},
|
||||
|
@ -3157,5 +3163,8 @@
|
|||
},
|
||||
"errorAssigningTargetFolder": {
|
||||
"message": "Error assigning target folder."
|
||||
},
|
||||
"new": {
|
||||
"message": "New"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { Location } from "@angular/common";
|
||||
import { CommonModule, Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Observable, combineLatest, switchMap } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { AvatarModule } from "@bitwarden/components";
|
||||
|
||||
export type CurrentAccount = {
|
||||
id: UserId;
|
||||
|
@ -20,6 +22,8 @@ export type CurrentAccount = {
|
|||
@Component({
|
||||
selector: "app-current-account",
|
||||
templateUrl: "current-account.component.html",
|
||||
standalone: true,
|
||||
imports: [CommonModule, JslibModule, AvatarModule],
|
||||
})
|
||||
export class CurrentAccountComponent {
|
||||
currentAccount$: Observable<CurrentAccount>;
|
||||
|
|
|
@ -20,7 +20,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
|||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
@ -44,7 +43,6 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
|
|||
platformUtilsService: PlatformUtilsService,
|
||||
anonymousHubService: AnonymousHubService,
|
||||
validationService: ValidationService,
|
||||
stateService: StateService,
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
syncService: SyncService,
|
||||
deviceTrustService: DeviceTrustServiceAbstraction,
|
||||
|
@ -67,12 +65,11 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
|
|||
platformUtilsService,
|
||||
anonymousHubService,
|
||||
validationService,
|
||||
stateService,
|
||||
accountService,
|
||||
loginEmailService,
|
||||
deviceTrustService,
|
||||
authRequestService,
|
||||
loginStrategyService,
|
||||
accountService,
|
||||
);
|
||||
super.onSuccessfulLogin = async () => {
|
||||
await syncService.fullSync(true);
|
||||
|
|
|
@ -110,6 +110,7 @@ import { DefaultConfigService } from "@bitwarden/common/platform/services/config
|
|||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
|
||||
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
|
||||
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
|
||||
import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service";
|
||||
import { Fido2ClientService } from "@bitwarden/common/platform/services/fido2/fido2-client.service";
|
||||
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service";
|
||||
|
@ -221,7 +222,6 @@ import { BrowserCryptoService } from "../platform/services/browser-crypto.servic
|
|||
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
|
||||
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
|
||||
import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service";
|
||||
import { BrowserMultithreadEncryptServiceImplementation } from "../platform/services/browser-multithread-encrypt.service.implementation";
|
||||
import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service";
|
||||
import { DefaultBrowserStateService } from "../platform/services/default-browser-state.service";
|
||||
import I18nService from "../platform/services/i18n.service";
|
||||
|
@ -489,14 +489,14 @@ export default class MainBackground {
|
|||
storageServiceProvider,
|
||||
);
|
||||
|
||||
this.encryptService = flagEnabled("multithreadDecryption")
|
||||
? new BrowserMultithreadEncryptServiceImplementation(
|
||||
this.cryptoFunctionService,
|
||||
this.logService,
|
||||
true,
|
||||
this.offscreenDocumentService,
|
||||
)
|
||||
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
|
||||
this.encryptService =
|
||||
flagEnabled("multithreadDecryption") && BrowserApi.isManifestVersion(2)
|
||||
? new MultithreadEncryptServiceImplementation(
|
||||
this.cryptoFunctionService,
|
||||
this.logService,
|
||||
true,
|
||||
)
|
||||
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
|
||||
|
||||
this.singleUserStateProvider = new DefaultSingleUserStateProvider(
|
||||
storageServiceProvider,
|
||||
|
|
|
@ -321,6 +321,15 @@ export class NativeMessagingBackground {
|
|||
type: "danger",
|
||||
});
|
||||
break;
|
||||
} else if (message.response === "not unlocked") {
|
||||
this.messagingService.send("showDialog", {
|
||||
title: { key: "biometricsNotUnlockedTitle" },
|
||||
content: { key: "biometricsNotUnlockedDesc" },
|
||||
acceptButtonText: { key: "ok" },
|
||||
cancelButtonText: null,
|
||||
type: "danger",
|
||||
});
|
||||
break;
|
||||
} else if (message.response === "canceled") {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ export type OffscreenDocumentExtensionMessage = {
|
|||
[key: string]: any;
|
||||
command: string;
|
||||
text?: string;
|
||||
decryptRequest?: string;
|
||||
};
|
||||
|
||||
type OffscreenExtensionMessageEventParams = {
|
||||
|
@ -14,7 +13,6 @@ export type OffscreenDocumentExtensionMessageHandlers = {
|
|||
[key: string]: ({ message, sender }: OffscreenExtensionMessageEventParams) => any;
|
||||
offscreenCopyToClipboard: ({ message }: OffscreenExtensionMessageEventParams) => any;
|
||||
offscreenReadFromClipboard: () => any;
|
||||
offscreenDecryptItems: ({ message }: OffscreenExtensionMessageEventParams) => Promise<string>;
|
||||
};
|
||||
|
||||
export interface OffscreenDocument {
|
||||
|
|
|
@ -1,25 +1,7 @@
|
|||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
|
||||
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
||||
import { flushPromises, sendExtensionRuntimeMessage } from "../../autofill/spec/testing-utils";
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
import BrowserClipboardService from "../services/browser-clipboard.service";
|
||||
|
||||
jest.mock(
|
||||
"@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation",
|
||||
() => ({
|
||||
MultithreadEncryptServiceImplementation: class MultithreadEncryptServiceImplementation {
|
||||
getDecryptedItemsFromWorker = async <T extends InitializerMetadata>(
|
||||
items: Decryptable<T>[],
|
||||
_key: SymmetricCryptoKey,
|
||||
): Promise<string> => JSON.stringify(items);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
describe("OffscreenDocument", () => {
|
||||
const browserApiMessageListenerSpy = jest.spyOn(BrowserApi, "messageListener");
|
||||
const browserClipboardServiceCopySpy = jest.spyOn(BrowserClipboardService, "copy");
|
||||
|
@ -78,37 +60,5 @@ describe("OffscreenDocument", () => {
|
|||
expect(browserClipboardServiceReadSpy).toHaveBeenCalledWith(window);
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleOffscreenDecryptItems", () => {
|
||||
it("returns an empty array as a string if the decrypt request is not present in the message", async () => {
|
||||
let response: string | undefined;
|
||||
sendExtensionRuntimeMessage(
|
||||
{ command: "offscreenDecryptItems" },
|
||||
mock<chrome.runtime.MessageSender>(),
|
||||
(res: string) => (response = res),
|
||||
);
|
||||
await flushPromises();
|
||||
|
||||
expect(response).toBe("[]");
|
||||
});
|
||||
|
||||
it("decrypts the items and sends back the response as a string", async () => {
|
||||
const items = [{ id: "test" }];
|
||||
const key = { id: "test" };
|
||||
const decryptRequest = JSON.stringify({ items, key });
|
||||
let response: string | undefined;
|
||||
|
||||
sendExtensionRuntimeMessage(
|
||||
{ command: "offscreenDecryptItems", decryptRequest },
|
||||
mock<chrome.runtime.MessageSender>(),
|
||||
(res: string) => {
|
||||
response = res;
|
||||
},
|
||||
);
|
||||
await flushPromises();
|
||||
|
||||
expect(response).toBe(JSON.stringify(items));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,35 +1,21 @@
|
|||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
||||
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
|
||||
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
import BrowserClipboardService from "../services/browser-clipboard.service";
|
||||
|
||||
import {
|
||||
OffscreenDocument as OffscreenDocumentInterface,
|
||||
OffscreenDocumentExtensionMessage,
|
||||
OffscreenDocumentExtensionMessageHandlers,
|
||||
OffscreenDocument as OffscreenDocumentInterface,
|
||||
} from "./abstractions/offscreen-document";
|
||||
|
||||
class OffscreenDocument implements OffscreenDocumentInterface {
|
||||
private readonly consoleLogService: ConsoleLogService;
|
||||
private encryptService: MultithreadEncryptServiceImplementation;
|
||||
private consoleLogService: ConsoleLogService = new ConsoleLogService(false);
|
||||
private readonly extensionMessageHandlers: OffscreenDocumentExtensionMessageHandlers = {
|
||||
offscreenCopyToClipboard: ({ message }) => this.handleOffscreenCopyToClipboard(message),
|
||||
offscreenReadFromClipboard: () => this.handleOffscreenReadFromClipboard(),
|
||||
offscreenDecryptItems: ({ message }) => this.handleOffscreenDecryptItems(message),
|
||||
};
|
||||
|
||||
constructor() {
|
||||
const cryptoFunctionService = new WebCryptoFunctionService(self);
|
||||
this.consoleLogService = new ConsoleLogService(false);
|
||||
this.encryptService = new MultithreadEncryptServiceImplementation(
|
||||
cryptoFunctionService,
|
||||
this.consoleLogService,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the offscreen document extension.
|
||||
*/
|
||||
|
@ -53,23 +39,6 @@ class OffscreenDocument implements OffscreenDocumentInterface {
|
|||
return await BrowserClipboardService.read(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the items in the message using the encrypt service.
|
||||
*
|
||||
* @param message - The extension message containing the items to decrypt
|
||||
*/
|
||||
private async handleOffscreenDecryptItems(
|
||||
message: OffscreenDocumentExtensionMessage,
|
||||
): Promise<string> {
|
||||
const { decryptRequest } = message;
|
||||
if (!decryptRequest) {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
const request = JSON.parse(decryptRequest);
|
||||
return await this.encryptService.getDecryptedItemsFromWorker(request.items, request.key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the listener for extension messages.
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
<ng-container *ngIf="show">
|
||||
<ng-container *ngIf="show && !useRefreshVariant">
|
||||
<button type="button" (click)="expand()" appA11yTitle="{{ 'popOutNewWindow' | i18n }}">
|
||||
<i class="bwi bwi-external-link bwi-rotate-270 bwi-lg bwi-fw" aria-hidden="true"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="show && useRefreshVariant">
|
||||
<button
|
||||
bitIconButton="bwi-popout"
|
||||
size="small"
|
||||
type="button"
|
||||
appA11yTitle="{{ 'popOutNewWindow' | i18n }}"
|
||||
[title]="'popOutNewWindow' | i18n"
|
||||
(click)="expand()"
|
||||
></button>
|
||||
</ng-container>
|
||||
|
|
|
@ -2,7 +2,10 @@ import { CommonModule } from "@angular/common";
|
|||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { IconButtonModule } from "@bitwarden/components";
|
||||
|
||||
import BrowserPopupUtils from "../browser-popup-utils";
|
||||
|
||||
|
@ -10,14 +13,20 @@ import BrowserPopupUtils from "../browser-popup-utils";
|
|||
selector: "app-pop-out",
|
||||
templateUrl: "pop-out.component.html",
|
||||
standalone: true,
|
||||
imports: [CommonModule, JslibModule],
|
||||
imports: [CommonModule, JslibModule, IconButtonModule],
|
||||
})
|
||||
export class PopOutComponent implements OnInit {
|
||||
@Input() show = true;
|
||||
useRefreshVariant = false;
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.useRefreshVariant = await this.configService.getFeatureFlag(FeatureFlag.ExtensionRefresh);
|
||||
|
||||
ngOnInit() {
|
||||
if (this.show) {
|
||||
if (
|
||||
(BrowserPopupUtils.inSidebar(window) && this.platformUtilsService.isFirefox()) ||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*ngIf="showBackButton"
|
||||
[title]="'back' | i18n"
|
||||
[ariaLabel]="'back' | i18n"
|
||||
(click)="back()"
|
||||
></button>
|
||||
<h1 bitTypography="h3" class="!tw-mb-0.5 tw-text-headers">{{ pageTitle }}</h1>
|
||||
</div>
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { EncryptionType } from "@bitwarden/common/platform/enums";
|
||||
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
|
||||
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { InitializerKey } from "@bitwarden/common/platform/services/cryptography/initializer-key";
|
||||
import { makeStaticByteArray } from "@bitwarden/common/spec";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
import { OffscreenDocumentService } from "../offscreen-document/abstractions/offscreen-document";
|
||||
|
||||
import { BrowserMultithreadEncryptServiceImplementation } from "./browser-multithread-encrypt.service.implementation";
|
||||
|
||||
describe("BrowserMultithreadEncryptServiceImplementation", () => {
|
||||
let cryptoFunctionServiceMock: MockProxy<CryptoFunctionService>;
|
||||
let logServiceMock: MockProxy<LogService>;
|
||||
let offscreenDocumentServiceMock: MockProxy<OffscreenDocumentService>;
|
||||
let encryptService: BrowserMultithreadEncryptServiceImplementation;
|
||||
const manifestVersionSpy = jest.spyOn(BrowserApi, "manifestVersion", "get");
|
||||
const sendMessageWithResponseSpy = jest.spyOn(BrowserApi, "sendMessageWithResponse");
|
||||
const encType = EncryptionType.AesCbc256_HmacSha256_B64;
|
||||
const key = new SymmetricCryptoKey(makeStaticByteArray(64, 100), encType);
|
||||
const items: Decryptable<InitializerMetadata>[] = [
|
||||
{
|
||||
decrypt: jest.fn(),
|
||||
initializerKey: InitializerKey.Cipher,
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
cryptoFunctionServiceMock = mock<CryptoFunctionService>();
|
||||
logServiceMock = mock<LogService>();
|
||||
offscreenDocumentServiceMock = mock<OffscreenDocumentService>({
|
||||
withDocument: jest.fn((_, __, callback) => callback() as any),
|
||||
});
|
||||
encryptService = new BrowserMultithreadEncryptServiceImplementation(
|
||||
cryptoFunctionServiceMock,
|
||||
logServiceMock,
|
||||
false,
|
||||
offscreenDocumentServiceMock,
|
||||
);
|
||||
manifestVersionSpy.mockReturnValue(3);
|
||||
sendMessageWithResponseSpy.mockResolvedValue(JSON.stringify([]));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("decrypts items using web workers if the chrome.offscreen API is not supported", async () => {
|
||||
manifestVersionSpy.mockReturnValue(2);
|
||||
|
||||
await encryptService.decryptItems([], key);
|
||||
|
||||
expect(offscreenDocumentServiceMock.withDocument).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("decrypts items using the chrome.offscreen API if it is supported", async () => {
|
||||
sendMessageWithResponseSpy.mockResolvedValue(JSON.stringify(items));
|
||||
|
||||
await encryptService.decryptItems(items, key);
|
||||
|
||||
expect(offscreenDocumentServiceMock.withDocument).toHaveBeenCalledWith(
|
||||
[chrome.offscreen.Reason.WORKERS],
|
||||
"Use web worker to decrypt items.",
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(BrowserApi.sendMessageWithResponse).toHaveBeenCalledWith("offscreenDecryptItems", {
|
||||
decryptRequest: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it("returns an empty array if the passed items are not defined", async () => {
|
||||
const result = await encryptService.decryptItems(null, key);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns an empty array if the offscreen document message returns an empty value", async () => {
|
||||
sendMessageWithResponseSpy.mockResolvedValue("");
|
||||
|
||||
const result = await encryptService.decryptItems(items, key);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns an empty array if the offscreen document message returns an empty array", async () => {
|
||||
sendMessageWithResponseSpy.mockResolvedValue("[]");
|
||||
|
||||
const result = await encryptService.decryptItems(items, key);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -1,91 +0,0 @@
|
|||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Decryptable } from "@bitwarden/common/platform/interfaces/decryptable.interface";
|
||||
import { InitializerMetadata } from "@bitwarden/common/platform/interfaces/initializer-metadata.interface";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
|
||||
|
||||
import { BrowserApi } from "../browser/browser-api";
|
||||
import { OffscreenDocumentService } from "../offscreen-document/abstractions/offscreen-document";
|
||||
|
||||
export class BrowserMultithreadEncryptServiceImplementation extends MultithreadEncryptServiceImplementation {
|
||||
constructor(
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
logService: LogService,
|
||||
logMacFailures: boolean,
|
||||
private offscreenDocumentService: OffscreenDocumentService,
|
||||
) {
|
||||
super(cryptoFunctionService, logService, logMacFailures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles decryption of items, will use the offscreen document if supported.
|
||||
*
|
||||
* @param items - The items to decrypt.
|
||||
* @param key - The key to use for decryption.
|
||||
*/
|
||||
async decryptItems<T extends InitializerMetadata>(
|
||||
items: Decryptable<T>[],
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<T[]> {
|
||||
if (!this.isOffscreenDocumentSupported()) {
|
||||
return await super.decryptItems(items, key);
|
||||
}
|
||||
|
||||
return await this.decryptItemsInOffscreenDocument(items, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts items using the offscreen document api.
|
||||
*
|
||||
* @param items - The items to decrypt.
|
||||
* @param key - The key to use for decryption.
|
||||
*/
|
||||
private async decryptItemsInOffscreenDocument<T extends InitializerMetadata>(
|
||||
items: Decryptable<T>[],
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<T[]> {
|
||||
if (items == null || items.length < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const request = {
|
||||
id: Utils.newGuid(),
|
||||
items: items,
|
||||
key: key,
|
||||
};
|
||||
|
||||
const response = await this.offscreenDocumentService.withDocument(
|
||||
[chrome.offscreen.Reason.WORKERS],
|
||||
"Use web worker to decrypt items.",
|
||||
async () => {
|
||||
return (await BrowserApi.sendMessageWithResponse("offscreenDecryptItems", {
|
||||
decryptRequest: JSON.stringify(request),
|
||||
})) as string;
|
||||
},
|
||||
);
|
||||
|
||||
if (!response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const responseItems = JSON.parse(response);
|
||||
if (responseItems?.length < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.initializeItems(responseItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the offscreen document api is supported.
|
||||
*/
|
||||
private isOffscreenDocumentSupported() {
|
||||
return (
|
||||
BrowserApi.isManifestVersion(3) &&
|
||||
typeof chrome !== "undefined" &&
|
||||
typeof chrome.offscreen !== "undefined"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -126,6 +126,7 @@ import "../platform/popup/locales";
|
|||
PopupHeaderComponent,
|
||||
UserVerificationDialogComponent,
|
||||
PopupSectionHeaderComponent,
|
||||
CurrentAccountComponent,
|
||||
],
|
||||
declarations: [
|
||||
ActionButtonsComponent,
|
||||
|
@ -188,7 +189,6 @@ import "../platform/popup/locales";
|
|||
HelpAndFeedbackComponent,
|
||||
AutofillComponent,
|
||||
EnvironmentSelectorComponent,
|
||||
CurrentAccountComponent,
|
||||
AccountSwitcherComponent,
|
||||
VaultV2Component,
|
||||
],
|
||||
|
|
|
@ -4,6 +4,7 @@ import { ActivatedRoute } from "@angular/router";
|
|||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
@ -32,6 +33,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
cipherService: CipherService,
|
||||
route: ActivatedRoute,
|
||||
logService: LogService,
|
||||
accountService: AccountService,
|
||||
private location: Location,
|
||||
) {
|
||||
super(
|
||||
|
@ -42,6 +44,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
i18nService,
|
||||
logService,
|
||||
route,
|
||||
accountService,
|
||||
window,
|
||||
);
|
||||
this.cipherService = cipherService;
|
||||
|
|
|
@ -10,13 +10,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
|
@ -53,7 +53,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
accountService: AccountService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
collectionService: CollectionService,
|
||||
messagingService: MessagingService,
|
||||
|
@ -78,7 +78,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
accountService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
eventCollectionService,
|
||||
|
|
|
@ -1 +1,13 @@
|
|||
<h1>Vault V2 Extension Refresh</h1>
|
||||
<popup-page>
|
||||
<popup-header slot="header" [pageTitle]="'vault' | i18n">
|
||||
<ng-container slot="end">
|
||||
<!-- TODO PM-6826: add selectedVault query param -->
|
||||
<a bitButton buttonType="primary" type="button" routerLink="/add-cipher">
|
||||
<i class="bwi bwi-plus-f" aria-hidden="true"></i>
|
||||
{{ "new" | i18n }}
|
||||
</a>
|
||||
<app-pop-out></app-pop-out>
|
||||
<app-current-account></app-current-account>
|
||||
</ng-container>
|
||||
</popup-header>
|
||||
</popup-page>
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as http from "http";
|
|||
import { OptionValues } from "commander";
|
||||
import * as inquirer from "inquirer";
|
||||
import Separator from "inquirer/lib/objects/separator";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import {
|
||||
LoginStrategyServiceAbstraction,
|
||||
|
@ -15,6 +15,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
|
@ -31,7 +32,6 @@ import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/c
|
|||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||
|
@ -60,7 +60,7 @@ export class LoginCommand {
|
|||
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
protected passwordStrengthService: PasswordStrengthServiceAbstraction,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected stateService: StateService,
|
||||
protected accountService: AccountService,
|
||||
protected cryptoService: CryptoService,
|
||||
protected policyService: PolicyService,
|
||||
protected twoFactorService: TwoFactorService,
|
||||
|
@ -491,7 +491,9 @@ export class LoginCommand {
|
|||
hint?: string;
|
||||
}> {
|
||||
if (this.email == null || this.email === "undefined") {
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
}
|
||||
|
||||
// Get New Master Password
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
|
@ -49,10 +49,11 @@ export class UnlockCommand {
|
|||
}
|
||||
|
||||
await this.setNewSessionKey();
|
||||
const email = await this.stateService.getEmail();
|
||||
const [userId, email] = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||
);
|
||||
const kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdfConfig);
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
const storedMasterKeyHash = await firstValueFrom(
|
||||
this.masterPasswordService.masterKeyHash$(userId),
|
||||
);
|
||||
|
|
|
@ -103,7 +103,7 @@ export class ServeCommand {
|
|||
this.statusCommand = new StatusCommand(
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.syncService,
|
||||
this.serviceContainer.stateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.authService,
|
||||
);
|
||||
this.deleteCommand = new DeleteCommand(
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
import { Response } from "../models/response";
|
||||
|
@ -13,7 +13,7 @@ export class StatusCommand {
|
|||
constructor(
|
||||
private envService: EnvironmentService,
|
||||
private syncService: SyncService,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
private authService: AuthService,
|
||||
) {}
|
||||
|
||||
|
@ -22,8 +22,9 @@ export class StatusCommand {
|
|||
const baseUrl = await this.baseUrl();
|
||||
const status = await this.status();
|
||||
const lastSync = await this.syncService.getLastSync();
|
||||
const userId = await this.stateService.getUserId();
|
||||
const email = await this.stateService.getEmail();
|
||||
const [userId, email] = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||
);
|
||||
|
||||
return Response.success(
|
||||
new TemplateResponse({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as chalk from "chalk";
|
||||
import { program, Command, OptionValues } from "commander";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
|
||||
|
@ -161,7 +161,7 @@ export class Program {
|
|||
this.serviceContainer.passwordGenerationService,
|
||||
this.serviceContainer.passwordStrengthService,
|
||||
this.serviceContainer.platformUtilsService,
|
||||
this.serviceContainer.stateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.cryptoService,
|
||||
this.serviceContainer.policyService,
|
||||
this.serviceContainer.twoFactorService,
|
||||
|
@ -481,7 +481,7 @@ export class Program {
|
|||
const command = new StatusCommand(
|
||||
this.serviceContainer.environmentService,
|
||||
this.serviceContainer.syncService,
|
||||
this.serviceContainer.stateService,
|
||||
this.serviceContainer.accountService,
|
||||
this.serviceContainer.authService,
|
||||
);
|
||||
const response = await command.run();
|
||||
|
@ -603,9 +603,15 @@ export class Program {
|
|||
}
|
||||
|
||||
private async exitIfAuthed() {
|
||||
const authed = await this.serviceContainer.stateService.getIsAuthenticated();
|
||||
const authed = await firstValueFrom(
|
||||
this.serviceContainer.authService.activeAccountStatus$.pipe(
|
||||
map((status) => status > AuthenticationStatus.LoggedOut),
|
||||
),
|
||||
);
|
||||
if (authed) {
|
||||
const email = await this.serviceContainer.stateService.getEmail();
|
||||
const email = await firstValueFrom(
|
||||
this.serviceContainer.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
this.processResponse(Response.error("You are already logged in as " + email + "."), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,7 +224,9 @@ export class SettingsComponent implements OnInit {
|
|||
if ((await this.stateService.getUserId()) == null) {
|
||||
return;
|
||||
}
|
||||
this.currentUserEmail = await this.stateService.getEmail();
|
||||
this.currentUserEmail = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
this.currentUserId = (await this.stateService.getUserId()) as UserId;
|
||||
|
||||
this.availableVaultTimeoutActions$ = this.refreshTimeoutSettings$.pipe(
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
|||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
@ -22,6 +23,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
i18nService: I18nService,
|
||||
route: ActivatedRoute,
|
||||
logService: LogService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
passwordGenerationService,
|
||||
|
@ -31,6 +33,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
i18nService,
|
||||
logService,
|
||||
route,
|
||||
accountService,
|
||||
window,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, OnInit, OnDestroy, Inject } from "@angular/core";
|
||||
import { Subject, firstValueFrom } from "rxjs";
|
||||
import { Subject, firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import {
|
||||
AsyncActionsModule,
|
||||
|
@ -47,7 +47,7 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
|
|||
constructor(
|
||||
@Inject(DIALOG_DATA) private params: LoginApprovalDialogParams,
|
||||
protected authRequestService: AuthRequestServiceAbstraction,
|
||||
protected stateService: StateService,
|
||||
protected accountService: AccountService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected i18nService: I18nService,
|
||||
protected apiService: ApiService,
|
||||
|
@ -74,7 +74,9 @@ export class LoginApprovalComponent implements OnInit, OnDestroy {
|
|||
if (this.notificationId != null) {
|
||||
this.authRequestResponse = await this.apiService.getAuthRequest(this.notificationId);
|
||||
const publicKey = Utils.fromB64ToArray(this.authRequestResponse.publicKey);
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
this.fingerprintPhrase = (
|
||||
await this.cryptoService.getFingerprint(this.email, publicKey)
|
||||
).join("-");
|
||||
|
|
|
@ -21,7 +21,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
|||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
@ -53,7 +52,6 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
|
|||
validationService: ValidationService,
|
||||
private modalService: ModalService,
|
||||
syncService: SyncService,
|
||||
stateService: StateService,
|
||||
loginEmailService: LoginEmailServiceAbstraction,
|
||||
deviceTrustService: DeviceTrustServiceAbstraction,
|
||||
authRequestService: AuthRequestServiceAbstraction,
|
||||
|
@ -75,12 +73,11 @@ export class LoginViaAuthRequestComponent extends BaseLoginWithDeviceComponent {
|
|||
platformUtilsService,
|
||||
anonymousHubService,
|
||||
validationService,
|
||||
stateService,
|
||||
accountService,
|
||||
loginEmailService,
|
||||
deviceTrustService,
|
||||
authRequestService,
|
||||
loginStrategyService,
|
||||
accountService,
|
||||
);
|
||||
|
||||
super.onSuccessfulLogin = () => {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { Injectable, NgZone } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
|
@ -43,6 +45,7 @@ export class NativeMessagingService {
|
|||
private nativeMessageHandler: NativeMessageHandlerService,
|
||||
private dialogService: DialogService,
|
||||
private accountService: AccountService,
|
||||
private authService: AuthService,
|
||||
private ngZone: NgZone,
|
||||
) {}
|
||||
|
||||
|
@ -137,6 +140,19 @@ export class NativeMessagingService {
|
|||
return this.send({ command: "biometricUnlock", response: "not supported" }, appId);
|
||||
}
|
||||
|
||||
const userId =
|
||||
(message.userId as UserId) ??
|
||||
(await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))));
|
||||
|
||||
if (userId == null) {
|
||||
return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId);
|
||||
}
|
||||
|
||||
const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId));
|
||||
if (authStatus !== AuthenticationStatus.Unlocked) {
|
||||
return this.send({ command: "biometricUnlock", response: "not unlocked" }, appId);
|
||||
}
|
||||
|
||||
const biometricUnlockPromise =
|
||||
message.userId == null
|
||||
? firstValueFrom(this.biometricStateService.biometricUnlockEnabled$)
|
||||
|
|
|
@ -7,13 +7,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
|
@ -36,7 +36,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges,
|
|||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
accountService: AccountService,
|
||||
collectionService: CollectionService,
|
||||
messagingService: MessagingService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
|
@ -57,7 +57,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnChanges,
|
|||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
accountService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
eventCollectionService,
|
||||
|
|
|
@ -27,15 +27,26 @@ export class IsPaidOrgGuard implements CanActivate {
|
|||
// Users without billing permission can't access billing
|
||||
if (!org.canEditSubscription) {
|
||||
await this.dialogService.openSimpleDialog({
|
||||
title: { key: "upgradeOrganization" },
|
||||
content: { key: "notAvailableForFreeOrganization" },
|
||||
title: { key: "upgradeOrganizationCloseSecurityGaps" },
|
||||
content: { key: "upgradeOrganizationCloseSecurityGapsDesc" },
|
||||
acceptButtonText: { key: "ok" },
|
||||
cancelButtonText: null,
|
||||
type: "info",
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
this.messagingService.send("upgradeOrganization", { organizationId: org.id });
|
||||
const upgradeConfirmed = await this.dialogService.openSimpleDialog({
|
||||
title: { key: "upgradeOrganizationCloseSecurityGaps" },
|
||||
content: { key: "upgradeOrganizationCloseSecurityGapsDesc" },
|
||||
acceptButtonText: { key: "upgradeOrganization" },
|
||||
type: "info",
|
||||
icon: "bwi-arrow-circle-up",
|
||||
});
|
||||
if (upgradeConfirmed) {
|
||||
await this.router.navigate(["organizations", org.id, "billing", "subscription"], {
|
||||
queryParams: { upgrade: true },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<bit-tab label="{{ 'members' | i18n }}">
|
||||
<p>
|
||||
{{ "editGroupMembersDesc" | i18n }}
|
||||
<span *ngIf="restrictGroupAccess$ | async">
|
||||
<span *ngIf="cannotAddSelfToGroup$ | async">
|
||||
{{ "restrictedGroupAccessDesc" | i18n }}
|
||||
</span>
|
||||
</p>
|
||||
|
@ -52,8 +52,8 @@
|
|||
<bit-tab label="{{ 'collections' | i18n }}">
|
||||
<p>
|
||||
{{ "editGroupCollectionsDesc" | i18n }}
|
||||
<span *ngIf="!(allowAdminAccessToAllCollectionItems$ | async)">
|
||||
{{ "editGroupCollectionsRestrictionsDesc" | i18n }}
|
||||
<span *ngIf="!(canEditAnyCollection$ | async)">
|
||||
{{ "restrictedCollectionAssignmentDesc" | i18n }}
|
||||
</span>
|
||||
</p>
|
||||
<div *ngIf="!(flexibleCollectionsEnabled$ | async)" class="tw-my-3">
|
||||
|
|
|
@ -183,7 +183,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
allowAdminAccessToAllCollectionItems$ = combineLatest([
|
||||
protected allowAdminAccessToAllCollectionItems$ = combineLatest([
|
||||
this.organization$,
|
||||
this.flexibleCollectionsV1Enabled$,
|
||||
]).pipe(
|
||||
|
@ -196,7 +196,16 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||
}),
|
||||
);
|
||||
|
||||
restrictGroupAccess$ = combineLatest([
|
||||
protected canEditAnyCollection$ = combineLatest([
|
||||
this.organization$,
|
||||
this.flexibleCollectionsV1Enabled$,
|
||||
]).pipe(
|
||||
map(([org, flexibleCollectionsV1Enabled]) =>
|
||||
org.canEditAnyCollection(flexibleCollectionsV1Enabled),
|
||||
),
|
||||
);
|
||||
|
||||
protected cannotAddSelfToGroup$ = combineLatest([
|
||||
this.allowAdminAccessToAllCollectionItems$,
|
||||
this.groupDetails$,
|
||||
]).pipe(map(([allowAdminAccess, groupDetails]) => !allowAdminAccess && groupDetails != null));
|
||||
|
@ -229,7 +238,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||
this.orgCollections$,
|
||||
this.orgMembers$,
|
||||
this.groupDetails$,
|
||||
this.restrictGroupAccess$,
|
||||
this.cannotAddSelfToGroup$,
|
||||
this.accountService.activeAccount$,
|
||||
this.organization$,
|
||||
this.flexibleCollectionsV1Enabled$,
|
||||
|
|
|
@ -405,7 +405,7 @@
|
|||
<bit-tab *ngIf="organization.useGroups" [label]="'groups' | i18n">
|
||||
<div class="tw-mb-6">
|
||||
{{
|
||||
(restrictedAccess$ | async)
|
||||
(restrictEditingSelf$ | async)
|
||||
? ("restrictedGroupAccess" | i18n)
|
||||
: ("groupAccessUserDesc" | i18n)
|
||||
}}
|
||||
|
@ -417,15 +417,18 @@
|
|||
[selectorLabelText]="'selectGroups' | i18n"
|
||||
[emptySelectionText]="'noGroupsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
[hideMultiSelect]="restrictedAccess$ | async"
|
||||
[hideMultiSelect]="restrictEditingSelf$ | async"
|
||||
></bit-access-selector>
|
||||
</bit-tab>
|
||||
<bit-tab [label]="'collections' | i18n">
|
||||
<div class="tw-mb-6" *ngIf="restrictedAccess$ | async">
|
||||
{{ "restrictedCollectionAccess" | i18n }}
|
||||
<div class="tw-mb-6" *ngIf="restrictEditingSelf$ | async">
|
||||
{{ "cannotAddYourselfToCollections" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="organization.useGroups && !(restrictedAccess$ | async)" class="tw-mb-6">
|
||||
{{ "userPermissionOverrideHelper" | i18n }}
|
||||
<div *ngIf="organization.useGroups && !(restrictEditingSelf$ | async)" class="tw-mb-6">
|
||||
{{ "userPermissionOverrideHelperDesc" | i18n }}
|
||||
<span *ngIf="!(canEditAnyCollection$ | async)">
|
||||
{{ "restrictedCollectionAssignmentDesc" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="!organization.flexibleCollections" class="tw-mb-6">
|
||||
<bit-form-control>
|
||||
|
@ -454,7 +457,7 @@
|
|||
[selectorLabelText]="'selectCollections' | i18n"
|
||||
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
[hideMultiSelect]="restrictedAccess$ | async"
|
||||
[hideMultiSelect]="restrictEditingSelf$ | async"
|
||||
></bit-access-selector
|
||||
></bit-tab>
|
||||
</bit-tab-group>
|
||||
|
|
|
@ -105,7 +105,9 @@ export class MemberDialogComponent implements OnDestroy {
|
|||
groups: [[] as AccessItemValue[]],
|
||||
});
|
||||
|
||||
protected restrictedAccess$: Observable<boolean>;
|
||||
protected allowAdminAccessToAllCollectionItems$: Observable<boolean>;
|
||||
protected restrictEditingSelf$: Observable<boolean>;
|
||||
protected canEditAnyCollection$: Observable<boolean>;
|
||||
|
||||
protected permissionsGroup = this.formBuilder.group({
|
||||
manageAssignedCollectionsGroup: this.formBuilder.group<Record<string, boolean>>({
|
||||
|
@ -182,43 +184,59 @@ export class MemberDialogComponent implements OnDestroy {
|
|||
? this.userService.get(this.params.organizationId, this.params.organizationUserId)
|
||||
: of(null);
|
||||
|
||||
// The orgUser cannot manage their own Group assignments if collection access is restricted
|
||||
// TODO: fix disabled state of access-selector rows so that any controls are hidden
|
||||
this.restrictedAccess$ = combineLatest([
|
||||
this.allowAdminAccessToAllCollectionItems$ = combineLatest([
|
||||
this.organization$,
|
||||
userDetails$,
|
||||
this.accountService.activeAccount$,
|
||||
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
|
||||
]).pipe(
|
||||
map(([organization, flexibleCollectionsV1Enabled]) => {
|
||||
if (!flexibleCollectionsV1Enabled || !organization.flexibleCollections) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return organization.allowAdminAccessToAllCollectionItems;
|
||||
}),
|
||||
);
|
||||
|
||||
// The orgUser cannot manage their own Group assignments if collection access is restricted
|
||||
this.restrictEditingSelf$ = combineLatest([
|
||||
this.allowAdminAccessToAllCollectionItems$,
|
||||
userDetails$,
|
||||
this.accountService.activeAccount$,
|
||||
]).pipe(
|
||||
map(
|
||||
([organization, userDetails, activeAccount, flexibleCollectionsV1Enabled]) =>
|
||||
// Feature flag conditionals
|
||||
flexibleCollectionsV1Enabled &&
|
||||
organization.flexibleCollections &&
|
||||
// Business logic conditionals
|
||||
userDetails != null &&
|
||||
userDetails.userId == activeAccount.id &&
|
||||
!organization.allowAdminAccessToAllCollectionItems,
|
||||
([allowAdminAccess, userDetails, activeAccount]) =>
|
||||
!allowAdminAccess && userDetails != null && userDetails.userId == activeAccount.id,
|
||||
),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
this.restrictedAccess$.pipe(takeUntil(this.destroy$)).subscribe((restrictedAccess) => {
|
||||
if (restrictedAccess) {
|
||||
this.restrictEditingSelf$.pipe(takeUntil(this.destroy$)).subscribe((restrictEditingSelf) => {
|
||||
if (restrictEditingSelf) {
|
||||
this.formGroup.controls.groups.disable();
|
||||
} else {
|
||||
this.formGroup.controls.groups.enable();
|
||||
}
|
||||
});
|
||||
|
||||
const flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
|
||||
FeatureFlag.FlexibleCollectionsV1,
|
||||
);
|
||||
|
||||
this.canEditAnyCollection$ = combineLatest([
|
||||
this.organization$,
|
||||
flexibleCollectionsV1Enabled$,
|
||||
]).pipe(
|
||||
map(([org, flexibleCollectionsV1Enabled]) =>
|
||||
org.canEditAnyCollection(flexibleCollectionsV1Enabled),
|
||||
),
|
||||
);
|
||||
|
||||
combineLatest({
|
||||
organization: this.organization$,
|
||||
collections: this.collectionAdminService.getAll(this.params.organizationId),
|
||||
userDetails: userDetails$,
|
||||
groups: groups$,
|
||||
flexibleCollectionsV1Enabled: this.configService.getFeatureFlag$(
|
||||
FeatureFlag.FlexibleCollectionsV1,
|
||||
),
|
||||
flexibleCollectionsV1Enabled: flexibleCollectionsV1Enabled$,
|
||||
})
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(
|
||||
|
@ -454,7 +472,7 @@ export class MemberDialogComponent implements OnDestroy {
|
|||
.filter((v) => v.type === AccessItemType.Collection)
|
||||
.map(convertToSelectionView);
|
||||
|
||||
userView.groups = (await firstValueFrom(this.restrictedAccess$))
|
||||
userView.groups = (await firstValueFrom(this.restrictEditingSelf$))
|
||||
? null
|
||||
: this.formGroup.value.groups.map((m) => m.id);
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms";
|
||||
import { map, Observable, Subject, takeUntil } from "rxjs";
|
||||
import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { notAllowedValueAsync } from "@bitwarden/angular/admin-console/validators/not-allowed-value-async.validator";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { PlanSponsorshipType } from "@bitwarden/common/billing/enums/";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
|
||||
interface RequestSponsorshipForm {
|
||||
|
@ -43,7 +43,7 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
|
|||
private syncService: SyncService,
|
||||
private organizationService: OrganizationService,
|
||||
private formBuilder: FormBuilder,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.sponsorshipForm = this.formBuilder.group<RequestSponsorshipForm>({
|
||||
selectedSponsorshipOrgId: new FormControl("", {
|
||||
|
@ -52,7 +52,10 @@ export class SponsoredFamiliesComponent implements OnInit, OnDestroy {
|
|||
sponsorshipEmail: new FormControl("", {
|
||||
validators: [Validators.email],
|
||||
asyncValidators: [
|
||||
notAllowedValueAsync(async () => await this.stateService.getEmail(), true),
|
||||
notAllowedValueAsync(
|
||||
() => firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))),
|
||||
true,
|
||||
),
|
||||
],
|
||||
updateOn: "blur",
|
||||
}),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Injectable } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||
|
@ -52,7 +52,7 @@ export class UserKeyRotationService {
|
|||
// Create master key to validate the master password
|
||||
const masterKey = await this.cryptoService.makeMasterKey(
|
||||
masterPassword,
|
||||
await this.stateService.getEmail(),
|
||||
await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))),
|
||||
await this.kdfConfigService.getKdfConfig(),
|
||||
);
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { Component } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ChangePasswordComponent as BaseChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
|
@ -52,6 +54,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
|||
private keyRotationService: UserKeyRotationService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
|
@ -64,6 +67,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
|||
dialogService,
|
||||
kdfConfigService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -170,7 +174,7 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent {
|
|||
) {
|
||||
const masterKey = await this.cryptoService.makeMasterKey(
|
||||
this.currentMasterPassword,
|
||||
await this.stateService.getEmail(),
|
||||
await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email))),
|
||||
await this.kdfConfigService.getKdfConfig(),
|
||||
);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import { takeUntil } from "rxjs";
|
|||
|
||||
import { ChangePasswordComponent } from "@bitwarden/angular/auth/components/change-password.component";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
|
@ -62,6 +63,7 @@ export class EmergencyAccessTakeoverComponent
|
|||
private dialogRef: DialogRef<EmergencyAccessTakeoverResultType>,
|
||||
kdfConfigService: KdfConfigService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
|
@ -74,6 +76,7 @@ export class EmergencyAccessTakeoverComponent
|
|||
dialogService,
|
||||
kdfConfigService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password/";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
@ -39,7 +39,7 @@ export class EmergencyAddEditCipherComponent extends BaseAddEditComponent {
|
|||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
accountService: AccountService,
|
||||
collectionService: CollectionService,
|
||||
totpService: TotpService,
|
||||
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
|
@ -61,7 +61,7 @@ export class EmergencyAddEditCipherComponent extends BaseAddEditComponent {
|
|||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
accountService,
|
||||
collectionService,
|
||||
totpService,
|
||||
passwordGenerationService,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { Component, Inject } from "@angular/core";
|
||||
import { FormGroup, FormControl, Validators } from "@angular/forms";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||
import { KdfRequest } from "@bitwarden/common/models/request/kdf.request";
|
||||
|
@ -11,7 +13,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { KdfType } from "@bitwarden/common/platform/enums";
|
||||
|
||||
@Component({
|
||||
|
@ -35,7 +36,7 @@ export class ChangeKdfConfirmationComponent {
|
|||
private platformUtilsService: PlatformUtilsService,
|
||||
private cryptoService: CryptoService,
|
||||
private messagingService: MessagingService,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
private logService: LogService,
|
||||
private kdfConfigService: KdfConfigService,
|
||||
@Inject(DIALOG_DATA) params: { kdfConfig: KdfConfig },
|
||||
|
@ -78,7 +79,9 @@ export class ChangeKdfConfirmationComponent {
|
|||
}
|
||||
const masterKey = await this.cryptoService.getOrDeriveMasterKey(masterPassword);
|
||||
request.masterPasswordHash = await this.cryptoService.hashMasterKey(masterPassword, masterKey);
|
||||
const email = await this.stateService.getEmail();
|
||||
const email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
|
||||
const newMasterKey = await this.cryptoService.makeMasterKey(
|
||||
masterPassword,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/auth/models/request/update-two-factor-authenticator.request";
|
||||
|
@ -9,7 +11,6 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
|
|||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
|
@ -51,7 +52,7 @@ export class TwoFactorAuthenticatorComponent
|
|||
userVerificationService: UserVerificationService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
dialogService: DialogService,
|
||||
) {
|
||||
super(
|
||||
|
@ -104,7 +105,9 @@ export class TwoFactorAuthenticatorComponent
|
|||
this.token = null;
|
||||
this.enabled = response.enabled;
|
||||
this.key = response.key;
|
||||
const email = await this.stateService.getEmail();
|
||||
const email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
window.setTimeout(() => {
|
||||
new window.QRious({
|
||||
element: document.getElementById("qr"),
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { Component } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
|
||||
import { TwoFactorEmailRequest } from "@bitwarden/common/auth/models/request/two-factor-email.request";
|
||||
|
@ -10,7 +12,6 @@ import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
|
|||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { TwoFactorBaseComponent } from "./two-factor-base.component";
|
||||
|
@ -35,7 +36,7 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService,
|
||||
userVerificationService: UserVerificationService,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
dialogService: DialogService,
|
||||
) {
|
||||
super(
|
||||
|
@ -90,7 +91,9 @@ export class TwoFactorEmailComponent extends TwoFactorBaseComponent {
|
|||
this.email = response.email;
|
||||
this.enabled = response.enabled;
|
||||
if (!this.enabled && (this.email == null || this.email === "")) {
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Router } from "@angular/router";
|
|||
import { UpdatePasswordComponent as BaseUpdatePasswordComponent } from "@bitwarden/angular/auth/components/update-password.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
|
@ -36,6 +37,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
|
|||
dialogService: DialogService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
router,
|
||||
|
@ -52,6 +54,7 @@ export class UpdatePasswordComponent extends BaseUpdatePasswordComponent {
|
|||
dialogService,
|
||||
kdfConfigService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,10 @@
|
|||
</ng-container>
|
||||
<ng-template #nonEnterprisePlans>
|
||||
<ng-container
|
||||
*ngIf="selectableProduct.product === productTypes.Teams; else fullFeatureList"
|
||||
*ngIf="
|
||||
selectableProduct.product === productTypes.Teams && teamsStarterPlanIsAvailable;
|
||||
else fullFeatureList
|
||||
"
|
||||
>
|
||||
<ul class="tw-pl-0 tw-list-inside tw-mb-0">
|
||||
<li>{{ "includeAllTeamsStarterFeatures" | i18n }}</li>
|
||||
|
|
|
@ -438,6 +438,10 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||
return this.selectedSecretsManagerPlan != null;
|
||||
}
|
||||
|
||||
get teamsStarterPlanIsAvailable() {
|
||||
return this.selectablePlans.some((plan) => plan.type === PlanType.TeamsStarter);
|
||||
}
|
||||
|
||||
changedProduct() {
|
||||
const selectedPlan = this.selectablePlans[0];
|
||||
|
||||
|
@ -511,8 +515,13 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||
if (!this.formGroup.controls.businessOwned.value || this.selectedPlan.canBeUsedByBusiness) {
|
||||
return;
|
||||
}
|
||||
this.formGroup.controls.product.setValue(ProductType.TeamsStarter);
|
||||
this.formGroup.controls.plan.setValue(PlanType.TeamsStarter);
|
||||
if (this.teamsStarterPlanIsAvailable) {
|
||||
this.formGroup.controls.product.setValue(ProductType.TeamsStarter);
|
||||
this.formGroup.controls.plan.setValue(PlanType.TeamsStarter);
|
||||
} else {
|
||||
this.formGroup.controls.product.setValue(ProductType.Teams);
|
||||
this.formGroup.controls.plan.setValue(PlanType.TeamsAnnually);
|
||||
}
|
||||
this.changedProduct();
|
||||
}
|
||||
|
||||
|
@ -763,11 +772,20 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
if (this.currentPlan && this.currentPlan.product !== ProductType.Enterprise) {
|
||||
const upgradedPlan = this.passwordManagerPlans.find((plan) =>
|
||||
this.currentPlan.product === ProductType.Free
|
||||
? plan.type === PlanType.FamiliesAnnually
|
||||
: plan.upgradeSortOrder == this.currentPlan.upgradeSortOrder + 1,
|
||||
);
|
||||
const upgradedPlan = this.passwordManagerPlans.find((plan) => {
|
||||
if (this.currentPlan.product === ProductType.Free) {
|
||||
return plan.type === PlanType.FamiliesAnnually;
|
||||
}
|
||||
|
||||
if (
|
||||
this.currentPlan.product === ProductType.Families &&
|
||||
!this.teamsStarterPlanIsAvailable
|
||||
) {
|
||||
return plan.type === PlanType.TeamsAnnually;
|
||||
}
|
||||
|
||||
return plan.upgradeSortOrder === this.currentPlan.upgradeSortOrder + 1;
|
||||
});
|
||||
|
||||
this.plan = upgradedPlan.type;
|
||||
this.product = upgradedPlan.product;
|
||||
|
|
|
@ -7,16 +7,16 @@ import {
|
|||
Output,
|
||||
ViewChild,
|
||||
} from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
|
||||
import { BitPayInvoiceRequest } from "@bitwarden/common/billing/models/request/bit-pay-invoice.request";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
|
||||
export type PayPalConfig = {
|
||||
businessId?: string;
|
||||
|
@ -52,7 +52,7 @@ export class AddCreditComponent implements OnInit {
|
|||
private region: string;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
private apiService: ApiService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private organizationService: OrganizationService,
|
||||
|
@ -79,8 +79,11 @@ export class AddCreditComponent implements OnInit {
|
|||
if (this.creditAmount == null) {
|
||||
this.creditAmount = "10.00";
|
||||
}
|
||||
this.userId = await this.stateService.getUserId();
|
||||
this.subject = await this.stateService.getEmail();
|
||||
const [userId, email] = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||
);
|
||||
this.userId = userId;
|
||||
this.subject = email;
|
||||
this.email = this.subject;
|
||||
this.ppButtonCustomField = "user_id:" + this.userId;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
|||
import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
@ -26,6 +27,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
logService: LogService,
|
||||
route: ActivatedRoute,
|
||||
private dialogService: DialogService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
passwordGenerationService,
|
||||
|
@ -35,6 +37,7 @@ export class GeneratorComponent extends BaseGeneratorComponent {
|
|||
i18nService,
|
||||
logService,
|
||||
route,
|
||||
accountService,
|
||||
window,
|
||||
);
|
||||
if (platformUtilsService.isSelfHost()) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BreachAccountResponse } from "@bitwarden/common/models/response/breach-account.response";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-breach-report",
|
||||
|
@ -17,11 +18,13 @@ export class BreachReportComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
private auditService: AuditService,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.username = await this.stateService.getEmail();
|
||||
this.username = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
[items]="accessItems"
|
||||
[columnHeader]="'groupSlashMemberColumnHeader' | i18n"
|
||||
[selectorLabelText]="'selectGroupsAndMembers' | i18n"
|
||||
[selectorHelpText]="'userPermissionOverrideHelper' | i18n"
|
||||
[selectorHelpText]="'userPermissionOverrideHelperDesc' | i18n"
|
||||
[emptySelectionText]="'noMembersOrGroupsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
></bit-access-selector>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType, ProductType } from "@bitwarden/common/enums";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
@ -14,7 +15,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
@ -52,7 +52,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
accountService: AccountService,
|
||||
collectionService: CollectionService,
|
||||
protected totpService: TotpService,
|
||||
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
|
@ -74,7 +74,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
|||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
accountService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
eventCollectionService,
|
||||
|
|
|
@ -6,13 +6,13 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
|||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
@ -40,7 +40,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
auditService: AuditService,
|
||||
stateService: StateService,
|
||||
accountService: AccountService,
|
||||
collectionService: CollectionService,
|
||||
totpService: TotpService,
|
||||
passwordGenerationService: PasswordGenerationServiceAbstraction,
|
||||
|
@ -63,7 +63,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||
i18nService,
|
||||
platformUtilsService,
|
||||
auditService,
|
||||
stateService,
|
||||
accountService,
|
||||
collectionService,
|
||||
totpService,
|
||||
passwordGenerationService,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
[items]="accessItems"
|
||||
[columnHeader]="'groupSlashMemberColumnHeader' | i18n"
|
||||
[selectorLabelText]="'selectGroupsAndMembers' | i18n"
|
||||
[selectorHelpText]="'userPermissionOverrideHelper' | i18n"
|
||||
[selectorHelpText]="'userPermissionOverrideHelperDesc' | i18n"
|
||||
[emptySelectionText]="'noMembersOrGroupsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="flexibleCollectionsEnabled$ | async"
|
||||
></bit-access-selector>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
></app-org-vault-header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-3" *ngIf="!organization?.isProviderUser">
|
||||
<div class="col-3" *ngIf="!hideVaultFilters">
|
||||
<div class="groupings">
|
||||
<div class="content">
|
||||
<div class="inner-content">
|
||||
|
@ -26,7 +26,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div [class]="organization?.isProviderUser ? 'col-12' : 'col-9'">
|
||||
<div [class]="hideVaultFilters ? 'col-12' : 'col-9'">
|
||||
<bit-toggle-group
|
||||
*ngIf="showAddAccessToggle && activeFilter.selectedCollectionNode"
|
||||
[selected]="addAccessStatus$ | async"
|
||||
|
|
|
@ -166,6 +166,10 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||
return this._restrictProviderAccessFlagEnabled && this.flexibleCollectionsV1Enabled;
|
||||
}
|
||||
|
||||
protected get hideVaultFilters(): boolean {
|
||||
return this.restrictProviderAccessEnabled && this.organization?.isProviderUser;
|
||||
}
|
||||
|
||||
private searchText$ = new Subject<string>();
|
||||
private refresh$ = new BehaviorSubject<void>(null);
|
||||
private destroy$ = new Subject<void>();
|
||||
|
|
|
@ -6597,7 +6597,7 @@
|
|||
"editGroupCollectionsDesc": {
|
||||
"message": "Grant access to collections by adding them to this group."
|
||||
},
|
||||
"editGroupCollectionsRestrictionsDesc": {
|
||||
"restrictedCollectionAssignmentDesc": {
|
||||
"message": "You can only assign collections you manage."
|
||||
},
|
||||
"accessAllCollectionsDesc": {
|
||||
|
@ -6831,8 +6831,8 @@
|
|||
"selectGroups": {
|
||||
"message": "Select groups"
|
||||
},
|
||||
"userPermissionOverrideHelper": {
|
||||
"message": "Permissions set for a member will replace permissions set by that member's group"
|
||||
"userPermissionOverrideHelperDesc": {
|
||||
"message": "Permissions set for a member will replace permissions set by that member's group."
|
||||
},
|
||||
"noMembersOrGroupsAdded": {
|
||||
"message": "No members or groups added"
|
||||
|
@ -7758,7 +7758,7 @@
|
|||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"cannotAddYourselfToCollections": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
},
|
||||
"assign": {
|
||||
|
@ -8231,5 +8231,11 @@
|
|||
},
|
||||
"protectYourFamilyOrBusiness": {
|
||||
"message": "Protect your family or business"
|
||||
},
|
||||
"upgradeOrganizationCloseSecurityGaps": {
|
||||
"message": "Close security gaps with monitoring reports"
|
||||
},
|
||||
"upgradeOrganizationCloseSecurityGapsDesc": {
|
||||
"message": "Stay ahead of security vulnerabilities by upgrading to a paid plan for enhanced monitoring."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ import {
|
|||
takeUntil,
|
||||
defer,
|
||||
throwError,
|
||||
map,
|
||||
Observable,
|
||||
take,
|
||||
} from "rxjs";
|
||||
|
||||
import {
|
||||
|
@ -67,6 +70,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||
protected data?: Data;
|
||||
protected loading = true;
|
||||
|
||||
private email$: Observable<string>;
|
||||
|
||||
activeAccountId: UserId;
|
||||
|
||||
// Remember device means for the user to trust the device
|
||||
|
@ -104,6 +109,14 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||
async ngOnInit() {
|
||||
this.loading = true;
|
||||
this.activeAccountId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
this.email$ = this.accountService.activeAccount$.pipe(
|
||||
map((a) => a?.email),
|
||||
catchError((err: unknown) => {
|
||||
this.validationService.showError(err);
|
||||
return of(undefined);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
);
|
||||
|
||||
this.setupRememberDeviceValueChanges();
|
||||
|
||||
|
@ -193,16 +206,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||
}),
|
||||
);
|
||||
|
||||
const email$ = from(this.stateService.getEmail()).pipe(
|
||||
catchError((err: unknown) => {
|
||||
this.validationService.showError(err);
|
||||
return of(undefined);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
);
|
||||
|
||||
const autoEnrollStatus = await firstValueFrom(autoEnrollStatus$);
|
||||
const email = await firstValueFrom(email$);
|
||||
const email = await firstValueFrom(this.email$);
|
||||
|
||||
this.data = { state: State.NewUser, organizationId: autoEnrollStatus.id, userEmail: email };
|
||||
this.loading = false;
|
||||
|
@ -211,17 +216,9 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
|
|||
loadUntrustedDeviceData(userDecryptionOptions: UserDecryptionOptions) {
|
||||
this.loading = true;
|
||||
|
||||
const email$ = from(this.stateService.getEmail()).pipe(
|
||||
catchError((err: unknown) => {
|
||||
this.validationService.showError(err);
|
||||
return of(undefined);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
);
|
||||
|
||||
email$
|
||||
this.email$
|
||||
.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
take(1),
|
||||
finalize(() => {
|
||||
this.loading = false;
|
||||
}),
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { Subject, firstValueFrom, map, takeUntil } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
|
||||
|
@ -47,10 +48,13 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
|||
protected dialogService: DialogService,
|
||||
protected kdfConfigService: KdfConfigService,
|
||||
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
protected accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
this.policyService
|
||||
.masterPasswordPolicyOptions$()
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
|
@ -74,7 +78,9 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
const email = await this.stateService.getEmail();
|
||||
const email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
if (this.kdfConfig == null) {
|
||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||
}
|
||||
|
|
|
@ -372,7 +372,9 @@ export class LockComponent implements OnInit, OnDestroy {
|
|||
(await this.vaultTimeoutSettingsService.isBiometricLockSet()) &&
|
||||
((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) ||
|
||||
!this.platformUtilsService.supportsSecureStorage());
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
|
||||
this.webVaultHostname = (await this.environmentService.getEnvironment()).getHostname();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Directive, OnDestroy, OnInit } from "@angular/core";
|
||||
import { IsActiveMatchOptions, Router } from "@angular/router";
|
||||
import { Subject, firstValueFrom, takeUntil } from "rxjs";
|
||||
import { Subject, firstValueFrom, map, takeUntil } from "rxjs";
|
||||
|
||||
import {
|
||||
AuthRequestLoginCredentials,
|
||||
|
@ -29,7 +29,6 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
|||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
|
||||
|
@ -84,12 +83,11 @@ export class LoginViaAuthRequestComponent
|
|||
platformUtilsService: PlatformUtilsService,
|
||||
private anonymousHubService: AnonymousHubService,
|
||||
private validationService: ValidationService,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
private loginEmailService: LoginEmailServiceAbstraction,
|
||||
private deviceTrustService: DeviceTrustServiceAbstraction,
|
||||
private authRequestService: AuthRequestServiceAbstraction,
|
||||
private loginStrategyService: LoginStrategyServiceAbstraction,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
super(environmentService, i18nService, platformUtilsService);
|
||||
|
||||
|
@ -131,7 +129,9 @@ export class LoginViaAuthRequestComponent
|
|||
// Pull email from state for admin auth reqs b/c it is available
|
||||
// This also prevents it from being lost on refresh as the
|
||||
// login service email does not persist.
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
|
||||
|
||||
if (!this.email) {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { Directive, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
|
@ -22,7 +23,7 @@ export class RemovePasswordComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private stateService: StateService,
|
||||
private accountService: AccountService,
|
||||
private syncService: SyncService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
|
@ -33,7 +34,9 @@ export class RemovePasswordComponent implements OnInit {
|
|||
|
||||
async ngOnInit() {
|
||||
this.organization = await this.keyConnectorService.getManagingOrganization();
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
await this.syncService.fullSync(false);
|
||||
this.loading = false;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||
ForceSetPasswordReason = ForceSetPasswordReason;
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
accountService: AccountService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
|
@ -83,6 +83,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
|
|||
dialogService,
|
||||
kdfConfigService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Router } from "@angular/router";
|
|||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
|
@ -48,6 +49,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
|||
dialogService: DialogService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
i18nService,
|
||||
|
@ -60,6 +62,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
|||
dialogService,
|
||||
kdfConfigService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Directive } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
|
@ -61,7 +61,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
|||
protected router: Router,
|
||||
dialogService: DialogService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
private accountService: AccountService,
|
||||
accountService: AccountService,
|
||||
masterPasswordService: InternalMasterPasswordServiceAbstraction,
|
||||
) {
|
||||
super(
|
||||
|
@ -75,6 +75,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
|||
dialogService,
|
||||
kdfConfigService,
|
||||
masterPasswordService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -107,7 +108,9 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
|
|||
}
|
||||
|
||||
async setupSubmitActions(): Promise<boolean> {
|
||||
this.email = await this.stateService.getEmail();
|
||||
this.email = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
||||
import { debounceTime, first, map } from "rxjs/operators";
|
||||
|
||||
import { PasswordGeneratorPolicyOptions } from "@bitwarden/common/admin-console/models/domain/password-generator-policy-options";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
@ -60,6 +61,7 @@ export class GeneratorComponent implements OnInit {
|
|||
protected i18nService: I18nService,
|
||||
protected logService: LogService,
|
||||
protected route: ActivatedRoute,
|
||||
protected accountService: AccountService,
|
||||
private win: Window,
|
||||
) {
|
||||
this.typeOptions = [
|
||||
|
@ -113,7 +115,9 @@ export class GeneratorComponent implements OnInit {
|
|||
this.usernameOptions.subaddressEmail == null ||
|
||||
this.usernameOptions.subaddressEmail === ""
|
||||
) {
|
||||
this.usernameOptions.subaddressEmail = await this.stateService.getEmail();
|
||||
this.usernameOptions.subaddressEmail = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
}
|
||||
if (this.usernameWebsite == null) {
|
||||
this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { DatePipe } from "@angular/common";
|
||||
import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { concatMap, firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
|
||||
import { concatMap, firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
|
@ -11,6 +11,7 @@ import {
|
|||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
|
||||
|
@ -19,7 +20,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
@ -108,7 +108,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
|||
protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected auditService: AuditService,
|
||||
protected stateService: StateService,
|
||||
protected accountService: AccountService,
|
||||
protected collectionService: CollectionService,
|
||||
protected messagingService: MessagingService,
|
||||
protected eventCollectionService: EventCollectionService,
|
||||
|
@ -215,7 +215,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
|||
if (this.personalOwnershipPolicyAppliesToActiveUser) {
|
||||
this.allowPersonal = false;
|
||||
} else {
|
||||
const myEmail = await this.stateService.getEmail();
|
||||
const myEmail = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
);
|
||||
this.ownershipOptions.push({ name: myEmail, value: null });
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
|
||||
|
@ -115,12 +115,14 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||
if (verification.type === VerificationType.OTP) {
|
||||
request.otp = verification.secret;
|
||||
} else {
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
const [userId, email] = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||
);
|
||||
let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId));
|
||||
if (!masterKey && !alreadyHashed) {
|
||||
masterKey = await this.cryptoService.makeMasterKey(
|
||||
verification.secret,
|
||||
await this.stateService.getEmail(),
|
||||
email,
|
||||
await this.kdfConfigService.getKdfConfig(),
|
||||
);
|
||||
}
|
||||
|
@ -138,7 +140,9 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||
* @param verification User-supplied verification data (OTP, MP, PIN, or biometrics)
|
||||
*/
|
||||
async verifyUser(verification: Verification): Promise<boolean> {
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
|
||||
const [userId, email] = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
|
||||
);
|
||||
|
||||
if (verificationHasSecret(verification)) {
|
||||
this.validateSecretInput(verification);
|
||||
|
@ -148,7 +152,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||
case VerificationType.OTP:
|
||||
return this.verifyUserByOTP(verification);
|
||||
case VerificationType.MasterPassword:
|
||||
return this.verifyUserByMasterPassword(verification, userId);
|
||||
return this.verifyUserByMasterPassword(verification, userId, email);
|
||||
case VerificationType.PIN:
|
||||
return this.verifyUserByPIN(verification, userId);
|
||||
case VerificationType.Biometrics:
|
||||
|
@ -174,6 +178,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||
private async verifyUserByMasterPassword(
|
||||
verification: MasterPasswordVerification,
|
||||
userId: UserId,
|
||||
email: string,
|
||||
): Promise<boolean> {
|
||||
if (!userId) {
|
||||
throw new Error("User ID is required. Cannot verify user by master password.");
|
||||
|
@ -183,7 +188,7 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||
if (!masterKey) {
|
||||
masterKey = await this.cryptoService.makeMasterKey(
|
||||
verification.secret,
|
||||
await this.stateService.getEmail(),
|
||||
email,
|
||||
await this.kdfConfigService.getKdfConfig(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -82,8 +82,6 @@ export abstract class StateService<T extends Account = Account> {
|
|||
) => Promise<void>;
|
||||
getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise<string>;
|
||||
setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEmail: (options?: StorageOptions) => Promise<string>;
|
||||
setEmail: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBrowserIntegration: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise<boolean>;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as bigInt from "big-integer";
|
||||
import { Observable, filter, firstValueFrom, map, zip } from "rxjs";
|
||||
import { Observable, combineLatest, filter, firstValueFrom, map, zip } from "rxjs";
|
||||
|
||||
import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions";
|
||||
import { EncryptedOrganizationKeyData } from "../../admin-console/models/data/encrypted-organization-key.data";
|
||||
|
@ -280,11 +280,18 @@ export class CryptoService implements CryptoServiceAbstraction {
|
|||
|
||||
// TODO: Move to MasterPasswordService
|
||||
async getOrDeriveMasterKey(password: string, userId?: UserId) {
|
||||
userId ??= await firstValueFrom(this.stateProvider.activeUserId$);
|
||||
let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId));
|
||||
const [resolvedUserId, email] = await firstValueFrom(
|
||||
combineLatest([this.accountService.activeAccount$, this.accountService.accounts$]).pipe(
|
||||
map(([activeAccount, accounts]) => {
|
||||
userId ??= activeAccount?.id;
|
||||
return [userId, accounts[userId]?.email];
|
||||
}),
|
||||
),
|
||||
);
|
||||
let masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(resolvedUserId));
|
||||
return (masterKey ||= await this.makeMasterKey(
|
||||
password,
|
||||
await this.stateService.getEmail({ userId: userId }),
|
||||
email,
|
||||
await this.kdfConfigService.getKdfConfig(),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -19,36 +19,17 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
|
|||
private clear$ = new Subject<void>();
|
||||
|
||||
/**
|
||||
* Decrypts items using a web worker if the environment supports it.
|
||||
* Will fall back to the main thread if the window object is not available.
|
||||
* Sends items to a web worker to decrypt them.
|
||||
* This utilises multithreading to decrypt items faster without interrupting other operations (e.g. updating UI).
|
||||
*/
|
||||
async decryptItems<T extends InitializerMetadata>(
|
||||
items: Decryptable<T>[],
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<T[]> {
|
||||
if (typeof window === "undefined") {
|
||||
return super.decryptItems(items, key);
|
||||
}
|
||||
|
||||
if (items == null || items.length < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const decryptedItems = await this.getDecryptedItemsFromWorker(items, key);
|
||||
const parsedItems = JSON.parse(decryptedItems);
|
||||
|
||||
return this.initializeItems(parsedItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends items to a web worker to decrypt them. This utilizes multithreading to decrypt items
|
||||
* faster without interrupting other operations (e.g. updating UI). This method returns values
|
||||
* prior to deserialization to support forwarding results to another party
|
||||
*/
|
||||
async getDecryptedItemsFromWorker<T extends InitializerMetadata>(
|
||||
items: Decryptable<T>[],
|
||||
key: SymmetricCryptoKey,
|
||||
): Promise<string> {
|
||||
this.logService.info("Starting decryption using multithreading");
|
||||
|
||||
this.worker ??= new Worker(
|
||||
|
@ -72,20 +53,19 @@ export class MultithreadEncryptServiceImplementation extends EncryptServiceImple
|
|||
return await firstValueFrom(
|
||||
fromEvent(this.worker, "message").pipe(
|
||||
filter((response: MessageEvent) => response.data?.id === request.id),
|
||||
map((response) => response.data.items),
|
||||
map((response) => JSON.parse(response.data.items)),
|
||||
map((items) =>
|
||||
items.map((jsonItem: Jsonify<T>) => {
|
||||
const initializer = getClassInitializer<T>(jsonItem.initializerKey);
|
||||
return initializer(jsonItem);
|
||||
}),
|
||||
),
|
||||
takeUntil(this.clear$),
|
||||
defaultIfEmpty("[]"),
|
||||
defaultIfEmpty([]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected initializeItems<T extends InitializerMetadata>(items: Jsonify<T>[]): T[] {
|
||||
return items.map((jsonItem: Jsonify<T>) => {
|
||||
const initializer = getClassInitializer<T>(jsonItem.initializerKey);
|
||||
return initializer(jsonItem);
|
||||
});
|
||||
}
|
||||
|
||||
private clear() {
|
||||
this.clear$.next();
|
||||
this.worker?.terminate();
|
||||
|
|
|
@ -347,23 +347,6 @@ export class StateService<
|
|||
: await this.secureStorageService.save(DDG_SHARED_KEY, value, options);
|
||||
}
|
||||
|
||||
async getEmail(options?: StorageOptions): Promise<string> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.profile?.email;
|
||||
}
|
||||
|
||||
async setEmail(value: string, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.profile.email = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getEnableBrowserIntegration(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { CommonModule } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CalloutModule } from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
|
@ -35,7 +36,7 @@ export class ExportScopeCalloutComponent implements OnInit {
|
|||
|
||||
constructor(
|
||||
protected organizationService: OrganizationService,
|
||||
protected stateService: StateService,
|
||||
protected accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
|
@ -58,7 +59,9 @@ export class ExportScopeCalloutComponent implements OnInit {
|
|||
: {
|
||||
title: "exportingPersonalVaultTitle",
|
||||
description: "exportingIndividualVaultDescription",
|
||||
scopeIdentifier: await this.stateService.getEmail(),
|
||||
scopeIdentifier: await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue