mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-29 22:31:29 +01:00
[PM-10607] Require userId for getKeyForCipherKeyDecryption (#10509)
* updated cipher service to stop using the deprecated getUserKeyWithLegacySupport and use the version that requires a user id * Added account service mock * fixed cipher test * Fixed test * removed async from encryptCipher * updated encryptSharedCipher to pass userId to the encrypt function * Pass userId to getUserKeyWithLegacySupport on encryptSharedCipher * pass in userid when setting masterKeyEncryptedUserKey * Added activer usedId to new web refresh function
This commit is contained in:
parent
ed719f835a
commit
dedd7f1b5c
@ -2,6 +2,7 @@ import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { ExtensionCommand } from "@bitwarden/common/autofill/constants";
|
||||
@ -12,6 +13,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
|
||||
@ -55,6 +57,7 @@ describe("NotificationBackground", () => {
|
||||
const logService = mock<LogService>();
|
||||
const themeStateService = mock<ThemeStateService>();
|
||||
const configService = mock<ConfigService>();
|
||||
const accountService = mock<AccountService>();
|
||||
|
||||
beforeEach(() => {
|
||||
notificationBackground = new NotificationBackground(
|
||||
@ -69,6 +72,7 @@ describe("NotificationBackground", () => {
|
||||
logService,
|
||||
themeStateService,
|
||||
configService,
|
||||
accountService,
|
||||
);
|
||||
});
|
||||
|
||||
@ -691,6 +695,13 @@ describe("NotificationBackground", () => {
|
||||
});
|
||||
|
||||
describe("saveOrUpdateCredentials", () => {
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||
id: "testId" as UserId,
|
||||
email: "test@example.com",
|
||||
emailVerified: true,
|
||||
name: "Test User",
|
||||
});
|
||||
|
||||
let getDecryptedCipherByIdSpy: jest.SpyInstance;
|
||||
let getAllDecryptedForUrlSpy: jest.SpyInstance;
|
||||
let updatePasswordSpy: jest.SpyInstance;
|
||||
@ -727,6 +738,8 @@ describe("NotificationBackground", () => {
|
||||
updateWithServerSpy = jest.spyOn(cipherService, "updateWithServer");
|
||||
folderExistsSpy = jest.spyOn(notificationBackground as any, "folderExists");
|
||||
cipherEncryptSpy = jest.spyOn(cipherService, "encrypt");
|
||||
|
||||
accountService.activeAccount$ = activeAccountSubject;
|
||||
});
|
||||
|
||||
it("skips saving the cipher if the notification queue does not have a tab that is related to the sender", async () => {
|
||||
@ -981,7 +994,7 @@ describe("NotificationBackground", () => {
|
||||
queueMessage,
|
||||
null,
|
||||
);
|
||||
expect(cipherEncryptSpy).toHaveBeenCalledWith(cipherView);
|
||||
expect(cipherEncryptSpy).toHaveBeenCalledWith(cipherView, "testId");
|
||||
expect(createWithServerSpy).toHaveBeenCalled();
|
||||
expect(tabSendMessageSpy).toHaveBeenCalledWith(sender.tab, {
|
||||
command: "saveCipherAttemptCompleted",
|
||||
@ -1020,7 +1033,7 @@ describe("NotificationBackground", () => {
|
||||
sendMockExtensionMessage(message, sender);
|
||||
await flushPromises();
|
||||
|
||||
expect(cipherEncryptSpy).toHaveBeenCalledWith(cipherView);
|
||||
expect(cipherEncryptSpy).toHaveBeenCalledWith(cipherView, "testId");
|
||||
expect(createWithServerSpy).toThrow(errorMessage);
|
||||
expect(tabSendMessageSpy).not.toHaveBeenCalledWith(sender.tab, {
|
||||
command: "addedCipher",
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
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 {
|
||||
@ -90,6 +91,7 @@ export default class NotificationBackground {
|
||||
private logService: LogService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private configService: ConfigService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
@ -556,7 +558,11 @@ export default class NotificationBackground {
|
||||
return;
|
||||
}
|
||||
|
||||
const cipher = await this.cipherService.encrypt(newCipher);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
const cipher = await this.cipherService.encrypt(newCipher, activeUserId);
|
||||
try {
|
||||
await this.cipherService.createWithServer(cipher);
|
||||
await BrowserApi.tabSendMessage(tab, { command: "saveCipherAttemptCompleted" });
|
||||
@ -594,7 +600,11 @@ export default class NotificationBackground {
|
||||
return;
|
||||
}
|
||||
|
||||
const cipher = await this.cipherService.encrypt(cipherView);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
const cipher = await this.cipherService.encrypt(cipherView, activeUserId);
|
||||
try {
|
||||
// We've only updated the password, no need to broadcast editedCipher message
|
||||
await this.cipherService.updateWithServer(cipher);
|
||||
@ -634,7 +644,13 @@ export default class NotificationBackground {
|
||||
private async getDecryptedCipherById(cipherId: string) {
|
||||
const cipher = await this.cipherService.get(cipherId);
|
||||
if (cipher != null && cipher.type === CipherType.Login) {
|
||||
return await cipher.decrypt(await this.cipherService.getKeyForCipherKeyDecryption(cipher));
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
return await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
} from "rxjs";
|
||||
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -80,6 +81,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
private browserMessagingApi: ZonedMessageListenerService,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private fido2UserVerificationService: Fido2UserVerificationService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -156,11 +158,15 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
case "PickCredentialRequest": {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
this.ciphers = await Promise.all(
|
||||
message.cipherIds.map(async (cipherId) => {
|
||||
const cipher = await this.cipherService.get(cipherId);
|
||||
return cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
}),
|
||||
);
|
||||
@ -172,11 +178,15 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
case "InformExcludedCredentialRequest": {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
this.ciphers = await Promise.all(
|
||||
message.existingCipherIds.map(async (cipherId) => {
|
||||
const cipher = await this.cipherService.get(cipherId);
|
||||
return cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
}),
|
||||
);
|
||||
@ -378,8 +388,12 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private async createNewCipher(name: string, username: string) {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
this.buildCipher(name, username);
|
||||
const cipher = await this.cipherService.encrypt(this.cipher);
|
||||
const cipher = await this.cipherService.encrypt(this.cipher, activeUserId);
|
||||
try {
|
||||
await this.cipherService.createWithServer(cipher);
|
||||
this.cipher.id = cipher.id;
|
||||
|
@ -956,6 +956,7 @@ export default class MainBackground {
|
||||
this.collectionService,
|
||||
this.cryptoService,
|
||||
this.pinService,
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.individualVaultExportService = new IndividualVaultExportService(
|
||||
@ -975,6 +976,7 @@ export default class MainBackground {
|
||||
this.cryptoFunctionService,
|
||||
this.collectionService,
|
||||
this.kdfConfigService,
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.exportService = new VaultExportService(
|
||||
@ -1000,6 +1002,7 @@ export default class MainBackground {
|
||||
this.cipherService,
|
||||
this.fido2UserInterfaceService,
|
||||
this.syncService,
|
||||
this.accountService,
|
||||
this.logService,
|
||||
);
|
||||
const fido2ActiveRequestManager = new Fido2ActiveRequestManager();
|
||||
@ -1093,6 +1096,7 @@ export default class MainBackground {
|
||||
this.logService,
|
||||
this.themeStateService,
|
||||
this.configService,
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.filelessImporterBackground = new FilelessImporterBackground(
|
||||
|
@ -3,9 +3,10 @@ import { Component } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { ReactiveFormsModule } from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Observable, combineLatest, first, switchMap } from "rxjs";
|
||||
import { Observable, combineLatest, first, map, switchMap } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
@ -52,18 +53,24 @@ export class AssignCollections {
|
||||
private location: Location,
|
||||
private collectionService: CollectionService,
|
||||
private cipherService: CipherService,
|
||||
private accountService: AccountService,
|
||||
route: ActivatedRoute,
|
||||
) {
|
||||
const $cipher: Observable<CipherView> = route.queryParams.pipe(
|
||||
const cipher$: Observable<CipherView> = route.queryParams.pipe(
|
||||
switchMap(({ cipherId }) => this.cipherService.get(cipherId)),
|
||||
switchMap((cipherDomain) =>
|
||||
this.cipherService
|
||||
.getKeyForCipherKeyDecryption(cipherDomain)
|
||||
.then(cipherDomain.decrypt.bind(cipherDomain)),
|
||||
this.accountService.activeAccount$.pipe(
|
||||
map((account) => account?.id),
|
||||
switchMap((userId) =>
|
||||
this.cipherService
|
||||
.getKeyForCipherKeyDecryption(cipherDomain, userId)
|
||||
.then(cipherDomain.decrypt.bind(cipherDomain)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
combineLatest([$cipher, this.collectionService.decryptedCollections$])
|
||||
combineLatest([cipher$, this.collectionService.decryptedCollections$])
|
||||
.pipe(takeUntilDestroyed(), first())
|
||||
.subscribe(([cipherView, collections]) => {
|
||||
let availableCollections = collections.filter((c) => !c.readOnly);
|
||||
|
@ -5,10 +5,14 @@ import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { ButtonComponent } from "@bitwarden/components";
|
||||
import { CipherAttachmentsComponent } from "@bitwarden/vault";
|
||||
@ -46,6 +50,9 @@ describe("AttachmentsV2Component", () => {
|
||||
const navigate = jest.fn();
|
||||
const back = jest.fn().mockResolvedValue(undefined);
|
||||
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
|
||||
beforeEach(async () => {
|
||||
back.mockClear();
|
||||
navigate.mockClear();
|
||||
@ -66,6 +73,10 @@ describe("AttachmentsV2Component", () => {
|
||||
queryParams,
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
},
|
||||
],
|
||||
})
|
||||
.overrideComponent(AttachmentsV2Component, {
|
||||
|
@ -5,10 +5,13 @@ import { BehaviorSubject } from "rxjs";
|
||||
|
||||
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 { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherId } from "@bitwarden/common/types/guid";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { CipherId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -51,6 +54,9 @@ describe("OpenAttachmentsComponent", () => {
|
||||
const getOrganization = jest.fn().mockResolvedValue(org);
|
||||
const showFilePopoutMessage = jest.fn().mockReturnValue(false);
|
||||
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
|
||||
beforeEach(async () => {
|
||||
openCurrentPagePopout.mockClear();
|
||||
getCipher.mockClear();
|
||||
@ -82,6 +88,10 @@ describe("OpenAttachmentsComponent", () => {
|
||||
provide: FilePopoutUtilsService,
|
||||
useValue: { showFilePopoutMessage },
|
||||
},
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
@ -2,9 +2,11 @@ import { CommonModule } from "@angular/common";
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { Router } from "@angular/router";
|
||||
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 { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -48,6 +50,7 @@ export class OpenAttachmentsComponent implements OnInit {
|
||||
private toastService: ToastService,
|
||||
private i18nService: I18nService,
|
||||
private filePopoutUtilsService: FilePopoutUtilsService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$
|
||||
.pipe(takeUntilDestroyed())
|
||||
@ -64,8 +67,11 @@ export class OpenAttachmentsComponent implements OnInit {
|
||||
}
|
||||
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipher = await cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain, activeUserId),
|
||||
);
|
||||
|
||||
if (!cipher.organizationId) {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { booleanAttribute, Component, Input } from "@angular/core";
|
||||
import { Router, RouterModule } from "@angular/router";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
@ -50,6 +52,7 @@ export class ItemMoreOptionsComponent {
|
||||
private router: Router,
|
||||
private i18nService: I18nService,
|
||||
private vaultPopupAutofillService: VaultPopupAutofillService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
get canEdit() {
|
||||
@ -108,7 +111,10 @@ export class ItemMoreOptionsComponent {
|
||||
*/
|
||||
async toggleFavorite() {
|
||||
this.cipher.favorite = !this.cipher.favorite;
|
||||
const encryptedCipher = await this.cipherService.encrypt(this.cipher);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const encryptedCipher = await this.cipherService.encrypt(this.cipher, activeUserId);
|
||||
await this.cipherService.updateWithServer(encryptedCipher);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
|
@ -3,10 +3,14 @@ import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
|
||||
@ -30,6 +34,9 @@ describe("ViewV2Component", () => {
|
||||
type: CipherType.Login,
|
||||
};
|
||||
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
|
||||
const mockCipherService = {
|
||||
get: jest.fn().mockResolvedValue({ decrypt: jest.fn().mockResolvedValue(mockCipher) }),
|
||||
getKeyForCipherKeyDecryption: jest.fn().mockResolvedValue({}),
|
||||
@ -59,6 +66,10 @@ describe("ViewV2Component", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
@ -3,10 +3,11 @@ import { Component } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Observable, switchMap } from "rxjs";
|
||||
import { firstValueFrom, map, Observable, switchMap } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@ -67,6 +68,7 @@ export class ViewV2Component {
|
||||
private dialogService: DialogService,
|
||||
private logService: LogService,
|
||||
private toastService: ToastService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.subscribeToParams();
|
||||
}
|
||||
@ -103,7 +105,12 @@ export class ViewV2Component {
|
||||
|
||||
async getCipherData(id: string) {
|
||||
const cipher = await this.cipherService.get(id);
|
||||
return await cipher.decrypt(await this.cipherService.getKeyForCipherKeyDecryption(cipher));
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
return await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
}
|
||||
|
||||
async editCipher() {
|
||||
|
@ -5,6 +5,7 @@ import { first } from "rxjs/operators";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
@ -36,6 +37,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -49,6 +51,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { first } from "rxjs/operators";
|
||||
|
||||
import { CollectionsComponent as BaseCollectionsComponent } from "@bitwarden/angular/admin-console/components/collections.component";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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";
|
||||
@ -28,6 +29,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
|
||||
private location: Location,
|
||||
logService: LogService,
|
||||
configService: ConfigService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
@ -37,6 +39,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
|
||||
organizationService,
|
||||
logService,
|
||||
configService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { ActivatedRoute } from "@angular/router";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { PasswordHistoryComponent as BasePasswordHistoryComponent } from "@bitwarden/angular/vault/components/password-history.component";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@ -18,10 +19,11 @@ export class PasswordHistoryComponent extends BasePasswordHistoryComponent imple
|
||||
cipherService: CipherService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
accountService: AccountService,
|
||||
private location: Location,
|
||||
private route: ActivatedRoute,
|
||||
) {
|
||||
super(cipherService, platformUtilsService, i18nService, window);
|
||||
super(cipherService, platformUtilsService, i18nService, accountService, window);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -4,6 +4,7 @@ import { first } from "rxjs/operators";
|
||||
|
||||
import { ShareComponent as BaseShareComponent } from "@bitwarden/angular/components/share.component";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
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";
|
||||
@ -25,6 +26,7 @@ export class ShareComponent extends BaseShareComponent implements OnInit {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
organizationService: OrganizationService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
@ -33,6 +35,7 @@ export class ShareComponent extends BaseShareComponent implements OnInit {
|
||||
cipherService,
|
||||
logService,
|
||||
organizationService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,13 @@ import { DatePipe, Location } from "@angular/common";
|
||||
import { ChangeDetectorRef, Component, NgZone, OnInit, OnDestroy } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Subject, firstValueFrom, takeUntil, Subscription } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
import { first, map } from "rxjs/operators";
|
||||
|
||||
import { ViewComponent as BaseViewComponent } from "@bitwarden/angular/vault/components/view.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
@ -97,6 +98,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
datePipe: DatePipe,
|
||||
accountService: AccountService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {
|
||||
super(
|
||||
@ -120,6 +122,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
datePipe,
|
||||
accountService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
@ -267,7 +270,10 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
|
||||
this.cipher.login.uris.push(loginUri);
|
||||
|
||||
try {
|
||||
const cipher: Cipher = await this.cipherService.encrypt(this.cipher);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipher: Cipher = await this.cipherService.encrypt(this.cipher, activeUserId);
|
||||
await this.cipherService.updateWithServer(cipher);
|
||||
this.platformUtilsService.showToast(
|
||||
"success",
|
||||
|
@ -2,10 +2,13 @@ import { TestBed } from "@angular/core/testing";
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { subscribeTo } from "@bitwarden/common/spec";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { FakeAccountService, mockAccountServiceWith, subscribeTo } from "@bitwarden/common/spec";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
@ -40,6 +43,9 @@ describe("VaultPopupAutofillService", () => {
|
||||
const mockCipherService = mock<CipherService>();
|
||||
const mockMessagingService = mock<MessagingService>();
|
||||
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(BrowserPopupUtils, "inPopout").mockReturnValue(false);
|
||||
jest.spyOn(BrowserApi, "getTabFromCurrentWindow").mockResolvedValue(mockCurrentTab);
|
||||
@ -55,6 +61,10 @@ describe("VaultPopupAutofillService", () => {
|
||||
{ provide: PasswordRepromptService, useValue: mockPasswordRepromptService },
|
||||
{ provide: CipherService, useValue: mockCipherService },
|
||||
{ provide: MessagingService, useValue: mockMessagingService },
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@ -311,7 +321,7 @@ describe("VaultPopupAutofillService", () => {
|
||||
expect(result).toBe(true);
|
||||
expect(mockCipher.login.uris).toHaveLength(1);
|
||||
expect(mockCipher.login.uris[0].uri).toBe(mockCurrentTab.url);
|
||||
expect(mockCipherService.encrypt).toHaveBeenCalledWith(mockCipher);
|
||||
expect(mockCipherService.encrypt).toHaveBeenCalledWith(mockCipher, mockUserId);
|
||||
expect(mockCipherService.updateWithServer).toHaveBeenCalledWith(mockEncryptedCipher);
|
||||
});
|
||||
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
switchMap,
|
||||
} from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -72,6 +73,7 @@ export class VaultPopupAutofillService {
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private cipherService: CipherService,
|
||||
private messagingService: MessagingService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this._currentPageDetails$.subscribe();
|
||||
}
|
||||
@ -221,7 +223,10 @@ export class VaultPopupAutofillService {
|
||||
cipher.login.uris.push(loginUri);
|
||||
|
||||
try {
|
||||
const encCipher = await this.cipherService.encrypt(cipher);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const encCipher = await this.cipherService.encrypt(cipher, activeUserId);
|
||||
await this.cipherService.updateWithServer(encCipher);
|
||||
this.messagingService.send("editedCipher");
|
||||
return true;
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
|
||||
import { Response } from "../../models/response";
|
||||
@ -5,7 +8,10 @@ import { CliUtils } from "../../utils";
|
||||
import { CipherResponse } from "../../vault/models/cipher.response";
|
||||
|
||||
export class ShareCommand {
|
||||
constructor(private cipherService: CipherService) {}
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async run(id: string, organizationId: string, requestJson: string): Promise<Response> {
|
||||
if (process.env.BW_SERVE !== "true" && (requestJson == null || requestJson === "")) {
|
||||
@ -45,14 +51,18 @@ export class ShareCommand {
|
||||
if (cipher.organizationId != null) {
|
||||
return Response.badRequest("This item already belongs to an organization.");
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
try {
|
||||
await this.cipherService.shareWithServer(cipherView, organizationId, req);
|
||||
await this.cipherService.shareWithServer(cipherView, organizationId, req, activeUserId);
|
||||
const updatedCipher = await this.cipherService.get(cipher.id);
|
||||
const decCipher = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
||||
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
|
||||
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
|
||||
@ -24,6 +27,7 @@ export class EditCommand {
|
||||
private cryptoService: CryptoService,
|
||||
private apiService: ApiService,
|
||||
private folderApiService: FolderApiServiceAbstraction,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async run(
|
||||
@ -77,18 +81,21 @@ export class EditCommand {
|
||||
return Response.notFound();
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
let cipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
if (cipherView.isDeleted) {
|
||||
return Response.badRequest("You may not edit a deleted item. Use the restore command first.");
|
||||
}
|
||||
cipherView = CipherExport.toView(req, cipherView);
|
||||
const encCipher = await this.cipherService.encrypt(cipherView);
|
||||
const encCipher = await this.cipherService.encrypt(cipherView, activeUserId);
|
||||
try {
|
||||
const updatedCipher = await this.cipherService.updateWithServer(encCipher);
|
||||
const decCipher = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
@ -110,9 +117,12 @@ export class EditCommand {
|
||||
|
||||
cipher.collectionIds = req;
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const updatedCipher = await this.cipherService.saveCollectionsWithServer(cipher);
|
||||
const decCipher = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
@ -6,6 +6,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.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 { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { CardExport } from "@bitwarden/common/models/export/card.export";
|
||||
@ -62,6 +63,7 @@ export class GetCommand extends DownloadCommand {
|
||||
private organizationService: OrganizationService,
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
super(cryptoService);
|
||||
}
|
||||
@ -110,9 +112,12 @@ export class GetCommand extends DownloadCommand {
|
||||
let decCipher: CipherView = null;
|
||||
if (Utils.isGuid(id)) {
|
||||
const cipher = await this.cipherService.get(id);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
if (cipher != null) {
|
||||
decCipher = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
}
|
||||
} else if (id.trim() !== "") {
|
||||
|
@ -63,6 +63,7 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.organizationService,
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
this.listCommand = new ListCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
@ -82,6 +83,7 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.folderApiService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.organizationService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
this.editCommand = new EditCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
@ -89,6 +91,7 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.cryptoService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.folderApiService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
this.generateCommand = new GenerateCommand(
|
||||
this.serviceContainer.passwordGenerationService,
|
||||
@ -114,7 +117,10 @@ export class OssServeConfigurator {
|
||||
this.serviceContainer.organizationUserService,
|
||||
);
|
||||
this.restoreCommand = new RestoreCommand(this.serviceContainer.cipherService);
|
||||
this.shareCommand = new ShareCommand(this.serviceContainer.cipherService);
|
||||
this.shareCommand = new ShareCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
this.lockCommand = new LockCommand(this.serviceContainer.vaultTimeoutService);
|
||||
this.unlockCommand = new UnlockCommand(
|
||||
this.serviceContainer.accountService,
|
||||
|
@ -714,6 +714,7 @@ export class ServiceContainer {
|
||||
this.collectionService,
|
||||
this.cryptoService,
|
||||
this.pinService,
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.individualExportService = new IndividualVaultExportService(
|
||||
@ -733,6 +734,7 @@ export class ServiceContainer {
|
||||
this.cryptoFunctionService,
|
||||
this.collectionService,
|
||||
this.kdfConfigService,
|
||||
this.accountService,
|
||||
);
|
||||
|
||||
this.exportService = new VaultExportService(
|
||||
|
@ -149,6 +149,7 @@ export class SendProgram extends BaseProgram {
|
||||
this.serviceContainer.organizationService,
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await cmd.run("template", object, null);
|
||||
this.processResponse(response);
|
||||
|
@ -184,6 +184,7 @@ export class VaultProgram extends BaseProgram {
|
||||
this.serviceContainer.organizationService,
|
||||
this.serviceContainer.eventCollectionService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await command.run(object, id, cmd);
|
||||
this.processResponse(response);
|
||||
@ -227,6 +228,7 @@ export class VaultProgram extends BaseProgram {
|
||||
this.serviceContainer.folderApiService,
|
||||
this.serviceContainer.billingAccountProfileStateService,
|
||||
this.serviceContainer.organizationService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await command.run(object, encodedJson, cmd);
|
||||
this.processResponse(response);
|
||||
@ -272,6 +274,7 @@ export class VaultProgram extends BaseProgram {
|
||||
this.serviceContainer.cryptoService,
|
||||
this.serviceContainer.apiService,
|
||||
this.serviceContainer.folderApiService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await command.run(object, id, encodedJson, cmd);
|
||||
this.processResponse(response);
|
||||
@ -375,7 +378,10 @@ export class VaultProgram extends BaseProgram {
|
||||
})
|
||||
.action(async (id, organizationId, encodedJson, cmd) => {
|
||||
await this.exitIfLocked();
|
||||
const command = new ShareCommand(this.serviceContainer.cipherService);
|
||||
const command = new ShareCommand(
|
||||
this.serviceContainer.cipherService,
|
||||
this.serviceContainer.accountService,
|
||||
);
|
||||
const response = await command.run(id, organizationId, encodedJson);
|
||||
this.processResponse(response);
|
||||
});
|
||||
|
@ -1,11 +1,12 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
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 { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
||||
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
|
||||
@ -34,6 +35,7 @@ export class CreateCommand {
|
||||
private folderApiService: FolderApiServiceAbstraction,
|
||||
private accountProfileService: BillingAccountProfileStateService,
|
||||
private organizationService: OrganizationService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async run(
|
||||
@ -80,11 +82,14 @@ export class CreateCommand {
|
||||
}
|
||||
|
||||
private async createCipher(req: CipherExport) {
|
||||
const cipher = await this.cipherService.encrypt(CipherExport.toView(req));
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipher = await this.cipherService.encrypt(CipherExport.toView(req), activeUserId);
|
||||
try {
|
||||
const newCipher = await this.cipherService.createWithServer(cipher);
|
||||
const decCipher = await newCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(newCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(newCipher, activeUserId),
|
||||
);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
@ -143,13 +148,17 @@ export class CreateCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const updatedCipher = await this.cipherService.saveAttachmentRawWithServer(
|
||||
cipher,
|
||||
fileName,
|
||||
new Uint8Array(fileBuf).buffer,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
);
|
||||
return Response.success(new CipherResponse(decCipher));
|
||||
} catch (e) {
|
||||
|
@ -164,7 +164,10 @@ export class EncryptedMessageHandlerService {
|
||||
cipherView.login.uris[0].uri = credentialCreatePayload.uri;
|
||||
|
||||
try {
|
||||
const encrypted = await this.cipherService.encrypt(cipherView);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const encrypted = await this.cipherService.encrypt(cipherView, activeUserId);
|
||||
await this.cipherService.createWithServer(encrypted);
|
||||
|
||||
// Notify other clients of new login
|
||||
@ -197,14 +200,17 @@ export class EncryptedMessageHandlerService {
|
||||
if (cipher === null) {
|
||||
return { status: "failure" };
|
||||
}
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
cipherView.name = credentialUpdatePayload.name;
|
||||
cipherView.login.password = credentialUpdatePayload.password;
|
||||
cipherView.login.username = credentialUpdatePayload.userName;
|
||||
cipherView.login.uris[0].uri = credentialUpdatePayload.uri;
|
||||
const encrypted = await this.cipherService.encrypt(cipherView);
|
||||
const encrypted = await this.cipherService.encrypt(cipherView, activeUserId);
|
||||
|
||||
await this.cipherService.updateWithServer(encrypted);
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
@ -28,6 +29,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -41,6 +43,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { CollectionsComponent as BaseCollectionsComponent } from "@bitwarden/angular/admin-console/components/collections.component";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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";
|
||||
@ -22,6 +23,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
organizationService: OrganizationService,
|
||||
logService: LogService,
|
||||
configService: ConfigService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
@ -31,6 +33,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
organizationService,
|
||||
logService,
|
||||
configService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { PasswordHistoryComponent as BasePasswordHistoryComponent } from "@bitwarden/angular/vault/components/password-history.component";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@ -14,7 +15,8 @@ export class PasswordHistoryComponent extends BasePasswordHistoryComponent {
|
||||
cipherService: CipherService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
i18nService: I18nService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(cipherService, platformUtilsService, i18nService, window);
|
||||
super(cipherService, platformUtilsService, i18nService, accountService, window);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { Component } from "@angular/core";
|
||||
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
|
||||
import { ShareComponent as BaseShareComponent } from "@bitwarden/angular/components/share.component";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
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";
|
||||
@ -21,6 +22,7 @@ export class ShareComponent extends BaseShareComponent {
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
logService: LogService,
|
||||
organizationService: OrganizationService,
|
||||
accountService: AccountService,
|
||||
private modalRef: ModalRef,
|
||||
) {
|
||||
super(
|
||||
@ -30,6 +32,7 @@ export class ShareComponent extends BaseShareComponent {
|
||||
cipherService,
|
||||
logService,
|
||||
organizationService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import { ViewComponent as BaseViewComponent } from "@bitwarden/angular/vault/com
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
@ -62,6 +63,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
|
||||
dialogService: DialogService,
|
||||
datePipe: DatePipe,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -84,6 +86,7 @@ export class ViewComponent extends BaseViewComponent implements OnInit, OnDestro
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
datePipe,
|
||||
accountService,
|
||||
billingAccountProfileStateService,
|
||||
);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
@ -32,6 +33,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -45,6 +47,7 @@ export class EmergencyAccessAttachmentsComponent extends BaseAttachmentsComponen
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||
|
||||
import { AttachmentsComponent as BaseAttachmentsComponent } from "@bitwarden/angular/vault/components/attachments.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||
@ -31,6 +32,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@ -44,6 +46,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
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 { 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";
|
||||
@ -61,6 +63,7 @@ export class BulkShareDialogComponent implements OnInit, OnDestroy {
|
||||
private collectionService: CollectionService,
|
||||
private organizationService: OrganizationService,
|
||||
private logService: LogService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.ciphers = params.ciphers ?? [];
|
||||
this.organizationId = params.organizationId;
|
||||
@ -98,10 +101,14 @@ export class BulkShareDialogComponent implements OnInit, OnDestroy {
|
||||
submit = async () => {
|
||||
const checkedCollectionIds = this.collections.filter(isChecked).map((c) => c.id);
|
||||
try {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
await this.cipherService.shareManyWithServer(
|
||||
this.shareableCiphers,
|
||||
this.organizationId,
|
||||
checkedCollectionIds,
|
||||
activeUserId,
|
||||
);
|
||||
const orgName =
|
||||
this.organizations.find((o) => o.id === this.organizationId)?.name ??
|
||||
|
@ -3,6 +3,7 @@ import { Component, Inject, OnDestroy } from "@angular/core";
|
||||
|
||||
import { CollectionsComponent as BaseCollectionsComponent } from "@bitwarden/angular/admin-console/components/collections.component";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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";
|
||||
@ -25,6 +26,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
|
||||
organizationSerivce: OrganizationService,
|
||||
logService: LogService,
|
||||
configService: ConfigService,
|
||||
accountService: AccountService,
|
||||
protected dialogRef: DialogRef,
|
||||
@Inject(DIALOG_DATA) params: CollectionsDialogParams,
|
||||
) {
|
||||
@ -36,6 +38,7 @@ export class CollectionsComponent extends BaseCollectionsComponent implements On
|
||||
organizationSerivce,
|
||||
logService,
|
||||
configService,
|
||||
accountService,
|
||||
);
|
||||
this.cipherId = params?.cipherId;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Component, OnDestroy } from "@angular/core";
|
||||
|
||||
import { ShareComponent as BaseShareComponent } from "@bitwarden/angular/components/share.component";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
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";
|
||||
@ -21,6 +22,7 @@ export class ShareComponent extends BaseShareComponent implements OnDestroy {
|
||||
cipherService: CipherService,
|
||||
organizationService: OrganizationService,
|
||||
logService: LogService,
|
||||
accountService: AccountService,
|
||||
) {
|
||||
super(
|
||||
collectionService,
|
||||
@ -29,6 +31,7 @@ export class ShareComponent extends BaseShareComponent implements OnDestroy {
|
||||
cipherService,
|
||||
logService,
|
||||
organizationService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.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 { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@ -198,6 +199,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private apiService: ApiService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
private toastService: ToastService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -699,9 +701,12 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
// Decrypt the cipher.
|
||||
const cipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
|
||||
// Open the dialog.
|
||||
|
@ -14,6 +14,7 @@ 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 { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
@ -107,11 +108,12 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
return cipher;
|
||||
}
|
||||
|
||||
protected encryptCipher() {
|
||||
protected encryptCipher(userId: UserId) {
|
||||
if (!this.organization.canEditAllCiphers(this.restrictProviderAccess)) {
|
||||
return super.encryptCipher();
|
||||
return super.encryptCipher(userId);
|
||||
}
|
||||
return this.cipherService.encrypt(this.cipher, null, null, this.originalCipher);
|
||||
|
||||
return this.cipherService.encrypt(this.cipher, userId, null, null, this.originalCipher);
|
||||
}
|
||||
|
||||
protected async deleteCipher() {
|
||||
|
@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
@ -12,6 +13,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
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 { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
@ -41,6 +43,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
|
||||
fileDownloadService: FileDownloadService,
|
||||
dialogService: DialogService,
|
||||
billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
accountService: AccountService,
|
||||
private configService: ConfigService,
|
||||
) {
|
||||
super(
|
||||
@ -54,6 +57,7 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
|
||||
fileDownloadService,
|
||||
dialogService,
|
||||
billingAccountProfileStateService,
|
||||
accountService,
|
||||
);
|
||||
}
|
||||
|
||||
@ -81,10 +85,11 @@ export class AttachmentsComponent extends BaseAttachmentsComponent implements On
|
||||
return new Cipher(new CipherData(response));
|
||||
}
|
||||
|
||||
protected saveCipherAttachment(file: File) {
|
||||
protected saveCipherAttachment(file: File, userId: UserId) {
|
||||
return this.cipherService.saveAttachmentWithServer(
|
||||
this.cipherDomain,
|
||||
file,
|
||||
userId,
|
||||
this.organization.canEditAllCiphers(this.restrictProviderAccess),
|
||||
);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import { Component, Inject } from "@angular/core";
|
||||
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 { 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";
|
||||
@ -37,6 +38,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
private apiService: ApiService,
|
||||
logService: LogService,
|
||||
configService: ConfigService,
|
||||
accountService: AccountService,
|
||||
protected dialogRef: DialogRef,
|
||||
@Inject(DIALOG_DATA) params: OrgVaultCollectionsDialogParams,
|
||||
) {
|
||||
@ -48,6 +50,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
organizationService,
|
||||
logService,
|
||||
configService,
|
||||
accountService,
|
||||
dialogRef,
|
||||
params,
|
||||
);
|
||||
|
@ -39,6 +39,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
|
||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||
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 { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
@ -216,6 +217,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private organizationUserService: OrganizationUserService,
|
||||
protected configService: ConfigService,
|
||||
private toastService: ToastService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -893,9 +895,12 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
// Decrypt the cipher.
|
||||
const cipherView = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
|
||||
// Open the dialog.
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -36,6 +38,7 @@ export class CollectionsComponent implements OnInit {
|
||||
protected organizationService: OrganizationService,
|
||||
private logService: LogService,
|
||||
private configService: ConfigService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -48,8 +51,11 @@ export class CollectionsComponent implements OnInit {
|
||||
async load() {
|
||||
this.cipherDomain = await this.loadCipher();
|
||||
this.collectionIds = this.loadCipherCollections();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipher = await this.cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, activeUserId),
|
||||
);
|
||||
this.collections = await this.loadCollections();
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationUserStatusType } 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 { 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";
|
||||
@ -36,6 +37,7 @@ export class ShareComponent implements OnInit, OnDestroy {
|
||||
protected cipherService: CipherService,
|
||||
private logService: LogService,
|
||||
protected organizationService: OrganizationService,
|
||||
protected accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -67,8 +69,11 @@ export class ShareComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipher = await cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain, activeUserId),
|
||||
);
|
||||
}
|
||||
|
||||
@ -95,8 +100,11 @@ export class ShareComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipherView = await cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain, activeUserId),
|
||||
);
|
||||
const orgs = await firstValueFrom(this.organizations$);
|
||||
const orgName =
|
||||
@ -104,7 +112,7 @@ export class ShareComponent implements OnInit, OnDestroy {
|
||||
|
||||
try {
|
||||
this.formPromise = this.cipherService
|
||||
.shareWithServer(cipherView, this.organizationId, selectedCollectionIds)
|
||||
.shareWithServer(cipherView, this.organizationId, selectedCollectionIds, activeUserId)
|
||||
.then(async () => {
|
||||
this.onSharedCipher.emit();
|
||||
this.platformUtilsService.showToast(
|
||||
|
@ -775,6 +775,7 @@ const safeProviders: SafeProvider[] = [
|
||||
CollectionServiceAbstraction,
|
||||
CryptoServiceAbstraction,
|
||||
PinServiceAbstraction,
|
||||
AccountServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
@ -800,6 +801,7 @@ const safeProviders: SafeProvider[] = [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
CollectionServiceAbstraction,
|
||||
KdfConfigServiceAbstraction,
|
||||
AccountServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
|
@ -22,6 +22,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||
@ -250,8 +251,11 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
if (this.cipher == null) {
|
||||
if (this.editMode) {
|
||||
const cipher = await this.loadCipher();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipher = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
|
||||
// Adjust Cipher Name if Cloning
|
||||
@ -371,7 +375,10 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
this.cipher.id = null;
|
||||
}
|
||||
|
||||
const cipher = await this.encryptCipher();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipher = await this.encryptCipher(activeUserId);
|
||||
try {
|
||||
this.formPromise = this.saveCipher(cipher);
|
||||
await this.formPromise;
|
||||
@ -664,8 +671,8 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
return this.cipherService.get(this.cipherId);
|
||||
}
|
||||
|
||||
protected encryptCipher() {
|
||||
return this.cipherService.encrypt(this.cipher);
|
||||
protected encryptCipher(userId: UserId) {
|
||||
return this.cipherService.encrypt(this.cipher, userId);
|
||||
}
|
||||
|
||||
protected saveCipher(cipher: Cipher) {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
@ -11,6 +12,7 @@ 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 { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
|
||||
@ -46,6 +48,7 @@ export class AttachmentsComponent implements OnInit {
|
||||
protected fileDownloadService: FileDownloadService,
|
||||
protected dialogService: DialogService,
|
||||
protected billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
protected accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -75,10 +78,13 @@ export class AttachmentsComponent implements OnInit {
|
||||
}
|
||||
|
||||
try {
|
||||
this.formPromise = this.saveCipherAttachment(files[0]);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.formPromise = this.saveCipherAttachment(files[0], activeUserId);
|
||||
this.cipherDomain = await this.formPromise;
|
||||
this.cipher = await this.cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, activeUserId),
|
||||
);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("attachmentSaved"));
|
||||
this.onUploadedAttachment.emit();
|
||||
@ -185,8 +191,11 @@ export class AttachmentsComponent implements OnInit {
|
||||
|
||||
protected async init() {
|
||||
this.cipherDomain = await this.loadCipher();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipher = await this.cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, activeUserId),
|
||||
);
|
||||
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
@ -235,14 +244,18 @@ export class AttachmentsComponent implements OnInit {
|
||||
? attachment.key
|
||||
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(encBuf, key);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipherDomain = await this.cipherService.saveAttachmentRawWithServer(
|
||||
this.cipherDomain,
|
||||
attachment.fileName,
|
||||
decBuf,
|
||||
activeUserId,
|
||||
admin,
|
||||
);
|
||||
this.cipher = await this.cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, activeUserId),
|
||||
);
|
||||
|
||||
// 3. Delete old
|
||||
@ -278,8 +291,8 @@ export class AttachmentsComponent implements OnInit {
|
||||
return this.cipherService.get(this.cipherId);
|
||||
}
|
||||
|
||||
protected saveCipherAttachment(file: File) {
|
||||
return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file);
|
||||
protected saveCipherAttachment(file: File, userId: UserId) {
|
||||
return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, userId);
|
||||
}
|
||||
|
||||
protected deleteCipherAttachment(attachmentId: string) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Directive, OnInit } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
@ -14,6 +16,7 @@ export class PasswordHistoryComponent implements OnInit {
|
||||
protected cipherService: CipherService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected i18nService: I18nService,
|
||||
protected accountService: AccountService,
|
||||
private win: Window,
|
||||
) {}
|
||||
|
||||
@ -33,8 +36,11 @@ export class PasswordHistoryComponent implements OnInit {
|
||||
|
||||
protected async init() {
|
||||
const cipher = await this.cipherService.get(this.cipherId);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const decCipher = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
this.history = decCipher.passwordHistory == null ? [] : decCipher.passwordHistory;
|
||||
}
|
||||
|
@ -9,11 +9,12 @@ import {
|
||||
OnInit,
|
||||
Output,
|
||||
} from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
@ -100,6 +101,7 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
protected fileDownloadService: FileDownloadService,
|
||||
protected dialogService: DialogService,
|
||||
protected datePipe: DatePipe,
|
||||
protected accountService: AccountService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
) {}
|
||||
|
||||
@ -129,8 +131,11 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
this.cleanUp();
|
||||
|
||||
const cipher = await this.cipherService.get(this.cipherId);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipher = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
this.canAccessPremium = await firstValueFrom(
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$,
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { TextEncoder } from "util";
|
||||
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
||||
import { SyncService } from "../../../vault/abstractions/sync/sync.service.abstraction";
|
||||
import { CipherRepromptType } from "../../../vault/enums/cipher-reprompt-type";
|
||||
@ -30,10 +33,18 @@ import { guidToRawFormat } from "./guid-utils";
|
||||
const RpId = "bitwarden.com";
|
||||
|
||||
describe("FidoAuthenticatorService", () => {
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||
id: "testId" as UserId,
|
||||
email: "test@example.com",
|
||||
emailVerified: true,
|
||||
name: "Test User",
|
||||
});
|
||||
|
||||
let cipherService!: MockProxy<CipherService>;
|
||||
let userInterface!: MockProxy<Fido2UserInterfaceService>;
|
||||
let userInterfaceSession!: MockProxy<Fido2UserInterfaceSession>;
|
||||
let syncService!: MockProxy<SyncService>;
|
||||
let accountService!: MockProxy<AccountService>;
|
||||
let authenticator!: Fido2AuthenticatorService;
|
||||
let tab!: chrome.tabs.Tab;
|
||||
|
||||
@ -43,8 +54,15 @@ describe("FidoAuthenticatorService", () => {
|
||||
userInterfaceSession = mock<Fido2UserInterfaceSession>();
|
||||
userInterface.newSession.mockResolvedValue(userInterfaceSession);
|
||||
syncService = mock<SyncService>();
|
||||
authenticator = new Fido2AuthenticatorService(cipherService, userInterface, syncService);
|
||||
accountService = mock<AccountService>();
|
||||
authenticator = new Fido2AuthenticatorService(
|
||||
cipherService,
|
||||
userInterface,
|
||||
syncService,
|
||||
accountService,
|
||||
);
|
||||
tab = { id: 123, windowId: 456 } as chrome.tabs.Tab;
|
||||
accountService.activeAccount$ = activeAccountSubject;
|
||||
});
|
||||
|
||||
describe("makeCredential", () => {
|
||||
@ -677,6 +695,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
],
|
||||
}),
|
||||
}),
|
||||
"testId",
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "../../../auth/abstractions/account.service";
|
||||
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
||||
import { SyncService } from "../../../vault/abstractions/sync/sync.service.abstraction";
|
||||
import { CipherRepromptType } from "../../../vault/enums/cipher-reprompt-type";
|
||||
@ -42,6 +45,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
|
||||
private cipherService: CipherService,
|
||||
private userInterface: Fido2UserInterfaceService,
|
||||
private syncService: SyncService,
|
||||
private accountService: AccountService,
|
||||
private logService?: LogService,
|
||||
) {}
|
||||
|
||||
@ -130,8 +134,12 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
|
||||
keyPair = await createKeyPair();
|
||||
pubKeyDer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
|
||||
const encrypted = await this.cipherService.get(cipherId);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
cipher = await encrypted.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(encrypted),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(encrypted, activeUserId),
|
||||
);
|
||||
|
||||
if (
|
||||
@ -150,7 +158,7 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
|
||||
if (Utils.isNullOrEmpty(cipher.login.username)) {
|
||||
cipher.login.username = fido2Credential.userName;
|
||||
}
|
||||
const reencrypted = await this.cipherService.encrypt(cipher);
|
||||
const reencrypted = await this.cipherService.encrypt(cipher, activeUserId);
|
||||
await this.cipherService.updateWithServer(reencrypted);
|
||||
credentialId = fido2Credential.credentialId;
|
||||
} catch (error) {
|
||||
@ -277,7 +285,10 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr
|
||||
};
|
||||
|
||||
if (selectedFido2Credential.counter > 0) {
|
||||
const encrypted = await this.cipherService.encrypt(selectedCipher);
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const encrypted = await this.cipherService.encrypt(selectedCipher, activeUserId);
|
||||
await this.cipherService.updateWithServer(encrypted);
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ export class DefaultSyncService extends CoreSyncService {
|
||||
throw new Error("Stamp has changed");
|
||||
}
|
||||
|
||||
await this.cryptoService.setMasterKeyEncryptedUserKey(response.key);
|
||||
await this.cryptoService.setMasterKeyEncryptedUserKey(response.key, response.id);
|
||||
await this.cryptoService.setPrivateKey(response.privateKey, response.id);
|
||||
await this.cryptoService.setProviderKeys(response.providers, response.id);
|
||||
await this.cryptoService.setOrgKeys(
|
||||
|
@ -27,6 +27,7 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
clearCache: (userId?: string) => Promise<void>;
|
||||
encrypt: (
|
||||
model: CipherView,
|
||||
userId: UserId,
|
||||
keyForEncryption?: SymmetricCryptoKey,
|
||||
keyForCipherKeyDecryption?: SymmetricCryptoKey,
|
||||
originalCipher?: Cipher,
|
||||
@ -83,21 +84,25 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
cipher: CipherView,
|
||||
organizationId: string,
|
||||
collectionIds: string[],
|
||||
userId: UserId,
|
||||
) => Promise<any>;
|
||||
shareManyWithServer: (
|
||||
ciphers: CipherView[],
|
||||
organizationId: string,
|
||||
collectionIds: string[],
|
||||
userId: UserId,
|
||||
) => Promise<any>;
|
||||
saveAttachmentWithServer: (
|
||||
cipher: Cipher,
|
||||
unencryptedFile: any,
|
||||
userId: UserId,
|
||||
admin?: boolean,
|
||||
) => Promise<Cipher>;
|
||||
saveAttachmentRawWithServer: (
|
||||
cipher: Cipher,
|
||||
filename: string,
|
||||
data: ArrayBuffer,
|
||||
userId: UserId,
|
||||
admin?: boolean,
|
||||
) => Promise<Cipher>;
|
||||
/**
|
||||
@ -147,7 +152,7 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
) => Promise<any>;
|
||||
restoreWithServer: (id: string, asAdmin?: boolean) => Promise<any>;
|
||||
restoreManyWithServer: (ids: string[], orgId?: string) => Promise<void>;
|
||||
getKeyForCipherKeyDecryption: (cipher: Cipher) => Promise<any>;
|
||||
getKeyForCipherKeyDecryption: (cipher: Cipher, userId: UserId) => Promise<any>;
|
||||
setAddEditCipherInfo: (value: AddEditCipherInfo) => Promise<void>;
|
||||
/**
|
||||
* Returns user ciphers re-encrypted with the new user key.
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { makeStaticByteArray, mockEnc, mockFromJson } from "../../../../spec/utils";
|
||||
import { UriMatchStrategy } from "../../../models/domain/domain-service";
|
||||
import { CryptoService } from "../../../platform/abstractions/crypto.service";
|
||||
@ -247,7 +249,7 @@ describe("Cipher DTO", () => {
|
||||
);
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@ -367,7 +369,7 @@ describe("Cipher DTO", () => {
|
||||
);
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@ -505,7 +507,7 @@ describe("Cipher DTO", () => {
|
||||
);
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@ -667,7 +669,7 @@ describe("Cipher DTO", () => {
|
||||
);
|
||||
|
||||
const cipherView = await cipher.decrypt(
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await cipherService.getKeyForCipherKeyDecryption(cipher, mockUserId),
|
||||
);
|
||||
|
||||
expect(cipherView).toMatchObject({
|
||||
@ -754,3 +756,5 @@ describe("Cipher DTO", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const mockUserId = "TestUserId" as UserId;
|
||||
|
@ -121,6 +121,8 @@ describe("Cipher Service", () => {
|
||||
accountService = mockAccountServiceWith(mockUserId);
|
||||
const stateProvider = new FakeStateProvider(accountService);
|
||||
|
||||
const userId = "TestUserId" as UserId;
|
||||
|
||||
let cipherService: CipherService;
|
||||
let cipherObj: Cipher;
|
||||
|
||||
@ -168,7 +170,7 @@ describe("Cipher Service", () => {
|
||||
|
||||
const spy = jest.spyOn(cipherFileUploadService, "upload");
|
||||
|
||||
await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData);
|
||||
await cipherService.saveAttachmentRawWithServer(new Cipher(), fileName, fileData, userId);
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
@ -283,7 +285,7 @@ describe("Cipher Service", () => {
|
||||
{ uri: "uri", match: UriMatchStrategy.RegularExpression } as LoginUriView,
|
||||
];
|
||||
|
||||
const domain = await cipherService.encrypt(cipherView);
|
||||
const domain = await cipherService.encrypt(cipherView, userId);
|
||||
|
||||
expect(domain.login.uris).toEqual([
|
||||
{
|
||||
@ -299,7 +301,7 @@ describe("Cipher Service", () => {
|
||||
it("is null when enableCipherKeyEncryption flag is false", async () => {
|
||||
setEncryptionKeyFlag(false);
|
||||
|
||||
const cipher = await cipherService.encrypt(cipherView);
|
||||
const cipher = await cipherService.encrypt(cipherView, userId);
|
||||
|
||||
expect(cipher.key).toBeNull();
|
||||
});
|
||||
@ -307,7 +309,7 @@ describe("Cipher Service", () => {
|
||||
it("is defined when enableCipherKeyEncryption flag is true", async () => {
|
||||
setEncryptionKeyFlag(true);
|
||||
|
||||
const cipher = await cipherService.encrypt(cipherView);
|
||||
const cipher = await cipherService.encrypt(cipherView, userId);
|
||||
|
||||
expect(cipher.key).toBeDefined();
|
||||
});
|
||||
@ -321,7 +323,7 @@ describe("Cipher Service", () => {
|
||||
it("is not called when enableCipherKeyEncryption is false", async () => {
|
||||
setEncryptionKeyFlag(false);
|
||||
|
||||
await cipherService.encrypt(cipherView);
|
||||
await cipherService.encrypt(cipherView, userId);
|
||||
|
||||
expect(cipherService["encryptCipherWithCipherKey"]).not.toHaveBeenCalled();
|
||||
});
|
||||
@ -329,7 +331,7 @@ describe("Cipher Service", () => {
|
||||
it("is called when enableCipherKeyEncryption is true", async () => {
|
||||
setEncryptionKeyFlag(true);
|
||||
|
||||
await cipherService.encrypt(cipherView);
|
||||
await cipherService.encrypt(cipherView, userId);
|
||||
|
||||
expect(cipherService["encryptCipherWithCipherKey"]).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -165,6 +165,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
async encrypt(
|
||||
model: CipherView,
|
||||
userId: UserId,
|
||||
keyForEncryption?: SymmetricCryptoKey,
|
||||
keyForCipherKeyDecryption?: SymmetricCryptoKey,
|
||||
originalCipher: Cipher = null,
|
||||
@ -174,7 +175,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
originalCipher = await this.get(model.id);
|
||||
}
|
||||
if (originalCipher != null) {
|
||||
await this.updateModelfromExistingCipher(model, originalCipher);
|
||||
await this.updateModelfromExistingCipher(model, originalCipher, userId);
|
||||
}
|
||||
this.adjustPasswordHistoryLength(model);
|
||||
}
|
||||
@ -192,7 +193,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
if (await this.getCipherKeyEncryptionEnabled()) {
|
||||
cipher.key = originalCipher?.key ?? null;
|
||||
const userOrOrgKey = await this.getKeyForCipherKeyDecryption(cipher);
|
||||
const userOrOrgKey = await this.getKeyForCipherKeyDecryption(cipher, userId);
|
||||
// The keyForEncryption is only used for encrypting the cipher key, not the cipher itself, since cipher key encryption is enabled.
|
||||
// If the caller has provided a key for cipher key encryption, use it. Otherwise, use the user or org key.
|
||||
keyForEncryption ||= userOrOrgKey;
|
||||
@ -718,6 +719,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
cipher: CipherView,
|
||||
organizationId: string,
|
||||
collectionIds: string[],
|
||||
userId: UserId,
|
||||
): Promise<any> {
|
||||
const attachmentPromises: Promise<any>[] = [];
|
||||
if (cipher.attachments != null) {
|
||||
@ -733,7 +735,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
cipher.organizationId = organizationId;
|
||||
cipher.collectionIds = collectionIds;
|
||||
const encCipher = await this.encryptSharedCipher(cipher);
|
||||
const encCipher = await this.encryptSharedCipher(cipher, userId);
|
||||
const request = new CipherShareRequest(encCipher);
|
||||
const response = await this.apiService.putShareCipher(cipher.id, request);
|
||||
const data = new CipherData(response, collectionIds);
|
||||
@ -744,6 +746,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
ciphers: CipherView[],
|
||||
organizationId: string,
|
||||
collectionIds: string[],
|
||||
userId: UserId,
|
||||
): Promise<any> {
|
||||
const promises: Promise<any>[] = [];
|
||||
const encCiphers: Cipher[] = [];
|
||||
@ -751,7 +754,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
cipher.organizationId = organizationId;
|
||||
cipher.collectionIds = collectionIds;
|
||||
promises.push(
|
||||
this.encryptSharedCipher(cipher).then((c) => {
|
||||
this.encryptSharedCipher(cipher, userId).then((c) => {
|
||||
encCiphers.push(c);
|
||||
}),
|
||||
);
|
||||
@ -770,7 +773,12 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
await this.upsert(encCiphers.map((c) => c.toCipherData()));
|
||||
}
|
||||
|
||||
saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise<Cipher> {
|
||||
saveAttachmentWithServer(
|
||||
cipher: Cipher,
|
||||
unencryptedFile: any,
|
||||
userId: UserId,
|
||||
admin = false,
|
||||
): Promise<Cipher> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(unencryptedFile);
|
||||
@ -780,6 +788,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
cipher,
|
||||
unencryptedFile.name,
|
||||
evt.target.result,
|
||||
userId,
|
||||
admin,
|
||||
);
|
||||
resolve(cData);
|
||||
@ -797,9 +806,10 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
cipher: Cipher,
|
||||
filename: string,
|
||||
data: Uint8Array,
|
||||
userId: UserId,
|
||||
admin = false,
|
||||
): Promise<Cipher> {
|
||||
const encKey = await this.getKeyForCipherKeyDecryption(cipher);
|
||||
const encKey = await this.getKeyForCipherKeyDecryption(cipher, userId);
|
||||
const cipherKeyEncryptionEnabled = await this.getCipherKeyEncryptionEnabled();
|
||||
|
||||
const cipherEncKey =
|
||||
@ -813,8 +823,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
//then we rollback to using the user key as the main key of encryption of the item
|
||||
//in order to keep item and it's attachments with the same encryption level
|
||||
if (cipher.key != null && !cipherKeyEncryptionEnabled) {
|
||||
const model = await cipher.decrypt(await this.getKeyForCipherKeyDecryption(cipher));
|
||||
cipher = await this.encrypt(model);
|
||||
const model = await cipher.decrypt(await this.getKeyForCipherKeyDecryption(cipher, userId));
|
||||
cipher = await this.encrypt(model, userId);
|
||||
await this.updateWithServer(cipher);
|
||||
}
|
||||
|
||||
@ -1209,10 +1219,10 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
await this.restore(restores);
|
||||
}
|
||||
|
||||
async getKeyForCipherKeyDecryption(cipher: Cipher): Promise<UserKey | OrgKey> {
|
||||
async getKeyForCipherKeyDecryption(cipher: Cipher, userId: UserId): Promise<UserKey | OrgKey> {
|
||||
return (
|
||||
(await this.cryptoService.getOrgKey(cipher.organizationId)) ||
|
||||
((await this.cryptoService.getUserKeyWithLegacySupport()) as UserKey)
|
||||
((await this.cryptoService.getUserKeyWithLegacySupport(userId)) as UserKey)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1247,7 +1257,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
encryptedCiphers = await Promise.all(
|
||||
userCiphers.map(async (cipher) => {
|
||||
const encryptedCipher = await this.encrypt(cipher, newUserKey, originalUserKey);
|
||||
const encryptedCipher = await this.encrypt(cipher, userId, newUserKey, originalUserKey);
|
||||
return new CipherWithIdRequest(encryptedCipher);
|
||||
}),
|
||||
);
|
||||
@ -1259,17 +1269,18 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
// In the case of a cipher that is being shared with an organization, we want to decrypt the
|
||||
// cipher key with the user's key and then re-encrypt it with the organization's key.
|
||||
private async encryptSharedCipher(model: CipherView): Promise<Cipher> {
|
||||
const keyForCipherKeyDecryption = await this.cryptoService.getUserKeyWithLegacySupport();
|
||||
return await this.encrypt(model, null, keyForCipherKeyDecryption);
|
||||
private async encryptSharedCipher(model: CipherView, userId: UserId): Promise<Cipher> {
|
||||
const keyForCipherKeyDecryption = await this.cryptoService.getUserKeyWithLegacySupport(userId);
|
||||
return await this.encrypt(model, userId, null, keyForCipherKeyDecryption);
|
||||
}
|
||||
|
||||
private async updateModelfromExistingCipher(
|
||||
model: CipherView,
|
||||
originalCipher: Cipher,
|
||||
userId: UserId,
|
||||
): Promise<void> {
|
||||
const existingCipher = await originalCipher.decrypt(
|
||||
await this.getKeyForCipherKeyDecryption(originalCipher),
|
||||
await this.getKeyForCipherKeyDecryption(originalCipher, userId),
|
||||
);
|
||||
model.passwordHistory = existingCipher.passwordHistory || [];
|
||||
if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { KdfType } from "@bitwarden/common/platform/enums";
|
||||
@ -21,6 +22,7 @@ describe("BitwardenPasswordProtectedImporter", () => {
|
||||
let i18nService: MockProxy<I18nService>;
|
||||
let cipherService: MockProxy<CipherService>;
|
||||
let pinService: MockProxy<PinServiceAbstraction>;
|
||||
let accountService: MockProxy<AccountService>;
|
||||
const password = Utils.newGuid();
|
||||
const promptForPassword_callback = async () => {
|
||||
return password;
|
||||
@ -31,12 +33,14 @@ describe("BitwardenPasswordProtectedImporter", () => {
|
||||
i18nService = mock<I18nService>();
|
||||
cipherService = mock<CipherService>();
|
||||
pinService = mock<PinServiceAbstraction>();
|
||||
accountService = mock<AccountService>();
|
||||
|
||||
importer = new BitwardenPasswordProtectedImporter(
|
||||
cryptoService,
|
||||
i18nService,
|
||||
cipherService,
|
||||
pinService,
|
||||
accountService,
|
||||
promptForPassword_callback,
|
||||
);
|
||||
});
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { 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 { ClientType } from "@bitwarden/common/enums";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@ -89,6 +90,7 @@ const safeProviders: SafeProvider[] = [
|
||||
CollectionService,
|
||||
CryptoService,
|
||||
PinServiceAbstraction,
|
||||
AccountService,
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import {
|
||||
CipherWithIdExport,
|
||||
CollectionWithIdExport,
|
||||
@ -33,6 +34,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
||||
protected i18nService: I18nService,
|
||||
protected cipherService: CipherService,
|
||||
protected pinService: PinServiceAbstraction,
|
||||
protected accountService: AccountService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -103,8 +105,11 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
||||
});
|
||||
}
|
||||
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const view = await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
this.cleanupCipher(view);
|
||||
this.result.ciphers.push(view);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import {
|
||||
Argon2KdfConfig,
|
||||
KdfConfig,
|
||||
@ -25,9 +26,10 @@ export class BitwardenPasswordProtectedImporter extends BitwardenJsonImporter im
|
||||
i18nService: I18nService,
|
||||
cipherService: CipherService,
|
||||
pinService: PinServiceAbstraction,
|
||||
accountService: AccountService,
|
||||
private promptForPassword_callback: () => Promise<string>,
|
||||
) {
|
||||
super(cryptoService, i18nService, cipherService, pinService);
|
||||
super(cryptoService, i18nService, cipherService, pinService, accountService);
|
||||
}
|
||||
|
||||
async parse(data: string): Promise<ImportResult> {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@ -27,6 +28,7 @@ describe("ImportService", () => {
|
||||
let collectionService: MockProxy<CollectionService>;
|
||||
let cryptoService: MockProxy<CryptoService>;
|
||||
let pinService: MockProxy<PinServiceAbstraction>;
|
||||
let accountService: MockProxy<AccountService>;
|
||||
|
||||
beforeEach(() => {
|
||||
cipherService = mock<CipherService>();
|
||||
@ -45,6 +47,7 @@ describe("ImportService", () => {
|
||||
collectionService,
|
||||
cryptoService,
|
||||
pinService,
|
||||
accountService,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ImportCiphersRequest } from "@bitwarden/common/models/request/import-ciphers.request";
|
||||
import { ImportOrganizationCiphersRequest } from "@bitwarden/common/models/request/import-organization-ciphers.request";
|
||||
import { KvpRequest } from "@bitwarden/common/models/request/kvp.request";
|
||||
@ -102,6 +105,7 @@ export class ImportService implements ImportServiceAbstraction {
|
||||
private collectionService: CollectionService,
|
||||
private cryptoService: CryptoService,
|
||||
private pinService: PinServiceAbstraction,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
getImportOptions(): ImportOption[] {
|
||||
@ -206,6 +210,7 @@ export class ImportService implements ImportServiceAbstraction {
|
||||
this.i18nService,
|
||||
this.cipherService,
|
||||
this.pinService,
|
||||
this.accountService,
|
||||
promptForPassword_callback,
|
||||
);
|
||||
case "lastpasscsv":
|
||||
@ -332,8 +337,11 @@ export class ImportService implements ImportServiceAbstraction {
|
||||
|
||||
private async handleIndividualImport(importResult: ImportResult) {
|
||||
const request = new ImportCiphersRequest();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
for (let i = 0; i < importResult.ciphers.length; i++) {
|
||||
const c = await this.cipherService.encrypt(importResult.ciphers[i]);
|
||||
const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId);
|
||||
request.ciphers.push(new CipherRequest(c));
|
||||
}
|
||||
if (importResult.folders != null) {
|
||||
@ -352,9 +360,12 @@ export class ImportService implements ImportServiceAbstraction {
|
||||
|
||||
private async handleOrganizationalImport(importResult: ImportResult, organizationId: string) {
|
||||
const request = new ImportOrganizationCiphersRequest();
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
for (let i = 0; i < importResult.ciphers.length; i++) {
|
||||
importResult.ciphers[i].organizationId = organizationId;
|
||||
const c = await this.cipherService.encrypt(importResult.ciphers[i]);
|
||||
const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId);
|
||||
request.ciphers.push(new CipherRequest(c));
|
||||
}
|
||||
if (importResult.collections != null) {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import * as papa from "papaparse";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||
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 { CipherWithIdExport, CollectionWithIdExport } from "@bitwarden/common/models/export";
|
||||
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
|
||||
@ -42,6 +43,7 @@ export class OrganizationVaultExportService
|
||||
cryptoFunctionService: CryptoFunctionService,
|
||||
private collectionService: CollectionService,
|
||||
kdfConfigService: KdfConfigService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
super(pinService, cryptoService, cryptoFunctionService, kdfConfigService);
|
||||
}
|
||||
@ -87,6 +89,9 @@ export class OrganizationVaultExportService
|
||||
const decCollections: CollectionView[] = [];
|
||||
const decCiphers: CipherView[] = [];
|
||||
const promises = [];
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
promises.push(
|
||||
this.apiService.getOrganizationExport(organizationId).then((exportData) => {
|
||||
@ -111,7 +116,7 @@ export class OrganizationVaultExportService
|
||||
const cipher = new Cipher(new CipherData(c));
|
||||
exportPromises.push(
|
||||
this.cipherService
|
||||
.getKeyForCipherKeyDecryption(cipher)
|
||||
.getKeyForCipherKeyDecryption(cipher, activeUserId)
|
||||
.then((key) => cipher.decrypt(key))
|
||||
.then((decCipher) => {
|
||||
decCiphers.push(decCipher);
|
||||
|
@ -3,11 +3,13 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { By } from "@angular/platform-browser";
|
||||
import { mock } from "jest-mock-extended";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherId } from "@bitwarden/common/types/guid";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
|
||||
@ -15,6 +17,8 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { ButtonComponent, ToastService } from "@bitwarden/components";
|
||||
import { DownloadAttachmentComponent } from "@bitwarden/vault";
|
||||
|
||||
import { FakeAccountService, mockAccountServiceWith } from "../../../../../common/spec";
|
||||
|
||||
import { CipherAttachmentsComponent } from "./cipher-attachments.component";
|
||||
import { DeleteAttachmentComponent } from "./delete-attachment/delete-attachment.component";
|
||||
|
||||
@ -49,6 +53,9 @@ describe("CipherAttachmentsComponent", () => {
|
||||
const cipherServiceGet = jest.fn().mockResolvedValue(cipherDomain);
|
||||
const saveAttachmentWithServer = jest.fn().mockResolvedValue(cipherDomain);
|
||||
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
const accountService: FakeAccountService = mockAccountServiceWith(mockUserId);
|
||||
|
||||
beforeEach(async () => {
|
||||
cipherServiceGet.mockClear();
|
||||
showToast.mockClear();
|
||||
@ -75,6 +82,10 @@ describe("CipherAttachmentsComponent", () => {
|
||||
{ provide: LogService, useValue: mock<LogService>() },
|
||||
{ provide: ConfigService, useValue: mock<ConfigService>() },
|
||||
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
|
||||
{
|
||||
provide: AccountService,
|
||||
useValue: accountService,
|
||||
},
|
||||
],
|
||||
})
|
||||
.overrideComponent(CipherAttachmentsComponent, {
|
||||
@ -219,7 +230,7 @@ describe("CipherAttachmentsComponent", () => {
|
||||
it("calls `saveAttachmentWithServer`", async () => {
|
||||
await component.submit();
|
||||
|
||||
expect(saveAttachmentWithServer).toHaveBeenCalledWith(cipherDomain, file);
|
||||
expect(saveAttachmentWithServer).toHaveBeenCalledWith(cipherDomain, file, mockUserId);
|
||||
});
|
||||
|
||||
it("resets form and input values", async () => {
|
||||
|
@ -19,11 +19,13 @@ import {
|
||||
ReactiveFormsModule,
|
||||
Validators,
|
||||
} from "@angular/forms";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
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 { CipherId } from "@bitwarden/common/types/guid";
|
||||
import { CipherId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view";
|
||||
@ -90,6 +92,7 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
|
||||
private cipherDomain: Cipher;
|
||||
private activeUserId: UserId;
|
||||
private destroy$ = inject(DestroyRef);
|
||||
|
||||
constructor(
|
||||
@ -98,6 +101,7 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit {
|
||||
private formBuilder: FormBuilder,
|
||||
private logService: LogService,
|
||||
private toastService: ToastService,
|
||||
private accountService: AccountService,
|
||||
) {
|
||||
this.attachmentForm.statusChanges.pipe(takeUntilDestroyed()).subscribe((status) => {
|
||||
if (!this.submitBtn) {
|
||||
@ -110,8 +114,11 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit {
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.cipherDomain = await this.cipherService.get(this.cipherId);
|
||||
this.activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
this.cipher = await this.cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, this.activeUserId),
|
||||
);
|
||||
|
||||
// Update the initial state of the submit button
|
||||
@ -178,11 +185,12 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit {
|
||||
this.cipherDomain = await this.cipherService.saveAttachmentWithServer(
|
||||
this.cipherDomain,
|
||||
file,
|
||||
this.activeUserId,
|
||||
);
|
||||
|
||||
// re-decrypt the cipher to update the attachments
|
||||
this.cipher = await this.cipherDomain.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, this.activeUserId),
|
||||
);
|
||||
|
||||
// Reset reactive form and input element
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { inject, Injectable } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -14,15 +16,25 @@ function isSetEqual(a: Set<string>, b: Set<string>) {
|
||||
@Injectable()
|
||||
export class DefaultCipherFormService implements CipherFormService {
|
||||
private cipherService: CipherService = inject(CipherService);
|
||||
private accountService: AccountService = inject(AccountService);
|
||||
|
||||
async decryptCipher(cipher: Cipher): Promise<CipherView> {
|
||||
return await cipher.decrypt(await this.cipherService.getKeyForCipherKeyDecryption(cipher));
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
return await cipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
|
||||
);
|
||||
}
|
||||
|
||||
async saveCipher(cipher: CipherView, config: CipherFormConfig): Promise<CipherView> {
|
||||
// Passing the original cipher is important here as it is responsible for appending to password history
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const encryptedCipher = await this.cipherService.encrypt(
|
||||
cipher,
|
||||
activeUserId,
|
||||
null,
|
||||
null,
|
||||
config.originalCipher ?? null,
|
||||
@ -34,7 +46,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
if (cipher.id == null) {
|
||||
savedCipher = await this.cipherService.createWithServer(encryptedCipher, config.admin);
|
||||
return await savedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(savedCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(savedCipher, activeUserId),
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,7 +80,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
}
|
||||
|
||||
return await savedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(savedCipher),
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(savedCipher, activeUserId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
Observable,
|
||||
Subject,
|
||||
combineLatest,
|
||||
firstValueFrom,
|
||||
map,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
@ -25,10 +26,11 @@ import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationUserStatusType } 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 { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -162,6 +164,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
|
||||
private get selectedOrgId(): OrganizationId {
|
||||
return this.formGroup.getRawValue().selectedOrg || this.params.organizationId;
|
||||
}
|
||||
private activeUserId: UserId;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
@ -172,6 +175,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
|
||||
private collectionService: CollectionService,
|
||||
private formBuilder: FormBuilder,
|
||||
private toastService: ToastService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -179,6 +183,10 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
|
||||
FeatureFlag.RestrictProviderAccess,
|
||||
);
|
||||
|
||||
this.activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
|
||||
const onlyPersonalItems = this.params.ciphers.every((c) => c.organizationId == null);
|
||||
|
||||
if (this.selectedOrgId === MY_VAULT_ID || onlyPersonalItems) {
|
||||
@ -420,6 +428,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
|
||||
shareableCiphers,
|
||||
organizationId,
|
||||
selectedCollectionIds,
|
||||
this.activeUserId,
|
||||
);
|
||||
|
||||
this.toastService.showToast({
|
||||
@ -460,7 +469,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
|
||||
private async updateAssignedCollections(cipherView: CipherView) {
|
||||
const { collections } = this.formGroup.getRawValue();
|
||||
cipherView.collectionIds = collections.map((i) => i.id as CollectionId);
|
||||
const cipher = await this.cipherService.encrypt(cipherView);
|
||||
const cipher = await this.cipherService.encrypt(cipherView, this.activeUserId);
|
||||
await this.cipherService.saveCollectionsWithServer(cipher);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user