mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-24096] replace getOrgKey with orgKey$, refactor collectionAdminService (#15928)
* replace getOrgKey with orgKey$, refactor collectionAdminService * clean up * uncomment accidental commet * remove cache
This commit is contained in:
@@ -1,17 +1,20 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
OrganizationUserConfirmRequest,
|
OrganizationUserConfirmRequest,
|
||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { OrgKey } from "@bitwarden/common/types/key";
|
import { OrgKey } from "@bitwarden/common/types/key";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
import { EncString } from "@bitwarden/sdk-internal";
|
import { EncString } from "@bitwarden/sdk-internal";
|
||||||
@@ -24,6 +27,7 @@ export class ConfirmCommand {
|
|||||||
private keyService: KeyService,
|
private keyService: KeyService,
|
||||||
private encryptService: EncryptService,
|
private encryptService: EncryptService,
|
||||||
private organizationUserApiService: OrganizationUserApiService,
|
private organizationUserApiService: OrganizationUserApiService,
|
||||||
|
private accountService: AccountService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
) {}
|
) {}
|
||||||
@@ -53,7 +57,14 @@ export class ConfirmCommand {
|
|||||||
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const orgKey = await this.keyService.getOrgKey(options.organizationId);
|
const orgKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[options.organizationId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (orgKey == null) {
|
if (orgKey == null) {
|
||||||
throw new Error("No encryption key for this organization.");
|
throw new Error("No encryption key for this organization.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { CollectionRequest } from "@bitwarden/admin-console/common";
|
import { CollectionRequest } from "@bitwarden/admin-console/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -14,6 +14,7 @@ import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
|
|||||||
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
|
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
|
||||||
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
|
import { FolderExport } from "@bitwarden/common/models/export/folder.export";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
|
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
|
||||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
@@ -201,7 +202,13 @@ export class EditCommand {
|
|||||||
return Response.badRequest("`organizationid` option does not match request object.");
|
return Response.badRequest("`organizationid` option does not match request object.");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const orgKey = await this.keyService.getOrgKey(req.organizationId);
|
const orgKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[options.organizationId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
if (orgKey == null) {
|
if (orgKey == null) {
|
||||||
throw new Error("No encryption key for this organization.");
|
throw new Error("No encryption key for this organization.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
|
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -485,7 +485,13 @@ export class GetCommand extends DownloadCommand {
|
|||||||
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const orgKey = await this.keyService.getOrgKey(options.organizationId);
|
const orgKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[options.organizationId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
if (orgKey == null) {
|
if (orgKey == null) {
|
||||||
throw new Error("No encryption key for this organization.");
|
throw new Error("No encryption key for this organization.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export class OssServeConfigurator {
|
|||||||
this.serviceContainer.keyService,
|
this.serviceContainer.keyService,
|
||||||
this.serviceContainer.encryptService,
|
this.serviceContainer.encryptService,
|
||||||
this.serviceContainer.organizationUserApiService,
|
this.serviceContainer.organizationUserApiService,
|
||||||
|
this.serviceContainer.accountService,
|
||||||
this.serviceContainer.configService,
|
this.serviceContainer.configService,
|
||||||
this.serviceContainer.i18nService,
|
this.serviceContainer.i18nService,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -432,6 +432,7 @@ export class VaultProgram extends BaseProgram {
|
|||||||
this.serviceContainer.keyService,
|
this.serviceContainer.keyService,
|
||||||
this.serviceContainer.encryptService,
|
this.serviceContainer.encryptService,
|
||||||
this.serviceContainer.organizationUserApiService,
|
this.serviceContainer.organizationUserApiService,
|
||||||
|
this.serviceContainer.accountService,
|
||||||
this.serviceContainer.configService,
|
this.serviceContainer.configService,
|
||||||
this.serviceContainer.i18nService,
|
this.serviceContainer.i18nService,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CollectionAdminService,
|
CollectionAdminService,
|
||||||
@@ -14,6 +14,8 @@ import {
|
|||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@@ -99,6 +101,7 @@ export class VaultHeaderComponent {
|
|||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private collectionAdminService: CollectionAdminService,
|
private collectionAdminService: CollectionAdminService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
@@ -199,7 +202,14 @@ export class VaultHeaderComponent {
|
|||||||
|
|
||||||
async addCollection() {
|
async addCollection() {
|
||||||
if (this.organization.productTierType === ProductTierType.Free) {
|
if (this.organization.productTierType === ProductTierType.Free) {
|
||||||
const collections = await this.collectionAdminService.getAll(this.organization.id);
|
const collections = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.collectionAdminService.collectionAdminViews$(this.organization.id, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
if (collections.length === this.organization.maxCollections) {
|
if (collections.length === this.organization.maxCollections) {
|
||||||
this.showFreeOrgUpgradeDialog();
|
this.showFreeOrgUpgradeDialog();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -363,7 +363,12 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.allCollectionsWithoutUnassigned$ = this.refresh$.pipe(
|
this.allCollectionsWithoutUnassigned$ = this.refresh$.pipe(
|
||||||
switchMap(() => organizationId$),
|
switchMap(() => organizationId$),
|
||||||
switchMap((orgId) => this.collectionAdminService.getAll(orgId)),
|
switchMap((orgId) =>
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.collectionAdminService.collectionAdminViews$(orgId, userId)),
|
||||||
|
),
|
||||||
|
),
|
||||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -156,7 +157,11 @@ export class GroupAddEditComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
|
|
||||||
private orgCollections$ = from(this.collectionAdminService.getAll(this.organizationId)).pipe(
|
private orgCollections$ = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.collectionAdminService.collectionAdminViews$(this.organizationId, userId),
|
||||||
|
),
|
||||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api";
|
import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -276,9 +277,16 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const collections = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.collectionAdminService.collectionAdminViews$(this.params.organizationId, userId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
combineLatest({
|
combineLatest({
|
||||||
organization: this.organization$,
|
organization: this.organization$,
|
||||||
collections: this.collectionAdminService.getAll(this.params.organizationId),
|
collections,
|
||||||
userDetails: userDetails$,
|
userDetails: userDetails$,
|
||||||
groups: groups$,
|
groups: groups$,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -200,7 +200,14 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
this.organization.canManageUsersPassword &&
|
this.organization.canManageUsersPassword &&
|
||||||
!this.organization.hasPublicAndPrivateKeys
|
!this.organization.hasPublicAndPrivateKeys
|
||||||
) {
|
) {
|
||||||
const orgShareKey = await this.keyService.getOrgKey(this.organization.id);
|
const orgShareKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[this.organization.id] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const orgKeys = await this.keyService.makeKeyPair(orgShareKey);
|
const orgKeys = await this.keyService.makeKeyPair(orgShareKey);
|
||||||
const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
const request = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
||||||
const response = await this.organizationApiService.updateKeys(
|
const response = await this.organizationApiService.updateKeys(
|
||||||
@@ -353,7 +360,13 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
this.organizationUserService.confirmUser(this.organization, user, publicKey),
|
this.organizationUserService.confirmUser(this.organization, user, publicKey),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const orgKey = await this.keyService.getOrgKey(this.organization.id);
|
const orgKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[this.organization.id] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
const key = await this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey);
|
const key = await this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey);
|
||||||
const request = new OrganizationUserConfirmRequest();
|
const request = new OrganizationUserConfirmRequest();
|
||||||
request.key = key.encryptedString;
|
request.key = key.encryptedString;
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { EncryptionType } from "@bitwarden/common/platform/enums";
|
import { EncryptionType } from "@bitwarden/common/platform/enums";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
import { CsprngArray } from "@bitwarden/common/types/csprng";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||||
import { UserKey, OrgKey, MasterKey } from "@bitwarden/common/types/key";
|
import { UserKey, OrgKey, MasterKey } from "@bitwarden/common/types/key";
|
||||||
import { KdfType, KeyService } from "@bitwarden/key-management";
|
import { KdfType, KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
@@ -36,6 +37,8 @@ describe("OrganizationUserResetPasswordService", () => {
|
|||||||
let organizationUserApiService: MockProxy<OrganizationUserApiService>;
|
let organizationUserApiService: MockProxy<OrganizationUserApiService>;
|
||||||
let organizationApiService: MockProxy<OrganizationApiService>;
|
let organizationApiService: MockProxy<OrganizationApiService>;
|
||||||
let i18nService: MockProxy<I18nService>;
|
let i18nService: MockProxy<I18nService>;
|
||||||
|
const mockUserId = Utils.newGuid() as UserId;
|
||||||
|
let accountService: FakeAccountService;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
keyService = mock<KeyService>();
|
keyService = mock<KeyService>();
|
||||||
@@ -44,6 +47,7 @@ describe("OrganizationUserResetPasswordService", () => {
|
|||||||
organizationUserApiService = mock<OrganizationUserApiService>();
|
organizationUserApiService = mock<OrganizationUserApiService>();
|
||||||
organizationApiService = mock<OrganizationApiService>();
|
organizationApiService = mock<OrganizationApiService>();
|
||||||
i18nService = mock<I18nService>();
|
i18nService = mock<I18nService>();
|
||||||
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
|
|
||||||
sut = new OrganizationUserResetPasswordService(
|
sut = new OrganizationUserResetPasswordService(
|
||||||
keyService,
|
keyService,
|
||||||
@@ -52,6 +56,7 @@ describe("OrganizationUserResetPasswordService", () => {
|
|||||||
organizationUserApiService,
|
organizationUserApiService,
|
||||||
organizationApiService,
|
organizationApiService,
|
||||||
i18nService,
|
i18nService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,7 +147,10 @@ describe("OrganizationUserResetPasswordService", () => {
|
|||||||
|
|
||||||
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
|
const mockRandomBytes = new Uint8Array(64) as CsprngArray;
|
||||||
const mockOrgKey = new SymmetricCryptoKey(mockRandomBytes) as OrgKey;
|
const mockOrgKey = new SymmetricCryptoKey(mockRandomBytes) as OrgKey;
|
||||||
keyService.getOrgKey.mockResolvedValue(mockOrgKey);
|
keyService.orgKeys$.mockReturnValue(
|
||||||
|
of({ [mockOrgId]: mockOrgKey } as Record<OrganizationId, OrgKey>),
|
||||||
|
);
|
||||||
|
|
||||||
encryptService.decryptToBytes.mockResolvedValue(mockRandomBytes);
|
encryptService.decryptToBytes.mockResolvedValue(mockRandomBytes);
|
||||||
|
|
||||||
encryptService.rsaDecrypt.mockResolvedValue(mockRandomBytes);
|
encryptService.rsaDecrypt.mockResolvedValue(mockRandomBytes);
|
||||||
@@ -170,7 +178,7 @@ describe("OrganizationUserResetPasswordService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should throw an error if the org key is null", async () => {
|
it("should throw an error if the org key is null", async () => {
|
||||||
keyService.getOrgKey.mockResolvedValue(null);
|
keyService.orgKeys$.mockReturnValue(of(null));
|
||||||
await expect(
|
await expect(
|
||||||
sut.resetMasterPassword(mockNewMP, mockEmail, mockOrgUserId, mockOrgId),
|
sut.resetMasterPassword(mockNewMP, mockEmail, mockOrgUserId, mockOrgId),
|
||||||
).rejects.toThrow();
|
).rejects.toThrow();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import {
|
import {
|
||||||
EncryptedString,
|
EncryptedString,
|
||||||
@@ -47,6 +49,7 @@ export class OrganizationUserResetPasswordService
|
|||||||
private organizationUserApiService: OrganizationUserApiService,
|
private organizationUserApiService: OrganizationUserApiService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +114,14 @@ export class OrganizationUserResetPasswordService
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt Organization's encrypted Private Key with org key
|
// Decrypt Organization's encrypted Private Key with org key
|
||||||
const orgSymKey = await this.keyService.getOrgKey(orgId);
|
const orgSymKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[orgId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (orgSymKey == null) {
|
if (orgSymKey == null) {
|
||||||
throw new Error("No org key found");
|
throw new Error("No org key found");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
firstValueFrom,
|
firstValueFrom,
|
||||||
from,
|
from,
|
||||||
lastValueFrom,
|
lastValueFrom,
|
||||||
|
map,
|
||||||
of,
|
of,
|
||||||
Subject,
|
Subject,
|
||||||
switchMap,
|
switchMap,
|
||||||
@@ -28,6 +29,7 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
|||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
@@ -179,7 +181,13 @@ export class AccountComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Backfill pub/priv key if necessary
|
// Backfill pub/priv key if necessary
|
||||||
if (!this.org.hasPublicAndPrivateKeys) {
|
if (!this.org.hasPublicAndPrivateKeys) {
|
||||||
const orgShareKey = await this.keyService.getOrgKey(this.organizationId);
|
const orgShareKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[this.organizationId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
const orgKeys = await this.keyService.makeKeyPair(orgShareKey);
|
const orgKeys = await this.keyService.makeKeyPair(orgShareKey);
|
||||||
request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
request.keys = new OrganizationKeysRequest(orgKeys[0], orgKeys[1].encryptedString);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,9 +240,15 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
return this.groupService.getAll(orgId);
|
return this.groupService.getAll(orgId);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const collections = this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.collectionAdminService.collectionAdminViews$(orgId, userId)),
|
||||||
|
);
|
||||||
|
|
||||||
combineLatest({
|
combineLatest({
|
||||||
organization: organization$,
|
organization: organization$,
|
||||||
collections: this.collectionAdminService.getAll(orgId),
|
collections,
|
||||||
groups: groups$,
|
groups: groups$,
|
||||||
users: this.organizationUserApiService.getAllMiniUserDetails(orgId),
|
users: this.organizationUserApiService.getAllMiniUserDetails(orgId),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common";
|
import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common";
|
||||||
import { ImportCollectionServiceAbstraction } from "@bitwarden/importer-core";
|
import { ImportCollectionServiceAbstraction } from "@bitwarden/importer-core";
|
||||||
|
import { UserId } from "@bitwarden/user-core";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ImportCollectionAdminService implements ImportCollectionServiceAbstraction {
|
export class ImportCollectionAdminService implements ImportCollectionServiceAbstraction {
|
||||||
constructor(private collectionAdminService: CollectionAdminService) {}
|
constructor(private collectionAdminService: CollectionAdminService) {}
|
||||||
|
|
||||||
async getAllAdminCollections(organizationId: string): Promise<CollectionAdminView[]> {
|
async getAllAdminCollections(
|
||||||
return await this.collectionAdminService.getAll(organizationId);
|
organizationId: string,
|
||||||
|
userId: UserId,
|
||||||
|
): Promise<CollectionAdminView[]> {
|
||||||
|
return await firstValueFrom(
|
||||||
|
this.collectionAdminService.collectionAdminViews$(organizationId, userId),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Unassigned,
|
Unassigned,
|
||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
@@ -93,6 +95,7 @@ export class VaultHeaderComponent {
|
|||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -225,7 +228,14 @@ export class VaultHeaderComponent {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (this.organizations?.length == 1 && !!organization) {
|
if (this.organizations?.length == 1 && !!organization) {
|
||||||
const collections = await this.collectionAdminService.getAll(organization.id);
|
const collections = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) =>
|
||||||
|
this.collectionAdminService.collectionAdminViews$(organization.id, userId),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
if (collections.length === organization.maxCollections) {
|
if (collections.length === organization.maxCollections) {
|
||||||
await this.showFreeOrgUpgradeDialog(organization);
|
await this.showFreeOrgUpgradeDialog(organization);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { TestBed } from "@angular/core/testing";
|
import { TestBed } from "@angular/core/testing";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject, of } from "rxjs";
|
||||||
|
|
||||||
import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common";
|
import { CollectionAdminService, CollectionAdminView } from "@bitwarden/admin-console/common";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
@@ -71,7 +71,7 @@ describe("AdminConsoleCipherFormConfigService", () => {
|
|||||||
{ provide: OrganizationService, useValue: { organizations$: () => orgs$ } },
|
{ provide: OrganizationService, useValue: { organizations$: () => orgs$ } },
|
||||||
{
|
{
|
||||||
provide: CollectionAdminService,
|
provide: CollectionAdminService,
|
||||||
useValue: { getAll: () => Promise.resolve([collection, collection2]) },
|
useValue: { collectionAdminViews$: () => of([collection, collection2]) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: PolicyService,
|
provide: PolicyService,
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ export class AdminConsoleCipherFormConfigService implements CipherFormConfigServ
|
|||||||
private apiService: ApiService = inject(ApiService);
|
private apiService: ApiService = inject(ApiService);
|
||||||
private accountService: AccountService = inject(AccountService);
|
private accountService: AccountService = inject(AccountService);
|
||||||
|
|
||||||
private organizationDataOwnershipDisabled$ = this.accountService.activeAccount$.pipe(
|
private userId$ = this.accountService.activeAccount$.pipe(getUserId);
|
||||||
getUserId,
|
|
||||||
|
private organizationDataOwnershipDisabled$ = this.userId$.pipe(
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
|
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
|
||||||
),
|
),
|
||||||
@@ -44,9 +45,9 @@ export class AdminConsoleCipherFormConfigService implements CipherFormConfigServ
|
|||||||
filter((filter) => filter !== undefined),
|
filter((filter) => filter !== undefined),
|
||||||
);
|
);
|
||||||
|
|
||||||
private allOrganizations$ = this.accountService.activeAccount$.pipe(
|
private allOrganizations$ = this.userId$.pipe(
|
||||||
switchMap((account) =>
|
switchMap((userId) =>
|
||||||
this.organizationService.organizations$(account?.id).pipe(
|
this.organizationService.organizations$(userId).pipe(
|
||||||
map((orgs) => {
|
map((orgs) => {
|
||||||
return orgs.filter(
|
return orgs.filter(
|
||||||
(o) => o.isMember && o.enabled && o.status === OrganizationUserStatusType.Confirmed,
|
(o) => o.isMember && o.enabled && o.status === OrganizationUserStatusType.Confirmed,
|
||||||
@@ -60,8 +61,8 @@ export class AdminConsoleCipherFormConfigService implements CipherFormConfigServ
|
|||||||
map(([orgs, orgId]) => orgs.find((o) => o.id === orgId)),
|
map(([orgs, orgId]) => orgs.find((o) => o.id === orgId)),
|
||||||
);
|
);
|
||||||
|
|
||||||
private allCollections$ = this.organization$.pipe(
|
private allCollections$ = combineLatest([this.organization$, this.userId$]).pipe(
|
||||||
switchMap(async (org) => await this.collectionAdminService.getAll(org.id)),
|
switchMap(([org, userId]) => this.collectionAdminService.collectionAdminViews$(org.id, userId)),
|
||||||
);
|
);
|
||||||
|
|
||||||
async buildConfig(
|
async buildConfig(
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export class ServiceContainer extends OssServiceContainer {
|
|||||||
this.keyService,
|
this.keyService,
|
||||||
this.encryptService,
|
this.encryptService,
|
||||||
this.organizationUserApiService,
|
this.organizationUserApiService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { MockProxy, mock } from "jest-mock-extended";
|
import { MockProxy, mock } from "jest-mock-extended";
|
||||||
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
@@ -8,30 +9,42 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract
|
|||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
import { newGuid } from "@bitwarden/guid";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
import { UserId } from "@bitwarden/user-core";
|
||||||
|
|
||||||
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
||||||
import { OrganizationAuthRequestUpdateRequest } from "./organization-auth-request-update.request";
|
import { OrganizationAuthRequestUpdateRequest } from "./organization-auth-request-update.request";
|
||||||
import { OrganizationAuthRequestService } from "./organization-auth-request.service";
|
import { OrganizationAuthRequestService } from "./organization-auth-request.service";
|
||||||
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
||||||
|
|
||||||
|
import {
|
||||||
|
FakeAccountService,
|
||||||
|
mockAccountServiceWith,
|
||||||
|
} from "@bitwarden/common/../spec/fake-account-service";
|
||||||
|
|
||||||
describe("OrganizationAuthRequestService", () => {
|
describe("OrganizationAuthRequestService", () => {
|
||||||
let organizationAuthRequestApiService: MockProxy<OrganizationAuthRequestApiService>;
|
let organizationAuthRequestApiService: MockProxy<OrganizationAuthRequestApiService>;
|
||||||
let keyService: MockProxy<KeyService>;
|
let keyService: MockProxy<KeyService>;
|
||||||
let encryptService: MockProxy<EncryptService>;
|
let encryptService: MockProxy<EncryptService>;
|
||||||
let organizationUserApiService: MockProxy<OrganizationUserApiService>;
|
let organizationUserApiService: MockProxy<OrganizationUserApiService>;
|
||||||
let organizationAuthRequestService: OrganizationAuthRequestService;
|
let organizationAuthRequestService: OrganizationAuthRequestService;
|
||||||
|
const mockUserId = newGuid() as UserId;
|
||||||
|
let accountService: FakeAccountService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
organizationAuthRequestApiService = mock<OrganizationAuthRequestApiService>();
|
organizationAuthRequestApiService = mock<OrganizationAuthRequestApiService>();
|
||||||
keyService = mock<KeyService>();
|
keyService = mock<KeyService>();
|
||||||
encryptService = mock<EncryptService>();
|
encryptService = mock<EncryptService>();
|
||||||
organizationUserApiService = mock<OrganizationUserApiService>();
|
organizationUserApiService = mock<OrganizationUserApiService>();
|
||||||
|
accountService = mockAccountServiceWith(mockUserId);
|
||||||
|
|
||||||
organizationAuthRequestService = new OrganizationAuthRequestService(
|
organizationAuthRequestService = new OrganizationAuthRequestService(
|
||||||
organizationAuthRequestApiService,
|
organizationAuthRequestApiService,
|
||||||
keyService,
|
keyService,
|
||||||
encryptService,
|
encryptService,
|
||||||
organizationUserApiService,
|
organizationUserApiService,
|
||||||
|
accountService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -162,6 +175,7 @@ describe("OrganizationAuthRequestService", () => {
|
|||||||
describe("approvePendingRequests", () => {
|
describe("approvePendingRequests", () => {
|
||||||
it("should approve the specified pending auth requests", async () => {
|
it("should approve the specified pending auth requests", async () => {
|
||||||
jest.spyOn(organizationAuthRequestApiService, "bulkUpdatePendingRequests");
|
jest.spyOn(organizationAuthRequestApiService, "bulkUpdatePendingRequests");
|
||||||
|
jest.spyOn(keyService, "orgKeys$").mockReturnValue(of({ key: "fake-key" }));
|
||||||
|
|
||||||
const organizationId = "organizationId";
|
const organizationId = "organizationId";
|
||||||
|
|
||||||
@@ -213,6 +227,7 @@ describe("OrganizationAuthRequestService", () => {
|
|||||||
describe("approvePendingRequest", () => {
|
describe("approvePendingRequest", () => {
|
||||||
it("should approve the specified pending auth request", async () => {
|
it("should approve the specified pending auth request", async () => {
|
||||||
jest.spyOn(organizationAuthRequestApiService, "approvePendingRequest");
|
jest.spyOn(organizationAuthRequestApiService, "approvePendingRequest");
|
||||||
|
jest.spyOn(keyService, "orgKeys$").mockReturnValue(of({ key: "fake-key" }));
|
||||||
|
|
||||||
const organizationId = "organizationId";
|
const organizationId = "organizationId";
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
|
import { firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
OrganizationUserResetPasswordDetailsResponse,
|
OrganizationUserResetPasswordDetailsResponse,
|
||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
|
import { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
||||||
@@ -20,6 +25,7 @@ export class OrganizationAuthRequestService {
|
|||||||
private keyService: KeyService,
|
private keyService: KeyService,
|
||||||
private encryptService: EncryptService,
|
private encryptService: EncryptService,
|
||||||
private organizationUserApiService: OrganizationUserApiService,
|
private organizationUserApiService: OrganizationUserApiService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async listPendingRequests(organizationId: string): Promise<PendingAuthRequestView[]> {
|
async listPendingRequests(organizationId: string): Promise<PendingAuthRequestView[]> {
|
||||||
@@ -122,7 +128,13 @@ export class OrganizationAuthRequestService {
|
|||||||
const devicePubKey = Utils.fromB64ToArray(devicePublicKey);
|
const devicePubKey = Utils.fromB64ToArray(devicePublicKey);
|
||||||
|
|
||||||
// Decrypt Organization's encrypted Private Key with org key
|
// Decrypt Organization's encrypted Private Key with org key
|
||||||
const orgSymKey = await this.keyService.getOrgKey(organizationId);
|
const orgSymKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[organizationId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
const decOrgPrivateKey = await this.encryptService.decryptBytes(
|
const decOrgPrivateKey = await this.encryptService.decryptBytes(
|
||||||
new EncString(encryptedOrgPrivateKey),
|
new EncString(encryptedOrgPrivateKey),
|
||||||
orgSymKey,
|
orgSymKey,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { OrganizationAuthRequestService } from "@bitwarden/bit-common/admin-cons
|
|||||||
import { PendingAuthRequestWithFingerprintView } from "@bitwarden/bit-common/admin-console/auth-requests/pending-auth-request-with-fingerprint.view";
|
import { PendingAuthRequestWithFingerprintView } from "@bitwarden/bit-common/admin-console/auth-requests/pending-auth-request-with-fingerprint.view";
|
||||||
import { PendingAuthRequestView } from "@bitwarden/bit-common/admin-console/auth-requests/pending-auth-request.view";
|
import { PendingAuthRequestView } from "@bitwarden/bit-common/admin-console/auth-requests/pending-auth-request.view";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -38,6 +39,7 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
|
|||||||
KeyService,
|
KeyService,
|
||||||
EncryptService,
|
EncryptService,
|
||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
|
AccountService,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
] satisfies SafeProvider[],
|
] satisfies SafeProvider[],
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|||||||
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
import { ProviderApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider/provider-api.service.abstraction";
|
||||||
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "@bitwarden/common/admin-console/models/request/organization-keys.request";
|
||||||
import { ProviderAddOrganizationRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-add-organization.request";
|
import { ProviderAddOrganizationRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-add-organization.request";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
import { PlanType } from "@bitwarden/common/billing/enums";
|
import { PlanType } from "@bitwarden/common/billing/enums";
|
||||||
import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request";
|
import { CreateClientOrganizationRequest } from "@bitwarden/common/billing/models/request/create-client-organization.request";
|
||||||
@@ -30,10 +32,17 @@ export class WebProviderService {
|
|||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
private stateProvider: StateProvider,
|
private stateProvider: StateProvider,
|
||||||
private providerApiService: ProviderApiServiceAbstraction,
|
private providerApiService: ProviderApiServiceAbstraction,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async addOrganizationToProvider(providerId: string, organizationId: string) {
|
async addOrganizationToProvider(providerId: string, organizationId: string) {
|
||||||
const orgKey = await this.keyService.getOrgKey(organizationId);
|
const orgKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map((orgKeys) => orgKeys[organizationId as OrganizationId] ?? null),
|
||||||
|
),
|
||||||
|
);
|
||||||
const providerKey = await this.keyService.getProviderKey(providerId);
|
const providerKey = await this.keyService.getProviderKey(providerId);
|
||||||
|
|
||||||
const encryptedOrgKey = await this.encryptService.wrapSymmetricKey(orgKey, providerKey);
|
const encryptedOrgKey = await this.encryptService.wrapSymmetricKey(orgKey, providerKey);
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { CollectionDetailsResponse } from "@bitwarden/admin-console/common";
|
import { CollectionDetailsResponse } from "@bitwarden/admin-console/common";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { CollectionAccessSelectionView, CollectionAdminView } from "../models";
|
import { CollectionAccessSelectionView, CollectionAdminView } from "../models";
|
||||||
|
|
||||||
export abstract class CollectionAdminService {
|
export abstract class CollectionAdminService {
|
||||||
abstract getAll(organizationId: string): Promise<CollectionAdminView[]>;
|
abstract collectionAdminViews$(
|
||||||
abstract get(
|
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
collectionId: string,
|
userId: UserId,
|
||||||
): Promise<CollectionAdminView | undefined>;
|
): Observable<CollectionAdminView[]>;
|
||||||
abstract save(
|
abstract save(
|
||||||
collection: CollectionAdminView,
|
collection: CollectionAdminView,
|
||||||
userId: UserId,
|
userId: UserId,
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
|
|
||||||
|
import { combineLatest, firstValueFrom, from, map, Observable, of, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { CollectionId, UserId } from "@bitwarden/common/types/guid";
|
import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||||
|
import { OrgKey } from "@bitwarden/common/types/key";
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { CollectionAdminService, CollectionService } from "../abstractions";
|
import { CollectionAdminService, CollectionService } from "../abstractions";
|
||||||
@@ -28,37 +31,23 @@ export class DefaultCollectionAdminService implements CollectionAdminService {
|
|||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getAll(organizationId: string): Promise<CollectionAdminView[]> {
|
collectionAdminViews$(organizationId: string, userId: UserId): Observable<CollectionAdminView[]> {
|
||||||
const collectionResponse =
|
return combineLatest([
|
||||||
await this.apiService.getManyCollectionsWithAccessDetails(organizationId);
|
this.keyService.orgKeys$(userId),
|
||||||
|
from(this.apiService.getManyCollectionsWithAccessDetails(organizationId)),
|
||||||
|
]).pipe(
|
||||||
|
switchMap(([orgKey, res]) => {
|
||||||
|
if (res?.data == null || res.data.length === 0) {
|
||||||
|
return of([]);
|
||||||
|
}
|
||||||
|
|
||||||
if (collectionResponse?.data == null || collectionResponse.data.length === 0) {
|
return this.decryptMany(organizationId, res.data, orgKey);
|
||||||
return [];
|
}),
|
||||||
}
|
|
||||||
|
|
||||||
return await this.decryptMany(organizationId, collectionResponse.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async get(
|
|
||||||
organizationId: string,
|
|
||||||
collectionId: string,
|
|
||||||
): Promise<CollectionAdminView | undefined> {
|
|
||||||
const collectionResponse = await this.apiService.getCollectionAccessDetails(
|
|
||||||
organizationId,
|
|
||||||
collectionId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (collectionResponse == null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [view] = await this.decryptMany(organizationId, [collectionResponse]);
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(collection: CollectionAdminView, userId: UserId): Promise<CollectionDetailsResponse> {
|
async save(collection: CollectionAdminView, userId: UserId): Promise<CollectionDetailsResponse> {
|
||||||
const request = await this.encrypt(collection);
|
const request = await this.encrypt(collection, userId);
|
||||||
|
|
||||||
let response: CollectionDetailsResponse;
|
let response: CollectionDetailsResponse;
|
||||||
if (collection.id == null) {
|
if (collection.id == null) {
|
||||||
@@ -112,13 +101,15 @@ export class DefaultCollectionAdminService implements CollectionAdminService {
|
|||||||
private async decryptMany(
|
private async decryptMany(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
collections: CollectionResponse[] | CollectionAccessDetailsResponse[],
|
collections: CollectionResponse[] | CollectionAccessDetailsResponse[],
|
||||||
|
orgKeys: Record<OrganizationId, OrgKey>,
|
||||||
): Promise<CollectionAdminView[]> {
|
): Promise<CollectionAdminView[]> {
|
||||||
const orgKey = await this.keyService.getOrgKey(organizationId);
|
|
||||||
|
|
||||||
const promises = collections.map(async (c) => {
|
const promises = collections.map(async (c) => {
|
||||||
const view = new CollectionAdminView();
|
const view = new CollectionAdminView();
|
||||||
view.id = c.id;
|
view.id = c.id;
|
||||||
view.name = await this.encryptService.decryptString(new EncString(c.name), orgKey);
|
view.name = await this.encryptService.decryptString(
|
||||||
|
new EncString(c.name),
|
||||||
|
orgKeys[organizationId as OrganizationId],
|
||||||
|
);
|
||||||
view.externalId = c.externalId;
|
view.externalId = c.externalId;
|
||||||
view.organizationId = c.organizationId;
|
view.organizationId = c.organizationId;
|
||||||
|
|
||||||
@@ -138,11 +129,15 @@ export class DefaultCollectionAdminService implements CollectionAdminService {
|
|||||||
return await Promise.all(promises);
|
return await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async encrypt(model: CollectionAdminView): Promise<CollectionRequest> {
|
private async encrypt(model: CollectionAdminView, userId: UserId): Promise<CollectionRequest> {
|
||||||
if (model.organizationId == null) {
|
if (model.organizationId == null) {
|
||||||
throw new Error("Collection has no organization id.");
|
throw new Error("Collection has no organization id.");
|
||||||
}
|
}
|
||||||
const key = await this.keyService.getOrgKey(model.organizationId);
|
const key = await firstValueFrom(
|
||||||
|
this.keyService
|
||||||
|
.orgKeys$(userId)
|
||||||
|
.pipe(map((orgKeys) => orgKeys[model.organizationId] ?? null)),
|
||||||
|
);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
throw new Error("No key for this collection's organization.");
|
throw new Error("No key for this collection's organization.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
|
|
||||||
this.collections$ = Utils.asyncToObservable(() =>
|
this.collections$ = Utils.asyncToObservable(() =>
|
||||||
this.importCollectionService
|
this.importCollectionService
|
||||||
.getAllAdminCollections(this.organizationId)
|
.getAllAdminCollections(this.organizationId, userId)
|
||||||
.then((collections) => collections.sort(Utils.getSortFunction(this.i18nService, "name"))),
|
.then((collections) => collections.sort(Utils.getSortFunction(this.i18nService, "name"))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { CollectionView } from "@bitwarden/admin-console/common";
|
import { CollectionView } from "@bitwarden/admin-console/common";
|
||||||
|
import { UserId } from "@bitwarden/user-core";
|
||||||
|
|
||||||
export abstract class ImportCollectionServiceAbstraction {
|
export abstract class ImportCollectionServiceAbstraction {
|
||||||
getAllAdminCollections: (organizationId: string) => Promise<CollectionView[]>;
|
getAllAdminCollections: (organizationId: string, userId: UserId) => Promise<CollectionView[]>;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user