mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 14:53:33 +00:00
[PM-24279] Utilize Policy vNext endpoint (#16317)
This commit is contained in:
@@ -9,13 +9,17 @@ import {
|
|||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { Observable, map } from "rxjs";
|
import { Observable, map, firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request";
|
|
||||||
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
||||||
|
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 { 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 { OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import {
|
import {
|
||||||
DIALOG_DATA,
|
DIALOG_DATA,
|
||||||
DialogConfig,
|
DialogConfig,
|
||||||
@@ -23,8 +27,10 @@ import {
|
|||||||
DialogService,
|
DialogService,
|
||||||
ToastService,
|
ToastService,
|
||||||
} from "@bitwarden/components";
|
} from "@bitwarden/components";
|
||||||
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { BasePolicy, BasePolicyComponent } from "../policies";
|
import { BasePolicy, BasePolicyComponent } from "../policies";
|
||||||
|
import { vNextOrganizationDataOwnershipPolicyComponent } from "../policies/vnext-organization-data-ownership.component";
|
||||||
|
|
||||||
export type PolicyEditDialogData = {
|
export type PolicyEditDialogData = {
|
||||||
/** Returns policy abstracts. */
|
/** Returns policy abstracts. */
|
||||||
@@ -59,12 +65,15 @@ export class PolicyEditComponent implements AfterViewInit {
|
|||||||
});
|
});
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DIALOG_DATA) protected data: PolicyEditDialogData,
|
@Inject(DIALOG_DATA) protected data: PolicyEditDialogData,
|
||||||
|
private accountService: AccountService,
|
||||||
private policyApiService: PolicyApiServiceAbstraction,
|
private policyApiService: PolicyApiServiceAbstraction,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private cdr: ChangeDetectorRef,
|
private cdr: ChangeDetectorRef,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private dialogRef: DialogRef<PolicyEditDialogResult>,
|
private dialogRef: DialogRef<PolicyEditDialogResult>,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private keyService: KeyService,
|
||||||
) {}
|
) {}
|
||||||
get policy(): BasePolicy {
|
get policy(): BasePolicy {
|
||||||
return this.data.policy;
|
return this.data.policy;
|
||||||
@@ -107,24 +116,70 @@ export class PolicyEditComponent implements AfterViewInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let request: PolicyRequest;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
request = await this.policyComponent.buildRequest();
|
if (
|
||||||
} catch (e) {
|
this.policyComponent instanceof vNextOrganizationDataOwnershipPolicyComponent &&
|
||||||
this.toastService.showToast({ variant: "error", title: null, message: e.message });
|
(await this.isVNextEnabled())
|
||||||
return;
|
) {
|
||||||
|
await this.handleVNextSubmission(this.policyComponent);
|
||||||
|
} else {
|
||||||
|
await this.handleStandardSubmission();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.policyApiService.putPolicy(this.data.organizationId, this.data.policy.type, request);
|
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: null,
|
||||||
message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)),
|
message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)),
|
||||||
});
|
});
|
||||||
this.dialogRef.close(PolicyEditDialogResult.Saved);
|
this.dialogRef.close(PolicyEditDialogResult.Saved);
|
||||||
|
} catch (error) {
|
||||||
|
this.toastService.showToast({
|
||||||
|
variant: "error",
|
||||||
|
title: null,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async isVNextEnabled(): Promise<boolean> {
|
||||||
|
const isVNextFeatureEnabled = await firstValueFrom(
|
||||||
|
this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation),
|
||||||
|
);
|
||||||
|
|
||||||
|
return isVNextFeatureEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleStandardSubmission(): Promise<void> {
|
||||||
|
const request = await this.policyComponent.buildRequest();
|
||||||
|
await this.policyApiService.putPolicy(this.data.organizationId, this.data.policy.type, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleVNextSubmission(
|
||||||
|
policyComponent: vNextOrganizationDataOwnershipPolicyComponent,
|
||||||
|
): Promise<void> {
|
||||||
|
const orgKey = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(
|
||||||
|
getUserId,
|
||||||
|
switchMap((userId) => this.keyService.orgKeys$(userId)),
|
||||||
|
map(
|
||||||
|
(orgKeys: { [key: OrganizationId]: any }) =>
|
||||||
|
orgKeys[this.data.organizationId as OrganizationId] ?? null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (orgKey == null) {
|
||||||
|
throw new Error("No encryption key for this organization.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const vNextRequest = await policyComponent.buildVNextRequest(orgKey);
|
||||||
|
|
||||||
|
await this.policyApiService.putPolicyVNext(
|
||||||
|
this.data.organizationId,
|
||||||
|
this.data.policy.type,
|
||||||
|
vNextRequest,
|
||||||
|
);
|
||||||
|
}
|
||||||
static open = (dialogService: DialogService, config: DialogConfig<PolicyEditDialogData>) => {
|
static open = (dialogService: DialogService, config: DialogConfig<PolicyEditDialogData>) => {
|
||||||
return dialogService.open<PolicyEditDialogResult>(PolicyEditComponent, config);
|
return dialogService.open<PolicyEditDialogResult>(PolicyEditComponent, config);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,14 +3,26 @@ import { lastValueFrom, Observable } from "rxjs";
|
|||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request";
|
||||||
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 { 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 { OrgKey } from "@bitwarden/common/types/key";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
import { EncString } from "@bitwarden/sdk-internal";
|
||||||
|
|
||||||
import { SharedModule } from "../../../shared";
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||||
|
|
||||||
|
interface VNextPolicyRequest {
|
||||||
|
policy: PolicyRequest;
|
||||||
|
metadata: {
|
||||||
|
defaultUserCollectionName: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export class vNextOrganizationDataOwnershipPolicy extends BasePolicy {
|
export class vNextOrganizationDataOwnershipPolicy extends BasePolicy {
|
||||||
name = "organizationDataOwnership";
|
name = "organizationDataOwnership";
|
||||||
description = "organizationDataOwnershipDesc";
|
description = "organizationDataOwnershipDesc";
|
||||||
@@ -33,7 +45,11 @@ export class vNextOrganizationDataOwnershipPolicyComponent
|
|||||||
extends BasePolicyComponent
|
extends BasePolicyComponent
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
constructor(private dialogService: DialogService) {
|
constructor(
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private encryptService: EncryptService,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,4 +63,36 @@ export class vNextOrganizationDataOwnershipPolicyComponent
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async buildVNextRequest(orgKey: OrgKey): Promise<VNextPolicyRequest> {
|
||||||
|
if (!this.policy) {
|
||||||
|
throw new Error("Policy was not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultUserCollectionName = await this.getEncryptedDefaultUserCollectionName(orgKey);
|
||||||
|
|
||||||
|
const request: VNextPolicyRequest = {
|
||||||
|
policy: {
|
||||||
|
type: this.policy.type,
|
||||||
|
enabled: this.enabled.value,
|
||||||
|
data: this.buildRequestData(),
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
defaultUserCollectionName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getEncryptedDefaultUserCollectionName(orgKey: OrgKey): Promise<EncString> {
|
||||||
|
const defaultCollectionName = this.i18nService.t("myItems");
|
||||||
|
const encrypted = await this.encryptService.encryptString(defaultCollectionName, orgKey);
|
||||||
|
|
||||||
|
if (!encrypted.encryptedString) {
|
||||||
|
throw new Error("Encryption error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return encrypted.encryptedString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,5 @@ export abstract class PolicyApiServiceAbstraction {
|
|||||||
type: PolicyType,
|
type: PolicyType,
|
||||||
request: PolicyRequest,
|
request: PolicyRequest,
|
||||||
) => Promise<any>;
|
) => Promise<any>;
|
||||||
|
abstract putPolicyVNext: (organizationId: string, type: PolicyType, request: any) => Promise<any>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,16 +116,32 @@ export class PolicyApiService implements PolicyApiServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async putPolicy(organizationId: string, type: PolicyType, request: PolicyRequest): Promise<any> {
|
async putPolicy(organizationId: string, type: PolicyType, request: PolicyRequest): Promise<any> {
|
||||||
const r = await this.apiService.send(
|
const response = await this.apiService.send(
|
||||||
"PUT",
|
"PUT",
|
||||||
"/organizations/" + organizationId + "/policies/" + type,
|
"/organizations/" + organizationId + "/policies/" + type,
|
||||||
request,
|
request,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
await this.handleResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async putPolicyVNext(organizationId: string, type: PolicyType, request: any): Promise<any> {
|
||||||
|
const response = await this.apiService.send(
|
||||||
|
"PUT",
|
||||||
|
`/organizations/${organizationId}/policies/${type}/vnext`,
|
||||||
|
request,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.handleResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleResponse(response: any): Promise<void> {
|
||||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
const response = new PolicyResponse(r);
|
const policyResponse = new PolicyResponse(response);
|
||||||
const data = new PolicyData(response);
|
const data = new PolicyData(policyResponse);
|
||||||
await this.policyService.upsert(data, userId);
|
await this.policyService.upsert(data, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user