[SM-910] Migrate service account -> projects tab to new access policy selector (#8572)
* Add view, requests and responses * access policy service update * Add read only support to access policy selector * Migrate service account -> projects tab
This commit is contained in:
parent
a4b3b83c46
commit
af0a884ee8
|
@ -58,3 +58,12 @@ export class ServiceAccountPeopleAccessPoliciesView {
|
||||||
userAccessPolicies: UserServiceAccountAccessPolicyView[];
|
userAccessPolicies: UserServiceAccountAccessPolicyView[];
|
||||||
groupAccessPolicies: GroupServiceAccountAccessPolicyView[];
|
groupAccessPolicies: GroupServiceAccountAccessPolicyView[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ServiceAccountProjectPolicyPermissionDetailsView {
|
||||||
|
accessPolicy: ServiceAccountProjectAccessPolicyView;
|
||||||
|
hasPermission: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ServiceAccountGrantedPoliciesView {
|
||||||
|
grantedProjectPolicies: ServiceAccountProjectPolicyPermissionDetailsView[];
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
<div class="tw-mt-4 tw-w-2/5">
|
<form [formGroup]="formGroup" [bitSubmit]="submit" *ngIf="!loading; else spinner">
|
||||||
<p class="tw-mt-6">
|
<div class="tw-w-2/5">
|
||||||
{{ "machineAccountProjectsDescription" | i18n }}
|
<p class="tw-mt-8" *ngIf="!loading">
|
||||||
</p>
|
{{ "machineAccountProjectsDescription" | i18n }}
|
||||||
<sm-access-selector
|
</p>
|
||||||
[rows]="rows$ | async"
|
<sm-access-policy-selector
|
||||||
granteeType="projects"
|
[loading]="loading"
|
||||||
[label]="'projects' | i18n"
|
formControlName="accessPolicies"
|
||||||
[hint]="'newSaSelectAccess' | i18n"
|
[addButtonMode]="true"
|
||||||
[columnTitle]="'projects' | i18n"
|
[items]="potentialGrantees"
|
||||||
[emptyMessage]="'serviceAccountEmptyProjectAccesspolicies' | i18n"
|
[label]="'projects' | i18n"
|
||||||
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
[hint]="'newSaSelectAccess' | i18n"
|
||||||
(onDeleteAccessPolicy)="handleDeleteAccessPolicy($event)"
|
[columnTitle]="'projects' | i18n"
|
||||||
(onUpdateAccessPolicy)="handleUpdateAccessPolicy($event)"
|
[emptyMessage]="'serviceAccountEmptyProjectAccesspolicies' | i18n"
|
||||||
>
|
>
|
||||||
</sm-access-selector>
|
</sm-access-policy-selector>
|
||||||
</div>
|
<button bitButton buttonType="primary" bitFormButton type="submit" class="tw-mt-7">
|
||||||
|
{{ "save" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ng-template #spinner>
|
||||||
|
<div class="tw-items-center tw-justify-center tw-pt-64 tw-text-center">
|
||||||
|
<i class="bwi bwi-spinner bwi-spin bwi-3x"></i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
|
@ -1,90 +1,68 @@
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatestWith, map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
import { combineLatest, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
|
||||||
|
|
||||||
import { ServiceAccountProjectAccessPolicyView } from "../../models/view/access-policy.view";
|
import { ServiceAccountGrantedPoliciesView } from "../../models/view/access-policy.view";
|
||||||
import { AccessPolicyService } from "../../shared/access-policies/access-policy.service";
|
|
||||||
import {
|
import {
|
||||||
AccessSelectorComponent,
|
ApItemValueType,
|
||||||
AccessSelectorRowView,
|
convertToServiceAccountGrantedPoliciesView,
|
||||||
} from "../../shared/access-policies/access-selector.component";
|
} from "../../shared/access-policies/access-policy-selector/models/ap-item-value.type";
|
||||||
|
import {
|
||||||
|
ApItemViewType,
|
||||||
|
convertPotentialGranteesToApItemViewType,
|
||||||
|
convertGrantedPoliciesToAccessPolicyItemViews,
|
||||||
|
} from "../../shared/access-policies/access-policy-selector/models/ap-item-view.type";
|
||||||
|
import { AccessPolicyService } from "../../shared/access-policies/access-policy.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "sm-service-account-projects",
|
selector: "sm-service-account-projects",
|
||||||
templateUrl: "./service-account-projects.component.html",
|
templateUrl: "./service-account-projects.component.html",
|
||||||
})
|
})
|
||||||
export class ServiceAccountProjectsComponent implements OnInit, OnDestroy {
|
export class ServiceAccountProjectsComponent implements OnInit, OnDestroy {
|
||||||
|
private currentAccessPolicies: ApItemViewType[];
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private serviceAccountId: string;
|
|
||||||
private organizationId: string;
|
private organizationId: string;
|
||||||
|
private serviceAccountId: string;
|
||||||
|
|
||||||
protected rows$: Observable<AccessSelectorRowView[]> =
|
private currentAccessPolicies$ = combineLatest([this.route.params]).pipe(
|
||||||
this.accessPolicyService.serviceAccountGrantedPolicyChanges$.pipe(
|
switchMap(([params]) =>
|
||||||
startWith(null),
|
this.accessPolicyService
|
||||||
combineLatestWith(this.route.params),
|
.getServiceAccountGrantedPolicies(params.organizationId, params.serviceAccountId)
|
||||||
switchMap(([_, params]) =>
|
.then((policies) => {
|
||||||
this.accessPolicyService.getGrantedPolicies(params.serviceAccountId, params.organizationId),
|
return convertGrantedPoliciesToAccessPolicyItemViews(policies);
|
||||||
),
|
}),
|
||||||
map((policies) => {
|
),
|
||||||
return policies.map((policy) => {
|
);
|
||||||
return {
|
|
||||||
type: "project",
|
|
||||||
name: policy.grantedProjectName,
|
|
||||||
id: policy.grantedProjectId,
|
|
||||||
accessPolicyId: policy.id,
|
|
||||||
read: policy.read,
|
|
||||||
write: policy.write,
|
|
||||||
icon: AccessSelectorComponent.projectIcon,
|
|
||||||
static: false,
|
|
||||||
} as AccessSelectorRowView;
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
protected handleCreateAccessPolicies(selected: SelectItemView[]) {
|
private potentialGrantees$ = combineLatest([this.route.params]).pipe(
|
||||||
const serviceAccountProjectAccessPolicyView = selected
|
switchMap(([params]) =>
|
||||||
.filter((selection) => AccessSelectorComponent.getAccessItemType(selection) === "project")
|
this.accessPolicyService
|
||||||
.map((filtered) => {
|
.getProjectsPotentialGrantees(params.organizationId)
|
||||||
const view = new ServiceAccountProjectAccessPolicyView();
|
.then((grantees) => {
|
||||||
view.serviceAccountId = this.serviceAccountId;
|
return convertPotentialGranteesToApItemViewType(grantees);
|
||||||
view.grantedProjectId = filtered.id;
|
}),
|
||||||
view.read = true;
|
),
|
||||||
view.write = false;
|
);
|
||||||
return view;
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.accessPolicyService.createGrantedPolicies(
|
protected formGroup = new FormGroup({
|
||||||
this.organizationId,
|
accessPolicies: new FormControl([] as ApItemValueType[]),
|
||||||
this.serviceAccountId,
|
});
|
||||||
serviceAccountProjectAccessPolicyView,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async handleUpdateAccessPolicy(policy: AccessSelectorRowView) {
|
protected loading = true;
|
||||||
try {
|
protected potentialGrantees: ApItemViewType[];
|
||||||
return await this.accessPolicyService.updateAccessPolicy(
|
|
||||||
AccessSelectorComponent.getBaseAccessPolicyView(policy),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
this.validationService.showError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async handleDeleteAccessPolicy(policy: AccessSelectorRowView) {
|
|
||||||
try {
|
|
||||||
await this.accessPolicyService.deleteAccessPolicy(policy.accessPolicyId);
|
|
||||||
} catch (e) {
|
|
||||||
this.validationService.showError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
private accessPolicyService: AccessPolicyService,
|
private accessPolicyService: AccessPolicyService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -92,10 +70,119 @@ export class ServiceAccountProjectsComponent implements OnInit, OnDestroy {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
this.serviceAccountId = params.serviceAccountId;
|
this.serviceAccountId = params.serviceAccountId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
combineLatest([this.potentialGrantees$, this.currentAccessPolicies$])
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(([potentialGrantees, currentAccessPolicies]) => {
|
||||||
|
this.potentialGrantees = this.getPotentialGrantees(
|
||||||
|
potentialGrantees,
|
||||||
|
currentAccessPolicies,
|
||||||
|
);
|
||||||
|
this.setSelected(currentAccessPolicies);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
|
if (this.isFormInvalid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const formValues = this.getFormValues();
|
||||||
|
this.formGroup.disable();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const grantedViews = await this.updateServiceAccountGrantedPolicies(
|
||||||
|
this.organizationId,
|
||||||
|
this.serviceAccountId,
|
||||||
|
formValues,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.currentAccessPolicies = convertGrantedPoliciesToAccessPolicyItemViews(grantedViews);
|
||||||
|
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
null,
|
||||||
|
this.i18nService.t("serviceAccountAccessUpdated"),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
this.setSelected(this.currentAccessPolicies);
|
||||||
|
}
|
||||||
|
this.formGroup.enable();
|
||||||
|
};
|
||||||
|
|
||||||
|
private setSelected(policiesToSelect: ApItemViewType[]) {
|
||||||
|
this.loading = true;
|
||||||
|
this.currentAccessPolicies = policiesToSelect;
|
||||||
|
if (policiesToSelect != undefined) {
|
||||||
|
// Must detect changes so that AccessSelector @Inputs() are aware of the latest
|
||||||
|
// potentialGrantees, otherwise no selected values will be patched below
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
this.formGroup.patchValue({
|
||||||
|
accessPolicies: policiesToSelect.map((m) => ({
|
||||||
|
type: m.type,
|
||||||
|
id: m.id,
|
||||||
|
permission: m.permission,
|
||||||
|
readOnly: m.readOnly,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isFormInvalid(): boolean {
|
||||||
|
this.formGroup.markAllAsTouched();
|
||||||
|
return this.formGroup.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateServiceAccountGrantedPolicies(
|
||||||
|
organizationId: string,
|
||||||
|
serviceAccountId: string,
|
||||||
|
selectedPolicies: ApItemValueType[],
|
||||||
|
): Promise<ServiceAccountGrantedPoliciesView> {
|
||||||
|
const grantedViews = convertToServiceAccountGrantedPoliciesView(
|
||||||
|
serviceAccountId,
|
||||||
|
selectedPolicies,
|
||||||
|
);
|
||||||
|
return await this.accessPolicyService.putServiceAccountGrantedPolicies(
|
||||||
|
organizationId,
|
||||||
|
serviceAccountId,
|
||||||
|
grantedViews,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPotentialGrantees(
|
||||||
|
potentialGrantees: ApItemViewType[],
|
||||||
|
currentAccessPolicies: ApItemViewType[],
|
||||||
|
) {
|
||||||
|
// If the user doesn't have access to the project, they won't be in the potentialGrantees list.
|
||||||
|
// Add them to the potentialGrantees list so they can be selected as read-only.
|
||||||
|
for (const policy of currentAccessPolicies) {
|
||||||
|
const exists = potentialGrantees.some((grantee) => grantee.id === policy.id);
|
||||||
|
if (!exists) {
|
||||||
|
potentialGrantees.push(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return potentialGrantees;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getFormValues(): ApItemValueType[] {
|
||||||
|
// The read-only disabled form values are not included in the formGroup value.
|
||||||
|
// Manually add them to the returned result to ensure they are included in the form submission.
|
||||||
|
let formValues = this.formGroup.value.accessPolicies;
|
||||||
|
formValues = formValues.concat(
|
||||||
|
this.currentAccessPolicies
|
||||||
|
.filter((m) => m.readOnly)
|
||||||
|
.map((m) => ({
|
||||||
|
id: m.id,
|
||||||
|
type: m.type,
|
||||||
|
permission: m.permission,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
return formValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,17 @@
|
||||||
bitRow
|
bitRow
|
||||||
*ngFor="let item of selectionList.selectedItems; let i = index"
|
*ngFor="let item of selectionList.selectedItems; let i = index"
|
||||||
[formGroupName]="i"
|
[formGroupName]="i"
|
||||||
|
[ngClass]="{ 'tw-text-muted': item.readOnly }"
|
||||||
>
|
>
|
||||||
<td bitCell class="tw-w-0 tw-pr-0">
|
<td bitCell class="tw-w-0 tw-pr-0">
|
||||||
<i class="bwi {{ item.icon }} tw-text-muted" aria-hidden="true"></i>
|
<i class="bwi {{ item.icon }}" aria-hidden="true"></i>
|
||||||
|
</td>
|
||||||
|
<td bitCell class="tw-max-w-sm tw-truncate">
|
||||||
|
{{ item.labelName }}
|
||||||
</td>
|
</td>
|
||||||
<td bitCell class="tw-max-w-sm tw-truncate">{{ item.labelName }}</td>
|
|
||||||
<td bitCell class="tw-mb-auto tw-inline-block tw-w-auto">
|
<td bitCell class="tw-mb-auto tw-inline-block tw-w-auto">
|
||||||
<select
|
<select
|
||||||
*ngIf="!staticPermission; else static"
|
*ngIf="!staticPermission && !item.readOnly; else readOnly"
|
||||||
bitInput
|
bitInput
|
||||||
formControlName="permission"
|
formControlName="permission"
|
||||||
(blur)="handleBlur()"
|
(blur)="handleBlur()"
|
||||||
|
@ -45,12 +48,20 @@
|
||||||
{{ p.labelId | i18n }}
|
{{ p.labelId | i18n }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
<ng-template #readOnly>
|
||||||
|
<ng-container *ngIf="item.readOnly; else static">
|
||||||
|
<div class="tw-overflow-hidden tw-overflow-ellipsis tw-whitespace-nowrap">
|
||||||
|
{{ item.permission | i18n }}
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ng-template>
|
||||||
<ng-template #static>
|
<ng-template #static>
|
||||||
<span>{{ staticPermission | i18n }}</span>
|
<span>{{ staticPermission | i18n }}</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</td>
|
</td>
|
||||||
<td bitCell class="tw-w-0">
|
<td bitCell class="tw-w-0">
|
||||||
<button
|
<button
|
||||||
|
*ngIf="!item.readOnly"
|
||||||
type="button"
|
type="button"
|
||||||
bitIconButton="bwi-close"
|
bitIconButton="bwi-close"
|
||||||
buttonType="main"
|
buttonType="main"
|
||||||
|
|
|
@ -35,6 +35,34 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn
|
||||||
private notifyOnTouch: () => void;
|
private notifyOnTouch: () => void;
|
||||||
private pauseChangeNotification: boolean;
|
private pauseChangeNotification: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the enabled/disabled state of provided row form group based on the item's readonly state.
|
||||||
|
* If a row is enabled, it also updates the enabled/disabled state of the permission control
|
||||||
|
* based on the item's accessAllItems state and the current value of `permissionMode`.
|
||||||
|
* @param controlRow - The form group for the row to update
|
||||||
|
* @param item - The access item that is represented by the row
|
||||||
|
*/
|
||||||
|
private updateRowControlDisableState = (
|
||||||
|
controlRow: FormGroup<ControlsOf<ApItemValueType>>,
|
||||||
|
item: ApItemViewType,
|
||||||
|
) => {
|
||||||
|
// Disable entire row form group if readOnly
|
||||||
|
if (item.readOnly || this.disabled) {
|
||||||
|
controlRow.disable();
|
||||||
|
} else {
|
||||||
|
controlRow.enable();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the enabled/disabled state of ALL row form groups based on each item's readonly state.
|
||||||
|
*/
|
||||||
|
private updateAllRowControlDisableStates = () => {
|
||||||
|
this.selectionList.forEachControlItem((controlRow, item) => {
|
||||||
|
this.updateRowControlDisableState(controlRow as FormGroup<ControlsOf<ApItemValueType>>, item);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The internal selection list that tracks the value of this form control / component.
|
* The internal selection list that tracks the value of this form control / component.
|
||||||
* It's responsible for keeping items sorted and synced with the rendered form controls
|
* It's responsible for keeping items sorted and synced with the rendered form controls
|
||||||
|
@ -59,6 +87,9 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn
|
||||||
currentUserInGroup: new FormControl(currentUserInGroup),
|
currentUserInGroup: new FormControl(currentUserInGroup),
|
||||||
currentUser: new FormControl(currentUser),
|
currentUser: new FormControl(currentUser),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateRowControlDisableState(fg, item);
|
||||||
|
|
||||||
return fg;
|
return fg;
|
||||||
}, this._itemComparator.bind(this));
|
}, this._itemComparator.bind(this));
|
||||||
|
|
||||||
|
@ -100,7 +131,13 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn
|
||||||
|
|
||||||
set items(val: ApItemViewType[]) {
|
set items(val: ApItemViewType[]) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
const selected = this.selectionList.formArray.getRawValue() ?? [];
|
let selected = this.selectionList.formArray.getRawValue() ?? [];
|
||||||
|
selected = selected.concat(
|
||||||
|
val
|
||||||
|
.filter((m) => m.readOnly)
|
||||||
|
.map((m) => ({ id: m.id, type: m.type, permission: m.permission })),
|
||||||
|
);
|
||||||
|
|
||||||
this.selectionList.populateItems(
|
this.selectionList.populateItems(
|
||||||
val.map((m) => {
|
val.map((m) => {
|
||||||
m.icon = m.icon ?? ApItemEnumUtil.itemIcon(m.type);
|
m.icon = m.icon ?? ApItemEnumUtil.itemIcon(m.type);
|
||||||
|
@ -137,6 +174,9 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn
|
||||||
} else {
|
} else {
|
||||||
this.formGroup.enable();
|
this.formGroup.enable();
|
||||||
this.multiSelectFormGroup.enable();
|
this.multiSelectFormGroup.enable();
|
||||||
|
// The enable() above automatically enables all the row controls,
|
||||||
|
// so we need to disable the readonly ones again
|
||||||
|
this.updateAllRowControlDisableStates();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +189,9 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn
|
||||||
// Always clear the internal selection list on a new value
|
// Always clear the internal selection list on a new value
|
||||||
this.selectionList.deselectAll();
|
this.selectionList.deselectAll();
|
||||||
|
|
||||||
|
// We need to also select any read only items to appear in the table
|
||||||
|
this.selectionList.selectItems(this.items.filter((m) => m.readOnly).map((m) => m.id));
|
||||||
|
|
||||||
// If the new value is null, then we're done
|
// If the new value is null, then we're done
|
||||||
if (selectedItems == null) {
|
if (selectedItems == null) {
|
||||||
this.pauseChangeNotification = false;
|
this.pauseChangeNotification = false;
|
||||||
|
|
|
@ -347,6 +347,7 @@ function createApItemViewType(options: Partial<ApItemViewType> = {}) {
|
||||||
labelName: options?.labelName ?? "test",
|
labelName: options?.labelName ?? "test",
|
||||||
type: options?.type ?? ApItemEnum.User,
|
type: options?.type ?? ApItemEnum.User,
|
||||||
permission: options?.permission ?? ApPermissionEnum.CanRead,
|
permission: options?.permission ?? ApPermissionEnum.CanRead,
|
||||||
|
readOnly: options?.readOnly ?? false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ import {
|
||||||
ServiceAccountPeopleAccessPoliciesView,
|
ServiceAccountPeopleAccessPoliciesView,
|
||||||
UserServiceAccountAccessPolicyView,
|
UserServiceAccountAccessPolicyView,
|
||||||
GroupServiceAccountAccessPolicyView,
|
GroupServiceAccountAccessPolicyView,
|
||||||
|
ServiceAccountGrantedPoliciesView,
|
||||||
|
ServiceAccountProjectPolicyPermissionDetailsView,
|
||||||
|
ServiceAccountProjectAccessPolicyView,
|
||||||
} from "../../../../models/view/access-policy.view";
|
} from "../../../../models/view/access-policy.view";
|
||||||
|
|
||||||
import { ApItemEnum } from "./enums/ap-item.enum";
|
import { ApItemEnum } from "./enums/ap-item.enum";
|
||||||
|
@ -76,3 +79,26 @@ export function convertToServiceAccountPeopleAccessPoliciesView(
|
||||||
});
|
});
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertToServiceAccountGrantedPoliciesView(
|
||||||
|
serviceAccountId: string,
|
||||||
|
selectedPolicyValues: ApItemValueType[],
|
||||||
|
): ServiceAccountGrantedPoliciesView {
|
||||||
|
const view = new ServiceAccountGrantedPoliciesView();
|
||||||
|
|
||||||
|
view.grantedProjectPolicies = selectedPolicyValues
|
||||||
|
.filter((x) => x.type == ApItemEnum.Project)
|
||||||
|
.map((filtered) => {
|
||||||
|
const detailView = new ServiceAccountProjectPolicyPermissionDetailsView();
|
||||||
|
const policyView = new ServiceAccountProjectAccessPolicyView();
|
||||||
|
policyView.serviceAccountId = serviceAccountId;
|
||||||
|
policyView.grantedProjectId = filtered.id;
|
||||||
|
policyView.read = ApPermissionEnumUtil.toRead(filtered.permission);
|
||||||
|
policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission);
|
||||||
|
|
||||||
|
detailView.accessPolicy = policyView;
|
||||||
|
return detailView;
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { SelectItemView } from "@bitwarden/components";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProjectPeopleAccessPoliciesView,
|
ProjectPeopleAccessPoliciesView,
|
||||||
|
ServiceAccountGrantedPoliciesView,
|
||||||
ServiceAccountPeopleAccessPoliciesView,
|
ServiceAccountPeopleAccessPoliciesView,
|
||||||
} from "../../../../models/view/access-policy.view";
|
} from "../../../../models/view/access-policy.view";
|
||||||
import { PotentialGranteeView } from "../../../../models/view/potential-grantee.view";
|
import { PotentialGranteeView } from "../../../../models/view/potential-grantee.view";
|
||||||
|
@ -13,6 +14,12 @@ import { ApPermissionEnum, ApPermissionEnumUtil } from "./enums/ap-permission.en
|
||||||
export type ApItemViewType = SelectItemView & {
|
export type ApItemViewType = SelectItemView & {
|
||||||
accessPolicyId?: string;
|
accessPolicyId?: string;
|
||||||
permission?: ApPermissionEnum;
|
permission?: ApPermissionEnum;
|
||||||
|
/**
|
||||||
|
* Flag that this item cannot be modified.
|
||||||
|
* This will disable the permission editor and will keep
|
||||||
|
* the item always selected.
|
||||||
|
*/
|
||||||
|
readOnly: boolean;
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
type: ApItemEnum.User;
|
type: ApItemEnum.User;
|
||||||
|
@ -47,6 +54,7 @@ export function convertToAccessPolicyItemViews(
|
||||||
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
||||||
userId: policy.userId,
|
userId: policy.userId,
|
||||||
currentUser: policy.currentUser,
|
currentUser: policy.currentUser,
|
||||||
|
readOnly: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,12 +68,36 @@ export function convertToAccessPolicyItemViews(
|
||||||
listName: policy.groupName,
|
listName: policy.groupName,
|
||||||
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write),
|
||||||
currentUserInGroup: policy.currentUserInGroup,
|
currentUserInGroup: policy.currentUserInGroup,
|
||||||
|
readOnly: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return accessPolicies;
|
return accessPolicies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function convertGrantedPoliciesToAccessPolicyItemViews(
|
||||||
|
value: ServiceAccountGrantedPoliciesView,
|
||||||
|
): ApItemViewType[] {
|
||||||
|
const accessPolicies: ApItemViewType[] = [];
|
||||||
|
|
||||||
|
value.grantedProjectPolicies.forEach((detailView) => {
|
||||||
|
accessPolicies.push({
|
||||||
|
type: ApItemEnum.Project,
|
||||||
|
icon: ApItemEnumUtil.itemIcon(ApItemEnum.Project),
|
||||||
|
id: detailView.accessPolicy.grantedProjectId,
|
||||||
|
accessPolicyId: detailView.accessPolicy.id,
|
||||||
|
labelName: detailView.accessPolicy.grantedProjectName,
|
||||||
|
listName: detailView.accessPolicy.grantedProjectName,
|
||||||
|
permission: ApPermissionEnumUtil.toApPermissionEnum(
|
||||||
|
detailView.accessPolicy.read,
|
||||||
|
detailView.accessPolicy.write,
|
||||||
|
),
|
||||||
|
readOnly: !detailView.hasPermission,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return accessPolicies;
|
||||||
|
}
|
||||||
|
|
||||||
export function convertPotentialGranteesToApItemViewType(
|
export function convertPotentialGranteesToApItemViewType(
|
||||||
grantees: PotentialGranteeView[],
|
grantees: PotentialGranteeView[],
|
||||||
): ApItemViewType[] {
|
): ApItemViewType[] {
|
||||||
|
@ -108,6 +140,7 @@ export function convertPotentialGranteesToApItemViewType(
|
||||||
listName: listName,
|
listName: listName,
|
||||||
currentUserInGroup: granteeView.currentUserInGroup,
|
currentUserInGroup: granteeView.currentUserInGroup,
|
||||||
currentUser: granteeView.currentUser,
|
currentUser: granteeView.currentUser,
|
||||||
|
readOnly: false,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,15 +18,17 @@ import {
|
||||||
UserProjectAccessPolicyView,
|
UserProjectAccessPolicyView,
|
||||||
UserServiceAccountAccessPolicyView,
|
UserServiceAccountAccessPolicyView,
|
||||||
ServiceAccountPeopleAccessPoliciesView,
|
ServiceAccountPeopleAccessPoliciesView,
|
||||||
|
ServiceAccountGrantedPoliciesView,
|
||||||
|
ServiceAccountProjectPolicyPermissionDetailsView,
|
||||||
} from "../../models/view/access-policy.view";
|
} from "../../models/view/access-policy.view";
|
||||||
import { PotentialGranteeView } from "../../models/view/potential-grantee.view";
|
import { PotentialGranteeView } from "../../models/view/potential-grantee.view";
|
||||||
import { AccessPoliciesCreateRequest } from "../../shared/access-policies/models/requests/access-policies-create.request";
|
import { AccessPoliciesCreateRequest } from "../../shared/access-policies/models/requests/access-policies-create.request";
|
||||||
import { PeopleAccessPoliciesRequest } from "../../shared/access-policies/models/requests/people-access-policies.request";
|
import { PeopleAccessPoliciesRequest } from "../../shared/access-policies/models/requests/people-access-policies.request";
|
||||||
import { ProjectAccessPoliciesResponse } from "../../shared/access-policies/models/responses/project-access-policies.response";
|
import { ProjectAccessPoliciesResponse } from "../../shared/access-policies/models/responses/project-access-policies.response";
|
||||||
|
import { ServiceAccountGrantedPoliciesRequest } from "../access-policies/models/requests/service-account-granted-policies.request";
|
||||||
|
|
||||||
import { AccessPolicyUpdateRequest } from "./models/requests/access-policy-update.request";
|
import { AccessPolicyUpdateRequest } from "./models/requests/access-policy-update.request";
|
||||||
import { AccessPolicyRequest } from "./models/requests/access-policy.request";
|
import { AccessPolicyRequest } from "./models/requests/access-policy.request";
|
||||||
import { GrantedPolicyRequest } from "./models/requests/granted-policy.request";
|
|
||||||
import {
|
import {
|
||||||
GroupServiceAccountAccessPolicyResponse,
|
GroupServiceAccountAccessPolicyResponse,
|
||||||
UserServiceAccountAccessPolicyResponse,
|
UserServiceAccountAccessPolicyResponse,
|
||||||
|
@ -36,28 +38,21 @@ import {
|
||||||
} from "./models/responses/access-policy.response";
|
} from "./models/responses/access-policy.response";
|
||||||
import { PotentialGranteeResponse } from "./models/responses/potential-grantee.response";
|
import { PotentialGranteeResponse } from "./models/responses/potential-grantee.response";
|
||||||
import { ProjectPeopleAccessPoliciesResponse } from "./models/responses/project-people-access-policies.response";
|
import { ProjectPeopleAccessPoliciesResponse } from "./models/responses/project-people-access-policies.response";
|
||||||
|
import { ServiceAccountGrantedPoliciesPermissionDetailsResponse } from "./models/responses/service-account-granted-policies-permission-details.response";
|
||||||
import { ServiceAccountPeopleAccessPoliciesResponse } from "./models/responses/service-account-people-access-policies.response";
|
import { ServiceAccountPeopleAccessPoliciesResponse } from "./models/responses/service-account-people-access-policies.response";
|
||||||
|
import { ServiceAccountProjectPolicyPermissionDetailsResponse } from "./models/responses/service-account-project-policy-permission-details.response";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: "root",
|
providedIn: "root",
|
||||||
})
|
})
|
||||||
export class AccessPolicyService {
|
export class AccessPolicyService {
|
||||||
private _projectAccessPolicyChanges$ = new Subject<ProjectAccessPoliciesView>();
|
private _projectAccessPolicyChanges$ = new Subject<ProjectAccessPoliciesView>();
|
||||||
private _serviceAccountGrantedPolicyChanges$ = new Subject<
|
|
||||||
ServiceAccountProjectAccessPolicyView[]
|
|
||||||
>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits when a project access policy is created or deleted.
|
* Emits when a project access policy is created or deleted.
|
||||||
*/
|
*/
|
||||||
readonly projectAccessPolicyChanges$ = this._projectAccessPolicyChanges$.asObservable();
|
readonly projectAccessPolicyChanges$ = this._projectAccessPolicyChanges$.asObservable();
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits when a service account granted policy is created or deleted.
|
|
||||||
*/
|
|
||||||
readonly serviceAccountGrantedPolicyChanges$ =
|
|
||||||
this._serviceAccountGrantedPolicyChanges$.asObservable();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
protected apiService: ApiService,
|
protected apiService: ApiService,
|
||||||
|
@ -68,44 +63,6 @@ export class AccessPolicyService {
|
||||||
this._projectAccessPolicyChanges$.next(null);
|
this._projectAccessPolicyChanges$.next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGrantedPolicies(
|
|
||||||
serviceAccountId: string,
|
|
||||||
organizationId: string,
|
|
||||||
): Promise<ServiceAccountProjectAccessPolicyView[]> {
|
|
||||||
const r = await this.apiService.send(
|
|
||||||
"GET",
|
|
||||||
"/service-accounts/" + serviceAccountId + "/granted-policies",
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
const results = new ListResponse(r, ServiceAccountProjectAccessPolicyResponse);
|
|
||||||
return await this.createServiceAccountProjectAccessPolicyViews(results.data, organizationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async createGrantedPolicies(
|
|
||||||
organizationId: string,
|
|
||||||
serviceAccountId: string,
|
|
||||||
policies: ServiceAccountProjectAccessPolicyView[],
|
|
||||||
): Promise<ServiceAccountProjectAccessPolicyView[]> {
|
|
||||||
const request = this.getGrantedPoliciesCreateRequest(policies);
|
|
||||||
const r = await this.apiService.send(
|
|
||||||
"POST",
|
|
||||||
"/service-accounts/" + serviceAccountId + "/granted-policies",
|
|
||||||
request,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
const results = new ListResponse(r, ServiceAccountProjectAccessPolicyResponse);
|
|
||||||
const views = await this.createServiceAccountProjectAccessPolicyViews(
|
|
||||||
results.data,
|
|
||||||
organizationId,
|
|
||||||
);
|
|
||||||
this._serviceAccountGrantedPolicyChanges$.next(views);
|
|
||||||
return views;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getProjectAccessPolicies(
|
async getProjectAccessPolicies(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
|
@ -184,6 +141,40 @@ export class AccessPolicyService {
|
||||||
return this.createServiceAccountPeopleAccessPoliciesView(results);
|
return this.createServiceAccountPeopleAccessPoliciesView(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getServiceAccountGrantedPolicies(
|
||||||
|
organizationId: string,
|
||||||
|
serviceAccountId: string,
|
||||||
|
): Promise<ServiceAccountGrantedPoliciesView> {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/service-accounts/" + serviceAccountId + "/granted-policies",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = new ServiceAccountGrantedPoliciesPermissionDetailsResponse(r);
|
||||||
|
return await this.createServiceAccountGrantedPoliciesView(result, organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async putServiceAccountGrantedPolicies(
|
||||||
|
organizationId: string,
|
||||||
|
serviceAccountId: string,
|
||||||
|
policies: ServiceAccountGrantedPoliciesView,
|
||||||
|
): Promise<ServiceAccountGrantedPoliciesView> {
|
||||||
|
const request = this.getServiceAccountGrantedPoliciesRequest(policies);
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"PUT",
|
||||||
|
"/service-accounts/" + serviceAccountId + "/granted-policies",
|
||||||
|
request,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = new ServiceAccountGrantedPoliciesPermissionDetailsResponse(r);
|
||||||
|
return await this.createServiceAccountGrantedPoliciesView(result, organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
async createProjectAccessPolicies(
|
async createProjectAccessPolicies(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
|
@ -206,7 +197,6 @@ export class AccessPolicyService {
|
||||||
async deleteAccessPolicy(accessPolicyId: string): Promise<void> {
|
async deleteAccessPolicy(accessPolicyId: string): Promise<void> {
|
||||||
await this.apiService.send("DELETE", "/access-policies/" + accessPolicyId, null, true, false);
|
await this.apiService.send("DELETE", "/access-policies/" + accessPolicyId, null, true, false);
|
||||||
this._projectAccessPolicyChanges$.next(null);
|
this._projectAccessPolicyChanges$.next(null);
|
||||||
this._serviceAccountGrantedPolicyChanges$.next(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAccessPolicy(baseAccessPolicyView: BaseAccessPolicyView): Promise<void> {
|
async updateAccessPolicy(baseAccessPolicyView: BaseAccessPolicyView): Promise<void> {
|
||||||
|
@ -222,6 +212,158 @@ export class AccessPolicyService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getPeoplePotentialGrantees(organizationId: string) {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/organizations/" + organizationId + "/access-policies/people/potential-grantees",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
const results = new ListResponse(r, PotentialGranteeResponse);
|
||||||
|
return await this.createPotentialGranteeViews(organizationId, results.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getServiceAccountsPotentialGrantees(organizationId: string) {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/organizations/" + organizationId + "/access-policies/service-accounts/potential-grantees",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
const results = new ListResponse(r, PotentialGranteeResponse);
|
||||||
|
return await this.createPotentialGranteeViews(organizationId, results.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProjectsPotentialGrantees(organizationId: string) {
|
||||||
|
const r = await this.apiService.send(
|
||||||
|
"GET",
|
||||||
|
"/organizations/" + organizationId + "/access-policies/projects/potential-grantees",
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
const results = new ListResponse(r, PotentialGranteeResponse);
|
||||||
|
return await this.createPotentialGranteeViews(organizationId, results.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async getOrganizationKey(organizationId: string): Promise<SymmetricCryptoKey> {
|
||||||
|
return await this.cryptoService.getOrgKey(organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getAccessPolicyRequest(
|
||||||
|
granteeId: string,
|
||||||
|
view:
|
||||||
|
| UserProjectAccessPolicyView
|
||||||
|
| UserServiceAccountAccessPolicyView
|
||||||
|
| GroupProjectAccessPolicyView
|
||||||
|
| GroupServiceAccountAccessPolicyView
|
||||||
|
| ServiceAccountProjectAccessPolicyView,
|
||||||
|
) {
|
||||||
|
const request = new AccessPolicyRequest();
|
||||||
|
request.granteeId = granteeId;
|
||||||
|
request.read = view.read;
|
||||||
|
request.write = view.write;
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createBaseAccessPolicyView(
|
||||||
|
response:
|
||||||
|
| UserProjectAccessPolicyResponse
|
||||||
|
| UserServiceAccountAccessPolicyResponse
|
||||||
|
| GroupProjectAccessPolicyResponse
|
||||||
|
| GroupServiceAccountAccessPolicyResponse
|
||||||
|
| ServiceAccountProjectAccessPolicyResponse,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
id: response.id,
|
||||||
|
read: response.read,
|
||||||
|
write: response.write,
|
||||||
|
creationDate: response.creationDate,
|
||||||
|
revisionDate: response.revisionDate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createPotentialGranteeViews(
|
||||||
|
organizationId: string,
|
||||||
|
results: PotentialGranteeResponse[],
|
||||||
|
): Promise<PotentialGranteeView[]> {
|
||||||
|
const orgKey = await this.getOrganizationKey(organizationId);
|
||||||
|
return await Promise.all(
|
||||||
|
results.map(async (r) => {
|
||||||
|
const view = new PotentialGranteeView();
|
||||||
|
view.id = r.id;
|
||||||
|
view.type = r.type;
|
||||||
|
view.email = r.email;
|
||||||
|
view.currentUser = r.currentUser;
|
||||||
|
view.currentUserInGroup = r.currentUserInGroup;
|
||||||
|
|
||||||
|
if (r.type === "serviceAccount" || r.type === "project") {
|
||||||
|
view.name = r.name
|
||||||
|
? await this.encryptService.decryptToUtf8(new EncString(r.name), orgKey)
|
||||||
|
: null;
|
||||||
|
} else {
|
||||||
|
view.name = r.name;
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getServiceAccountGrantedPoliciesRequest(
|
||||||
|
policies: ServiceAccountGrantedPoliciesView,
|
||||||
|
): ServiceAccountGrantedPoliciesRequest {
|
||||||
|
const request = new ServiceAccountGrantedPoliciesRequest();
|
||||||
|
|
||||||
|
request.projectGrantedPolicyRequests = policies.grantedProjectPolicies.map((detailView) => ({
|
||||||
|
grantedId: detailView.accessPolicy.grantedProjectId,
|
||||||
|
read: detailView.accessPolicy.read,
|
||||||
|
write: detailView.accessPolicy.write,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createServiceAccountGrantedPoliciesView(
|
||||||
|
response: ServiceAccountGrantedPoliciesPermissionDetailsResponse,
|
||||||
|
organizationId: string,
|
||||||
|
): Promise<ServiceAccountGrantedPoliciesView> {
|
||||||
|
const orgKey = await this.getOrganizationKey(organizationId);
|
||||||
|
|
||||||
|
const view = new ServiceAccountGrantedPoliciesView();
|
||||||
|
view.grantedProjectPolicies =
|
||||||
|
await this.createServiceAccountProjectPolicyPermissionDetailsViews(
|
||||||
|
orgKey,
|
||||||
|
response.grantedProjectPolicies,
|
||||||
|
);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createServiceAccountProjectPolicyPermissionDetailsViews(
|
||||||
|
orgKey: SymmetricCryptoKey,
|
||||||
|
responses: ServiceAccountProjectPolicyPermissionDetailsResponse[],
|
||||||
|
): Promise<ServiceAccountProjectPolicyPermissionDetailsView[]> {
|
||||||
|
return await Promise.all(
|
||||||
|
responses.map(async (response) => {
|
||||||
|
return await this.createServiceAccountProjectPolicyPermissionDetailsView(orgKey, response);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createServiceAccountProjectPolicyPermissionDetailsView(
|
||||||
|
orgKey: SymmetricCryptoKey,
|
||||||
|
response: ServiceAccountProjectPolicyPermissionDetailsResponse,
|
||||||
|
): Promise<ServiceAccountProjectPolicyPermissionDetailsView> {
|
||||||
|
const view = new ServiceAccountProjectPolicyPermissionDetailsView();
|
||||||
|
view.hasPermission = response.hasPermission;
|
||||||
|
view.accessPolicy = await this.createServiceAccountProjectAccessPolicyView(
|
||||||
|
orgKey,
|
||||||
|
response.accessPolicy,
|
||||||
|
);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
private async createProjectAccessPoliciesView(
|
private async createProjectAccessPoliciesView(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
projectAccessPoliciesResponse: ProjectAccessPoliciesResponse,
|
projectAccessPoliciesResponse: ProjectAccessPoliciesResponse,
|
||||||
|
@ -393,147 +535,4 @@ export class AccessPolicyService {
|
||||||
currentUserInGroup: response.currentUserInGroup,
|
currentUserInGroup: response.currentUserInGroup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPeoplePotentialGrantees(organizationId: string) {
|
|
||||||
const r = await this.apiService.send(
|
|
||||||
"GET",
|
|
||||||
"/organizations/" + organizationId + "/access-policies/people/potential-grantees",
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
const results = new ListResponse(r, PotentialGranteeResponse);
|
|
||||||
return await this.createPotentialGranteeViews(organizationId, results.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getServiceAccountsPotentialGrantees(organizationId: string) {
|
|
||||||
const r = await this.apiService.send(
|
|
||||||
"GET",
|
|
||||||
"/organizations/" + organizationId + "/access-policies/service-accounts/potential-grantees",
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
const results = new ListResponse(r, PotentialGranteeResponse);
|
|
||||||
return await this.createPotentialGranteeViews(organizationId, results.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getProjectsPotentialGrantees(organizationId: string) {
|
|
||||||
const r = await this.apiService.send(
|
|
||||||
"GET",
|
|
||||||
"/organizations/" + organizationId + "/access-policies/projects/potential-grantees",
|
|
||||||
null,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
const results = new ListResponse(r, PotentialGranteeResponse);
|
|
||||||
return await this.createPotentialGranteeViews(organizationId, results.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async getOrganizationKey(organizationId: string): Promise<SymmetricCryptoKey> {
|
|
||||||
return await this.cryptoService.getOrgKey(organizationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getAccessPolicyRequest(
|
|
||||||
granteeId: string,
|
|
||||||
view:
|
|
||||||
| UserProjectAccessPolicyView
|
|
||||||
| UserServiceAccountAccessPolicyView
|
|
||||||
| GroupProjectAccessPolicyView
|
|
||||||
| GroupServiceAccountAccessPolicyView
|
|
||||||
| ServiceAccountProjectAccessPolicyView,
|
|
||||||
) {
|
|
||||||
const request = new AccessPolicyRequest();
|
|
||||||
request.granteeId = granteeId;
|
|
||||||
request.read = view.read;
|
|
||||||
request.write = view.write;
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createBaseAccessPolicyView(
|
|
||||||
response:
|
|
||||||
| UserProjectAccessPolicyResponse
|
|
||||||
| UserServiceAccountAccessPolicyResponse
|
|
||||||
| GroupProjectAccessPolicyResponse
|
|
||||||
| GroupServiceAccountAccessPolicyResponse
|
|
||||||
| ServiceAccountProjectAccessPolicyResponse,
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
id: response.id,
|
|
||||||
read: response.read,
|
|
||||||
write: response.write,
|
|
||||||
creationDate: response.creationDate,
|
|
||||||
revisionDate: response.revisionDate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createPotentialGranteeViews(
|
|
||||||
organizationId: string,
|
|
||||||
results: PotentialGranteeResponse[],
|
|
||||||
): Promise<PotentialGranteeView[]> {
|
|
||||||
const orgKey = await this.getOrganizationKey(organizationId);
|
|
||||||
return await Promise.all(
|
|
||||||
results.map(async (r) => {
|
|
||||||
const view = new PotentialGranteeView();
|
|
||||||
view.id = r.id;
|
|
||||||
view.type = r.type;
|
|
||||||
view.email = r.email;
|
|
||||||
view.currentUser = r.currentUser;
|
|
||||||
view.currentUserInGroup = r.currentUserInGroup;
|
|
||||||
|
|
||||||
if (r.type === "serviceAccount" || r.type === "project") {
|
|
||||||
view.name = r.name
|
|
||||||
? await this.encryptService.decryptToUtf8(new EncString(r.name), orgKey)
|
|
||||||
: null;
|
|
||||||
} else {
|
|
||||||
view.name = r.name;
|
|
||||||
}
|
|
||||||
return view;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getGrantedPoliciesCreateRequest(
|
|
||||||
policies: ServiceAccountProjectAccessPolicyView[],
|
|
||||||
): GrantedPolicyRequest[] {
|
|
||||||
return policies.map((ap) => {
|
|
||||||
const request = new GrantedPolicyRequest();
|
|
||||||
request.grantedId = ap.grantedProjectId;
|
|
||||||
request.read = ap.read;
|
|
||||||
request.write = ap.write;
|
|
||||||
return request;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createServiceAccountProjectAccessPolicyViews(
|
|
||||||
responses: ServiceAccountProjectAccessPolicyResponse[],
|
|
||||||
organizationId: string,
|
|
||||||
): Promise<ServiceAccountProjectAccessPolicyView[]> {
|
|
||||||
const orgKey = await this.getOrganizationKey(organizationId);
|
|
||||||
return await Promise.all(
|
|
||||||
responses.map(async (response: ServiceAccountProjectAccessPolicyResponse) => {
|
|
||||||
const view = new ServiceAccountProjectAccessPolicyView();
|
|
||||||
view.id = response.id;
|
|
||||||
view.read = response.read;
|
|
||||||
view.write = response.write;
|
|
||||||
view.creationDate = response.creationDate;
|
|
||||||
view.revisionDate = response.revisionDate;
|
|
||||||
view.serviceAccountId = response.serviceAccountId;
|
|
||||||
view.grantedProjectId = response.grantedProjectId;
|
|
||||||
view.serviceAccountName = response.serviceAccountName
|
|
||||||
? await this.encryptService.decryptToUtf8(
|
|
||||||
new EncString(response.serviceAccountName),
|
|
||||||
orgKey,
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
view.grantedProjectName = response.grantedProjectName
|
|
||||||
? await this.encryptService.decryptToUtf8(
|
|
||||||
new EncString(response.grantedProjectName),
|
|
||||||
orgKey,
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
return view;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { GrantedPolicyRequest } from "./granted-policy.request";
|
||||||
|
|
||||||
|
export class ServiceAccountGrantedPoliciesRequest {
|
||||||
|
projectGrantedPolicyRequests?: GrantedPolicyRequest[];
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
|
import { ServiceAccountProjectPolicyPermissionDetailsResponse } from "./service-account-project-policy-permission-details.response";
|
||||||
|
|
||||||
|
export class ServiceAccountGrantedPoliciesPermissionDetailsResponse extends BaseResponse {
|
||||||
|
grantedProjectPolicies: ServiceAccountProjectPolicyPermissionDetailsResponse[];
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
const grantedProjectPolicies = this.getResponseProperty("GrantedProjectPolicies");
|
||||||
|
this.grantedProjectPolicies = grantedProjectPolicies.map(
|
||||||
|
(k: any) => new ServiceAccountProjectPolicyPermissionDetailsResponse(k),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { BaseResponse } from "@bitwarden/common/models/response/base.response";
|
||||||
|
|
||||||
|
import { ServiceAccountProjectAccessPolicyResponse } from "./access-policy.response";
|
||||||
|
|
||||||
|
export class ServiceAccountProjectPolicyPermissionDetailsResponse extends BaseResponse {
|
||||||
|
accessPolicy: ServiceAccountProjectAccessPolicyResponse;
|
||||||
|
hasPermission: boolean;
|
||||||
|
|
||||||
|
constructor(response: any) {
|
||||||
|
super(response);
|
||||||
|
this.accessPolicy = this.getResponseProperty("AccessPolicy");
|
||||||
|
this.hasPermission = this.getResponseProperty("HasPermission");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue