[SM-581] User access removal warnings (#4904)
* init refactor * Fix current user access checks * Add in warning dialogs that are aware of other APs * cleanup handlers; refresh sa list on removal * Code review updates * [SM-580] Add warning dialog for Service account People tab (#4893) * Add warning dialog from figma * move dialog out of access selector component; add after delete event; remove people-sa logic * remove commented code and unused service * Updates to work with SM-581 --------- Co-authored-by: William Martin <contact@willmartian.com> --------- Co-authored-by: William Martin <contact@willmartian.com>
This commit is contained in:
parent
f717c3d619
commit
c711312fee
|
@ -6570,6 +6570,27 @@
|
||||||
"secretsManagerEnable": {
|
"secretsManagerEnable": {
|
||||||
"message": "Enable Secrets Manager Beta"
|
"message": "Enable Secrets Manager Beta"
|
||||||
},
|
},
|
||||||
|
"saPeopleWarningTitle": {
|
||||||
|
"message": "Access tokens still available"
|
||||||
|
},
|
||||||
|
"saPeopleWarningMessage": {
|
||||||
|
"message": "Removing people from a service account does not remove the access tokens they created. For security best practice, it is recommended to revoke access tokens created by people removed from a service account."
|
||||||
|
},
|
||||||
|
"smAccessRemovalWarningProjectTitle": {
|
||||||
|
"message": "Remove access to this project"
|
||||||
|
},
|
||||||
|
"smAccessRemovalWarningProjectMessage": {
|
||||||
|
"message": "This action will remove your access to the project."
|
||||||
|
},
|
||||||
|
"smAccessRemovalWarningSaTitle": {
|
||||||
|
"message": "Remove access to this service account"
|
||||||
|
},
|
||||||
|
"smAccessRemovalWarningSaMessage": {
|
||||||
|
"message": "This action will remove your access to the service account."
|
||||||
|
},
|
||||||
|
"removeAccess": {
|
||||||
|
"message": "Remove access"
|
||||||
|
},
|
||||||
"checkForBreaches": {
|
"checkForBreaches": {
|
||||||
"message": "Check known data breaches for this password"
|
"message": "Check known data breaches for this password"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,24 +10,28 @@ export class UserProjectAccessPolicyView extends BaseAccessPolicyView {
|
||||||
organizationUserId: string;
|
organizationUserId: string;
|
||||||
organizationUserName: string;
|
organizationUserName: string;
|
||||||
grantedProjectId: string;
|
grantedProjectId: string;
|
||||||
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
export class UserServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
||||||
organizationUserId: string;
|
organizationUserId: string;
|
||||||
organizationUserName: string;
|
organizationUserName: string;
|
||||||
grantedServiceAccountId: string;
|
grantedServiceAccountId: string;
|
||||||
|
userId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GroupProjectAccessPolicyView extends BaseAccessPolicyView {
|
export class GroupProjectAccessPolicyView extends BaseAccessPolicyView {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
grantedProjectId: string;
|
grantedProjectId: string;
|
||||||
|
currentUserInGroup: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GroupServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
export class GroupServiceAccountAccessPolicyView extends BaseAccessPolicyView {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
grantedServiceAccountId: string;
|
grantedServiceAccountId: string;
|
||||||
|
currentUserInGroup: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ServiceAccountProjectAccessPolicyView extends BaseAccessPolicyView {
|
export class ServiceAccountProjectAccessPolicyView extends BaseAccessPolicyView {
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
[columnTitle]="'groupSlashUser' | i18n"
|
[columnTitle]="'groupSlashUser' | i18n"
|
||||||
[emptyMessage]="'projectEmptyPeopleAccessPolicies' | i18n"
|
[emptyMessage]="'projectEmptyPeopleAccessPolicies' | i18n"
|
||||||
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
||||||
|
(onDeleteAccessPolicy)="handleDeleteAccessPolicy($event)"
|
||||||
|
(onUpdateAccessPolicy)="handleUpdateAccessPolicy($event)"
|
||||||
>
|
>
|
||||||
</sm-access-selector>
|
</sm-access-selector>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
import { map, Observable, share, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { SelectItemView } from "@bitwarden/components";
|
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
||||||
|
import { DialogService, SelectItemView } from "@bitwarden/components";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GroupProjectAccessPolicyView,
|
GroupProjectAccessPolicyView,
|
||||||
|
@ -14,6 +15,10 @@ import {
|
||||||
AccessSelectorComponent,
|
AccessSelectorComponent,
|
||||||
AccessSelectorRowView,
|
AccessSelectorRowView,
|
||||||
} from "../../shared/access-policies/access-selector.component";
|
} from "../../shared/access-policies/access-selector.component";
|
||||||
|
import {
|
||||||
|
AccessRemovalDetails,
|
||||||
|
AccessRemovalDialogComponent,
|
||||||
|
} from "../../shared/access-policies/dialogs/access-removal-dialog.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "sm-project-people",
|
selector: "sm-project-people",
|
||||||
|
@ -23,6 +28,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private organizationId: string;
|
private organizationId: string;
|
||||||
private projectId: string;
|
private projectId: string;
|
||||||
|
private rows: AccessSelectorRowView[];
|
||||||
|
|
||||||
protected rows$: Observable<AccessSelectorRowView[]> =
|
protected rows$: Observable<AccessSelectorRowView[]> =
|
||||||
this.accessPolicyService.projectAccessPolicyChanges$.pipe(
|
this.accessPolicyService.projectAccessPolicyChanges$.pipe(
|
||||||
|
@ -40,6 +46,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
accessPolicyId: policy.id,
|
accessPolicyId: policy.id,
|
||||||
read: policy.read,
|
read: policy.read,
|
||||||
write: policy.write,
|
write: policy.write,
|
||||||
|
userId: policy.userId,
|
||||||
icon: AccessSelectorComponent.userIcon,
|
icon: AccessSelectorComponent.userIcon,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -52,11 +59,13 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
accessPolicyId: policy.id,
|
accessPolicyId: policy.id,
|
||||||
read: policy.read,
|
read: policy.read,
|
||||||
write: policy.write,
|
write: policy.write,
|
||||||
|
currentUserInGroup: policy.currentUserInGroup,
|
||||||
icon: AccessSelectorComponent.groupIcon,
|
icon: AccessSelectorComponent.groupIcon,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return rows;
|
return rows;
|
||||||
})
|
}),
|
||||||
|
share()
|
||||||
);
|
);
|
||||||
|
|
||||||
protected handleCreateAccessPolicies(selected: SelectItemView[]) {
|
protected handleCreateAccessPolicies(selected: SelectItemView[]) {
|
||||||
|
@ -90,17 +99,94 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private accessPolicyService: AccessPolicyService) {}
|
protected async handleDeleteAccessPolicy(policy: AccessSelectorRowView) {
|
||||||
|
if (
|
||||||
|
await this.accessPolicyService.needToShowAccessRemovalWarning(
|
||||||
|
this.organizationId,
|
||||||
|
policy,
|
||||||
|
this.rows
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.launchDeleteWarningDialog(policy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.accessPolicyService.deleteAccessPolicy(policy.accessPolicyId);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleUpdateAccessPolicy(policy: AccessSelectorRowView) {
|
||||||
|
if (
|
||||||
|
policy.read === true &&
|
||||||
|
policy.write === false &&
|
||||||
|
(await this.accessPolicyService.needToShowAccessRemovalWarning(
|
||||||
|
this.organizationId,
|
||||||
|
policy,
|
||||||
|
this.rows
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
this.launchUpdateWarningDialog(policy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this.accessPolicyService.updateAccessPolicy(
|
||||||
|
AccessSelectorComponent.getBaseAccessPolicyView(policy)
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private validationService: ValidationService,
|
||||||
|
private accessPolicyService: AccessPolicyService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
this.projectId = params.projectId;
|
this.projectId = params.projectId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.rows$.pipe(takeUntil(this.destroy$)).subscribe((rows) => {
|
||||||
|
this.rows = rows;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async launchDeleteWarningDialog(policy: AccessSelectorRowView) {
|
||||||
|
this.dialogService.open<unknown, AccessRemovalDetails>(AccessRemovalDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: "smAccessRemovalWarningProjectTitle",
|
||||||
|
message: "smAccessRemovalWarningProjectMessage",
|
||||||
|
operation: "delete",
|
||||||
|
type: "project",
|
||||||
|
returnRoute: ["sm", this.organizationId, "projects"],
|
||||||
|
policy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private launchUpdateWarningDialog(policy: AccessSelectorRowView) {
|
||||||
|
this.dialogService.open<unknown, AccessRemovalDetails>(AccessRemovalDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: "smAccessRemovalWarningProjectTitle",
|
||||||
|
message: "smAccessRemovalWarningProjectMessage",
|
||||||
|
operation: "update",
|
||||||
|
type: "project",
|
||||||
|
returnRoute: ["sm", this.organizationId, "projects"],
|
||||||
|
policy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[columnTitle]="'serviceAccounts' | i18n"
|
[columnTitle]="'serviceAccounts' | i18n"
|
||||||
[emptyMessage]="'projectEmptyServiceAccountAccessPolicies' | i18n"
|
[emptyMessage]="'projectEmptyServiceAccountAccessPolicies' | i18n"
|
||||||
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
||||||
|
(onDeleteAccessPolicy)="handleDeleteAccessPolicy($event)"
|
||||||
>
|
>
|
||||||
</sm-access-selector>
|
</sm-access-selector>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
import { map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
|
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
||||||
import { SelectItemView } from "@bitwarden/components";
|
import { SelectItemView } from "@bitwarden/components";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -65,7 +66,19 @@ export class ProjectServiceAccountsComponent implements OnInit, OnDestroy {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private accessPolicyService: AccessPolicyService) {}
|
protected async handleDeleteAccessPolicy(policy: AccessSelectorRowView) {
|
||||||
|
try {
|
||||||
|
await this.accessPolicyService.deleteAccessPolicy(policy.accessPolicyId);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private validationService: ValidationService,
|
||||||
|
private accessPolicyService: AccessPolicyService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatestWith, Observable, startWith, switchMap } from "rxjs";
|
import { combineLatest, Observable, startWith, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { ProjectListView } from "../../models/view/project-list.view";
|
import { ProjectListView } from "../../models/view/project-list.view";
|
||||||
|
import { AccessPolicyService } from "../../shared/access-policies/access-policy.service";
|
||||||
import {
|
import {
|
||||||
ProjectDeleteDialogComponent,
|
ProjectDeleteDialogComponent,
|
||||||
ProjectDeleteOperation,
|
ProjectDeleteOperation,
|
||||||
|
@ -29,14 +30,17 @@ export class ProjectsComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
|
private accessPolicyService: AccessPolicyService,
|
||||||
private dialogService: DialogService
|
private dialogService: DialogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.projects$ = this.projectService.project$.pipe(
|
this.projects$ = combineLatest([
|
||||||
startWith(null),
|
this.route.params,
|
||||||
combineLatestWith(this.route.params),
|
this.projectService.project$.pipe(startWith(null)),
|
||||||
switchMap(async ([_, params]) => {
|
this.accessPolicyService.projectAccessPolicyChanges$.pipe(startWith(null)),
|
||||||
|
]).pipe(
|
||||||
|
switchMap(async ([params]) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
return await this.getProjects();
|
return await this.getProjects();
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[columnTitle]="'groupSlashUser' | i18n"
|
[columnTitle]="'groupSlashUser' | i18n"
|
||||||
[emptyMessage]="'projectEmptyPeopleAccessPolicies' | i18n"
|
[emptyMessage]="'projectEmptyPeopleAccessPolicies' | i18n"
|
||||||
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
||||||
|
(onDeleteAccessPolicy)="handleDeleteAccessPolicy($event)"
|
||||||
>
|
>
|
||||||
</sm-access-selector>
|
</sm-access-selector>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,19 @@
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatestWith, map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
import {
|
||||||
|
combineLatestWith,
|
||||||
|
map,
|
||||||
|
Observable,
|
||||||
|
share,
|
||||||
|
startWith,
|
||||||
|
Subject,
|
||||||
|
switchMap,
|
||||||
|
takeUntil,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
|
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
||||||
|
import { DialogService, SimpleDialogOptions, SimpleDialogType } from "@bitwarden/components";
|
||||||
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -14,6 +26,10 @@ import {
|
||||||
AccessSelectorComponent,
|
AccessSelectorComponent,
|
||||||
AccessSelectorRowView,
|
AccessSelectorRowView,
|
||||||
} from "../../shared/access-policies/access-selector.component";
|
} from "../../shared/access-policies/access-selector.component";
|
||||||
|
import {
|
||||||
|
AccessRemovalDetails,
|
||||||
|
AccessRemovalDialogComponent,
|
||||||
|
} from "../../shared/access-policies/dialogs/access-removal-dialog.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "sm-service-account-people",
|
selector: "sm-service-account-people",
|
||||||
|
@ -22,6 +38,8 @@ import {
|
||||||
export class ServiceAccountPeopleComponent {
|
export class ServiceAccountPeopleComponent {
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private serviceAccountId: string;
|
private serviceAccountId: string;
|
||||||
|
private organizationId: string;
|
||||||
|
private rows: AccessSelectorRowView[];
|
||||||
|
|
||||||
protected rows$: Observable<AccessSelectorRowView[]> =
|
protected rows$: Observable<AccessSelectorRowView[]> =
|
||||||
this.accessPolicyService.serviceAccountAccessPolicyChanges$.pipe(
|
this.accessPolicyService.serviceAccountAccessPolicyChanges$.pipe(
|
||||||
|
@ -40,6 +58,7 @@ export class ServiceAccountPeopleComponent {
|
||||||
accessPolicyId: policy.id,
|
accessPolicyId: policy.id,
|
||||||
read: policy.read,
|
read: policy.read,
|
||||||
write: policy.write,
|
write: policy.write,
|
||||||
|
userId: policy.userId,
|
||||||
icon: AccessSelectorComponent.userIcon,
|
icon: AccessSelectorComponent.userIcon,
|
||||||
static: true,
|
static: true,
|
||||||
});
|
});
|
||||||
|
@ -53,13 +72,15 @@ export class ServiceAccountPeopleComponent {
|
||||||
accessPolicyId: policy.id,
|
accessPolicyId: policy.id,
|
||||||
read: policy.read,
|
read: policy.read,
|
||||||
write: policy.write,
|
write: policy.write,
|
||||||
|
currentUserInGroup: policy.currentUserInGroup,
|
||||||
icon: AccessSelectorComponent.groupIcon,
|
icon: AccessSelectorComponent.groupIcon,
|
||||||
static: true,
|
static: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
})
|
}),
|
||||||
|
share()
|
||||||
);
|
);
|
||||||
|
|
||||||
protected handleCreateAccessPolicies(selected: SelectItemView[]) {
|
protected handleCreateAccessPolicies(selected: SelectItemView[]) {
|
||||||
|
@ -92,11 +113,49 @@ export class ServiceAccountPeopleComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private accessPolicyService: AccessPolicyService) {}
|
protected async handleDeleteAccessPolicy(policy: AccessSelectorRowView) {
|
||||||
|
if (
|
||||||
|
await this.accessPolicyService.needToShowAccessRemovalWarning(
|
||||||
|
this.organizationId,
|
||||||
|
policy,
|
||||||
|
this.rows
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.launchDeleteWarningDialog(policy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.accessPolicyService.deleteAccessPolicy(policy.accessPolicyId);
|
||||||
|
const simpleDialogOpts: SimpleDialogOptions = {
|
||||||
|
title: this.i18nService.t("saPeopleWarningTitle"),
|
||||||
|
content: this.i18nService.t("saPeopleWarningMessage"),
|
||||||
|
type: SimpleDialogType.WARNING,
|
||||||
|
acceptButtonText: this.i18nService.t("close"),
|
||||||
|
cancelButtonText: null,
|
||||||
|
};
|
||||||
|
this.dialogService.openSimpleDialog(simpleDialogOpts);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private validationService: ValidationService,
|
||||||
|
private accessPolicyService: AccessPolicyService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
||||||
this.serviceAccountId = params.serviceAccountId;
|
this.serviceAccountId = params.serviceAccountId;
|
||||||
|
this.organizationId = params.organizationId;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rows$.pipe(takeUntil(this.destroy$)).subscribe((rows) => {
|
||||||
|
this.rows = rows;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,4 +163,17 @@ export class ServiceAccountPeopleComponent {
|
||||||
this.destroy$.next();
|
this.destroy$.next();
|
||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private launchDeleteWarningDialog(policy: AccessSelectorRowView) {
|
||||||
|
this.dialogService.open<unknown, AccessRemovalDetails>(AccessRemovalDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: "smAccessRemovalWarningSaTitle",
|
||||||
|
message: "smAccessRemovalWarningSaMessage",
|
||||||
|
operation: "delete",
|
||||||
|
type: "service-account",
|
||||||
|
returnRoute: ["sm", this.organizationId, "service-accounts"],
|
||||||
|
policy,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[columnTitle]="'projects' | i18n"
|
[columnTitle]="'projects' | i18n"
|
||||||
[emptyMessage]="'serviceAccountEmptyProjectAccesspolicies' | i18n"
|
[emptyMessage]="'serviceAccountEmptyProjectAccesspolicies' | i18n"
|
||||||
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
(onCreateAccessPolicies)="handleCreateAccessPolicies($event)"
|
||||||
|
(onDeleteAccessPolicy)="handleDeleteAccessPolicy($event)"
|
||||||
>
|
>
|
||||||
</sm-access-selector>
|
</sm-access-selector>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Component } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatestWith, map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
import { combineLatestWith, map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
|
|
||||||
|
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
||||||
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
||||||
|
|
||||||
import { ServiceAccountProjectAccessPolicyView } from "../../models/view/access-policy.view";
|
import { ServiceAccountProjectAccessPolicyView } from "../../models/view/access-policy.view";
|
||||||
|
@ -62,7 +63,19 @@ export class ServiceAccountProjectsComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private accessPolicyService: AccessPolicyService) {}
|
protected async handleDeleteAccessPolicy(policy: AccessSelectorRowView) {
|
||||||
|
try {
|
||||||
|
await this.accessPolicyService.deleteAccessPolicy(policy.accessPolicyId);
|
||||||
|
} catch (e) {
|
||||||
|
this.validationService.showError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private validationService: ValidationService,
|
||||||
|
private accessPolicyService: AccessPolicyService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { combineLatestWith, Observable, startWith, switchMap } from "rxjs";
|
import { combineLatest, Observable, startWith, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { ServiceAccountView } from "../models/view/service-account.view";
|
import { ServiceAccountView } from "../models/view/service-account.view";
|
||||||
|
import { AccessPolicyService } from "../shared/access-policies/access-policy.service";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ServiceAccountDialogComponent,
|
ServiceAccountDialogComponent,
|
||||||
|
@ -24,14 +25,17 @@ export class ServiceAccountsComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
|
private accessPolicyService: AccessPolicyService,
|
||||||
private serviceAccountService: ServiceAccountService
|
private serviceAccountService: ServiceAccountService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.serviceAccounts$ = this.serviceAccountService.serviceAccount$.pipe(
|
this.serviceAccounts$ = combineLatest([
|
||||||
startWith(null),
|
this.route.params,
|
||||||
combineLatestWith(this.route.params),
|
this.serviceAccountService.serviceAccount$.pipe(startWith(null)),
|
||||||
switchMap(async ([_, params]) => {
|
this.accessPolicyService.serviceAccountAccessPolicyChanges$.pipe(startWith(null)),
|
||||||
|
]).pipe(
|
||||||
|
switchMap(async ([params]) => {
|
||||||
this.organizationId = params.organizationId;
|
this.organizationId = params.organizationId;
|
||||||
return await this.getServiceAccounts();
|
return await this.getServiceAccounts();
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Subject } from "rxjs";
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||||
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||||
import { EncString } from "@bitwarden/common/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/models/domain/enc-string";
|
||||||
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key";
|
||||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
|
@ -23,6 +24,7 @@ import { AccessPoliciesCreateRequest } from "../../shared/access-policies/models
|
||||||
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 { ServiceAccountAccessPoliciesResponse } from "../../shared/access-policies/models/responses/service-accounts-access-policies.response";
|
import { ServiceAccountAccessPoliciesResponse } from "../../shared/access-policies/models/responses/service-accounts-access-policies.response";
|
||||||
|
|
||||||
|
import { AccessSelectorRowView } from "./access-selector.component";
|
||||||
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 { GrantedPolicyRequest } from "./models/requests/granted-policy.request";
|
||||||
|
@ -64,10 +66,19 @@ export class AccessPolicyService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
|
private organizationService: OrganizationService,
|
||||||
protected apiService: ApiService,
|
protected apiService: ApiService,
|
||||||
protected encryptService: EncryptService
|
protected encryptService: EncryptService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
refreshProjectAccessPolicyChanges() {
|
||||||
|
this._projectAccessPolicyChanges$.next(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshServiceAccountAccessPolicyChanges() {
|
||||||
|
this._serviceAccountAccessPolicyChanges$.next(null);
|
||||||
|
}
|
||||||
|
|
||||||
async getGrantedPolicies(
|
async getGrantedPolicies(
|
||||||
serviceAccountId: string,
|
serviceAccountId: string,
|
||||||
organizationId: string
|
organizationId: string
|
||||||
|
@ -196,6 +207,36 @@ export class AccessPolicyService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async needToShowAccessRemovalWarning(
|
||||||
|
organizationId: string,
|
||||||
|
policy: AccessSelectorRowView,
|
||||||
|
currentPolicies: AccessSelectorRowView[]
|
||||||
|
): Promise<boolean> {
|
||||||
|
const organization = this.organizationService.get(organizationId);
|
||||||
|
if (organization.isOwner || organization.isAdmin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const currentUserId = organization.userId;
|
||||||
|
const readWriteGroupPolicies = currentPolicies
|
||||||
|
.filter((x) => x.accessPolicyId != policy.accessPolicyId)
|
||||||
|
.filter((x) => x.currentUserInGroup && x.read && x.write).length;
|
||||||
|
const readWriteUserPolicies = currentPolicies
|
||||||
|
.filter((x) => x.accessPolicyId != policy.accessPolicyId)
|
||||||
|
.filter((x) => x.userId == currentUserId && x.read && x.write).length;
|
||||||
|
|
||||||
|
if (policy.type === "user" && policy.userId == currentUserId && readWriteGroupPolicies == 0) {
|
||||||
|
return true;
|
||||||
|
} else if (
|
||||||
|
policy.type === "group" &&
|
||||||
|
policy.currentUserInGroup &&
|
||||||
|
readWriteUserPolicies == 0 &&
|
||||||
|
readWriteGroupPolicies == 0
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private async createProjectAccessPoliciesView(
|
private async createProjectAccessPoliciesView(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
projectAccessPoliciesResponse: ProjectAccessPoliciesResponse
|
projectAccessPoliciesResponse: ProjectAccessPoliciesResponse
|
||||||
|
@ -255,6 +296,7 @@ export class AccessPolicyService {
|
||||||
grantedProjectId: response.grantedProjectId,
|
grantedProjectId: response.grantedProjectId,
|
||||||
organizationUserId: response.organizationUserId,
|
organizationUserId: response.organizationUserId,
|
||||||
organizationUserName: response.organizationUserName,
|
organizationUserName: response.organizationUserName,
|
||||||
|
userId: response.userId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +308,7 @@ export class AccessPolicyService {
|
||||||
grantedProjectId: response.grantedProjectId,
|
grantedProjectId: response.grantedProjectId,
|
||||||
groupId: response.groupId,
|
groupId: response.groupId,
|
||||||
groupName: response.groupName,
|
groupName: response.groupName,
|
||||||
|
currentUserInGroup: response.currentUserInGroup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +378,7 @@ export class AccessPolicyService {
|
||||||
grantedServiceAccountId: response.grantedServiceAccountId,
|
grantedServiceAccountId: response.grantedServiceAccountId,
|
||||||
organizationUserId: response.organizationUserId,
|
organizationUserId: response.organizationUserId,
|
||||||
organizationUserName: response.organizationUserName,
|
organizationUserName: response.organizationUserName,
|
||||||
|
userId: response.userId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,6 +390,7 @@ export class AccessPolicyService {
|
||||||
grantedServiceAccountId: response.grantedServiceAccountId,
|
grantedServiceAccountId: response.grantedServiceAccountId,
|
||||||
groupId: response.groupId,
|
groupId: response.groupId,
|
||||||
groupName: response.groupName,
|
groupName: response.groupName,
|
||||||
|
currentUserInGroup: response.currentUserInGroup,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,14 +516,18 @@ export class AccessPolicyService {
|
||||||
view.revisionDate = response.revisionDate;
|
view.revisionDate = response.revisionDate;
|
||||||
view.serviceAccountId = response.serviceAccountId;
|
view.serviceAccountId = response.serviceAccountId;
|
||||||
view.grantedProjectId = response.grantedProjectId;
|
view.grantedProjectId = response.grantedProjectId;
|
||||||
view.serviceAccountName = await this.encryptService.decryptToUtf8(
|
view.serviceAccountName = response.serviceAccountName
|
||||||
new EncString(response.serviceAccountName),
|
? await this.encryptService.decryptToUtf8(
|
||||||
orgKey
|
new EncString(response.serviceAccountName),
|
||||||
);
|
orgKey
|
||||||
view.grantedProjectName = await this.encryptService.decryptToUtf8(
|
)
|
||||||
new EncString(response.grantedProjectName),
|
: null;
|
||||||
orgKey
|
view.grantedProjectName = response.grantedProjectName
|
||||||
);
|
? await this.encryptService.decryptToUtf8(
|
||||||
|
new EncString(response.grantedProjectName),
|
||||||
|
orgKey
|
||||||
|
)
|
||||||
|
: null;
|
||||||
return view;
|
return view;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -35,11 +35,7 @@
|
||||||
*ngIf="!row.static; else staticPermissions"
|
*ngIf="!row.static; else staticPermissions"
|
||||||
class="tw-mb-auto tw-inline-block tw-w-auto"
|
class="tw-mb-auto tw-inline-block tw-w-auto"
|
||||||
>
|
>
|
||||||
<select
|
<select bitInput (change)="update($event.target, row)" [disabled]="row.static">
|
||||||
bitInput
|
|
||||||
(change)="update($event.target, row.accessPolicyId)"
|
|
||||||
[disabled]="row.static"
|
|
||||||
>
|
|
||||||
<option value="canRead" [selected]="row.read && row.write != true">
|
<option value="canRead" [selected]="row.read && row.write != true">
|
||||||
{{ "canRead" | i18n }}
|
{{ "canRead" | i18n }}
|
||||||
</option>
|
</option>
|
||||||
|
@ -62,7 +58,7 @@
|
||||||
size="default"
|
size="default"
|
||||||
[attr.title]="'remove' | i18n"
|
[attr.title]="'remove' | i18n"
|
||||||
[attr.aria-label]="'remove' | i18n"
|
[attr.aria-label]="'remove' | i18n"
|
||||||
[bitAction]="delete(row.accessPolicyId)"
|
[bitAction]="delete(row)"
|
||||||
></button>
|
></button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {
|
||||||
tap,
|
tap,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
|
|
||||||
import { ValidationService } from "@bitwarden/common/abstractions/validation.service";
|
|
||||||
import { Utils } from "@bitwarden/common/misc/utils";
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view";
|
||||||
|
|
||||||
|
@ -28,6 +27,8 @@ export type AccessSelectorRowView = {
|
||||||
read: boolean;
|
read: boolean;
|
||||||
write: boolean;
|
write: boolean;
|
||||||
icon: string;
|
icon: string;
|
||||||
|
userId?: string;
|
||||||
|
currentUserInGroup?: boolean;
|
||||||
static?: boolean;
|
static?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,7 +42,12 @@ export class AccessSelectorComponent implements OnInit {
|
||||||
static readonly serviceAccountIcon = "bwi-wrench";
|
static readonly serviceAccountIcon = "bwi-wrench";
|
||||||
static readonly projectIcon = "bwi-collection";
|
static readonly projectIcon = "bwi-collection";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits the selected itemss on submit.
|
||||||
|
*/
|
||||||
@Output() onCreateAccessPolicies = new EventEmitter<SelectItemView[]>();
|
@Output() onCreateAccessPolicies = new EventEmitter<SelectItemView[]>();
|
||||||
|
@Output() onDeleteAccessPolicy = new EventEmitter<AccessSelectorRowView>();
|
||||||
|
@Output() onUpdateAccessPolicy = new EventEmitter<AccessSelectorRowView>();
|
||||||
|
|
||||||
@Input() label: string;
|
@Input() label: string;
|
||||||
@Input() hint: string;
|
@Input() hint: string;
|
||||||
|
@ -105,11 +111,7 @@ export class AccessSelectorComponent implements OnInit {
|
||||||
share()
|
share()
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(private accessPolicyService: AccessPolicyService, private route: ActivatedRoute) {}
|
||||||
private accessPolicyService: AccessPolicyService,
|
|
||||||
private validationService: ValidationService,
|
|
||||||
private route: ActivatedRoute
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.formGroup.disable();
|
this.formGroup.disable();
|
||||||
|
@ -128,28 +130,21 @@ export class AccessSelectorComponent implements OnInit {
|
||||||
return firstValueFrom(this.selectItems$);
|
return firstValueFrom(this.selectItems$);
|
||||||
};
|
};
|
||||||
|
|
||||||
async update(target: any, accessPolicyId: string): Promise<void> {
|
async update(target: any, row: AccessSelectorRowView): Promise<void> {
|
||||||
try {
|
if (target.value === "canRead") {
|
||||||
const accessPolicyView = new BaseAccessPolicyView();
|
row.read = true;
|
||||||
accessPolicyView.id = accessPolicyId;
|
row.write = false;
|
||||||
if (target.value === "canRead") {
|
} else if (target.value === "canReadWrite") {
|
||||||
accessPolicyView.read = true;
|
row.read = true;
|
||||||
accessPolicyView.write = false;
|
row.write = true;
|
||||||
} else if (target.value === "canReadWrite") {
|
|
||||||
accessPolicyView.read = true;
|
|
||||||
accessPolicyView.write = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.accessPolicyService.updateAccessPolicy(accessPolicyView);
|
|
||||||
} catch (e) {
|
|
||||||
this.validationService.showError(e);
|
|
||||||
}
|
}
|
||||||
|
this.onUpdateAccessPolicy.emit(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete = (accessPolicyId: string) => async () => {
|
delete = (row: AccessSelectorRowView) => async () => {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.formGroup.disable();
|
this.formGroup.disable();
|
||||||
await this.accessPolicyService.deleteAccessPolicy(accessPolicyId);
|
this.onDeleteAccessPolicy.emit(row);
|
||||||
return firstValueFrom(this.selectItems$);
|
return firstValueFrom(this.selectItems$);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,4 +171,12 @@ export class AccessSelectorComponent implements OnInit {
|
||||||
return "project";
|
return "project";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getBaseAccessPolicyView(row: AccessSelectorRowView) {
|
||||||
|
const view = new BaseAccessPolicyView();
|
||||||
|
view.id = row.accessPolicyId;
|
||||||
|
view.read = row.read;
|
||||||
|
view.write = row.write;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<bit-simple-dialog>
|
||||||
|
<span bitDialogTitle>{{ data.title | i18n }}</span>
|
||||||
|
<span bitDialogContent>
|
||||||
|
{{ data.message | i18n }}
|
||||||
|
</span>
|
||||||
|
<div bitDialogFooter class="tw-flex tw-flex-row tw-gap-2">
|
||||||
|
<button type="button" bitButton buttonType="danger" [bitAction]="removeAccess">
|
||||||
|
{{ "removeAccess" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button type="button" bitButton buttonType="secondary" [bitAction]="cancel">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</bit-simple-dialog>
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
|
||||||
|
import { Component, Inject, OnInit } from "@angular/core";
|
||||||
|
import { Router } from "@angular/router";
|
||||||
|
|
||||||
|
import { AccessPolicyService } from "../access-policy.service";
|
||||||
|
import { AccessSelectorComponent, AccessSelectorRowView } from "../access-selector.component";
|
||||||
|
|
||||||
|
export interface AccessRemovalDetails {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
operation: "update" | "delete";
|
||||||
|
type: "project" | "service-account";
|
||||||
|
returnRoute: string[];
|
||||||
|
policy: AccessSelectorRowView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "sm-access-removal-dialog",
|
||||||
|
templateUrl: "./access-removal-dialog.component.html",
|
||||||
|
})
|
||||||
|
export class AccessRemovalDialogComponent implements OnInit {
|
||||||
|
constructor(
|
||||||
|
public dialogRef: DialogRef,
|
||||||
|
private router: Router,
|
||||||
|
private accessPolicyService: AccessPolicyService,
|
||||||
|
@Inject(DIALOG_DATA) public data: AccessRemovalDetails
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// TODO remove null checks once strictNullChecks in TypeScript is turned on.
|
||||||
|
if (
|
||||||
|
!this.data.message ||
|
||||||
|
!this.data.title ||
|
||||||
|
!this.data.operation ||
|
||||||
|
!this.data.returnRoute ||
|
||||||
|
!this.data.policy
|
||||||
|
) {
|
||||||
|
this.dialogRef.close();
|
||||||
|
throw new Error(
|
||||||
|
"The access removal dialog was not called with the appropriate operation values."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAccess = async () => {
|
||||||
|
await this.router.navigate(this.data.returnRoute);
|
||||||
|
if (this.data.operation === "delete") {
|
||||||
|
await this.accessPolicyService.deleteAccessPolicy(this.data.policy.accessPolicyId);
|
||||||
|
} else if (this.data.operation == "update") {
|
||||||
|
await this.accessPolicyService.updateAccessPolicy(
|
||||||
|
AccessSelectorComponent.getBaseAccessPolicyView(this.data.policy)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.dialogRef.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
cancel = () => {
|
||||||
|
if (this.data.type == "project") {
|
||||||
|
this.accessPolicyService.refreshProjectAccessPolicyChanges();
|
||||||
|
} else if (this.data.type == "service-account") {
|
||||||
|
this.accessPolicyService.refreshServiceAccountAccessPolicyChanges();
|
||||||
|
}
|
||||||
|
this.dialogRef.close();
|
||||||
|
};
|
||||||
|
}
|
|
@ -21,12 +21,14 @@ export class UserProjectAccessPolicyResponse extends BaseAccessPolicyResponse {
|
||||||
organizationUserId: string;
|
organizationUserId: string;
|
||||||
organizationUserName: string;
|
organizationUserName: string;
|
||||||
grantedProjectId: string;
|
grantedProjectId: string;
|
||||||
|
userId: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
||||||
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
||||||
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
||||||
|
this.userId = this.getResponseProperty("UserId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,12 +36,14 @@ export class UserServiceAccountAccessPolicyResponse extends BaseAccessPolicyResp
|
||||||
organizationUserId: string;
|
organizationUserId: string;
|
||||||
organizationUserName: string;
|
organizationUserName: string;
|
||||||
grantedServiceAccountId: string;
|
grantedServiceAccountId: string;
|
||||||
|
userId: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
this.organizationUserId = this.getResponseProperty("OrganizationUserId");
|
||||||
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
this.organizationUserName = this.getResponseProperty("OrganizationUserName");
|
||||||
this.grantedServiceAccountId = this.getResponseProperty("GrantedServiceAccountId");
|
this.grantedServiceAccountId = this.getResponseProperty("GrantedServiceAccountId");
|
||||||
|
this.userId = this.getResponseProperty("UserId");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +51,14 @@ export class GroupProjectAccessPolicyResponse extends BaseAccessPolicyResponse {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
grantedProjectId: string;
|
grantedProjectId: string;
|
||||||
|
currentUserInGroup: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.groupId = this.getResponseProperty("GroupId");
|
this.groupId = this.getResponseProperty("GroupId");
|
||||||
this.groupName = this.getResponseProperty("GroupName");
|
this.groupName = this.getResponseProperty("GroupName");
|
||||||
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
this.grantedProjectId = this.getResponseProperty("GrantedProjectId");
|
||||||
|
this.currentUserInGroup = this.getResponseProperty("CurrentUserInGroup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,12 +66,14 @@ export class GroupServiceAccountAccessPolicyResponse extends BaseAccessPolicyRes
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
grantedServiceAccountId: string;
|
grantedServiceAccountId: string;
|
||||||
|
currentUserInGroup: boolean;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.groupId = this.getResponseProperty("GroupId");
|
this.groupId = this.getResponseProperty("GroupId");
|
||||||
this.groupName = this.getResponseProperty("GroupName");
|
this.groupName = this.getResponseProperty("GroupName");
|
||||||
this.grantedServiceAccountId = this.getResponseProperty("GrantedServiceAccountId");
|
this.grantedServiceAccountId = this.getResponseProperty("GrantedServiceAccountId");
|
||||||
|
this.currentUserInGroup = this.getResponseProperty("CurrentUserInGroup");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { CoreOrganizationModule } from "@bitwarden/web-vault/app/organizations/c
|
||||||
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||||
|
|
||||||
import { AccessSelectorComponent } from "./access-policies/access-selector.component";
|
import { AccessSelectorComponent } from "./access-policies/access-selector.component";
|
||||||
|
import { AccessRemovalDialogComponent } from "./access-policies/dialogs/access-removal-dialog.component";
|
||||||
import { BulkStatusDialogComponent } from "./dialogs/bulk-status-dialog.component";
|
import { BulkStatusDialogComponent } from "./dialogs/bulk-status-dialog.component";
|
||||||
import { HeaderComponent } from "./header.component";
|
import { HeaderComponent } from "./header.component";
|
||||||
import { NewMenuComponent } from "./new-menu.component";
|
import { NewMenuComponent } from "./new-menu.component";
|
||||||
|
@ -17,6 +18,7 @@ import { SecretsListComponent } from "./secrets-list.component";
|
||||||
imports: [SharedModule, ProductSwitcherModule, MultiSelectModule, CoreOrganizationModule],
|
imports: [SharedModule, ProductSwitcherModule, MultiSelectModule, CoreOrganizationModule],
|
||||||
exports: [
|
exports: [
|
||||||
SharedModule,
|
SharedModule,
|
||||||
|
AccessRemovalDialogComponent,
|
||||||
BulkStatusDialogComponent,
|
BulkStatusDialogComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
NewMenuComponent,
|
NewMenuComponent,
|
||||||
|
@ -26,6 +28,7 @@ import { SecretsListComponent } from "./secrets-list.component";
|
||||||
AccessSelectorComponent,
|
AccessSelectorComponent,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
|
AccessRemovalDialogComponent,
|
||||||
BulkStatusDialogComponent,
|
BulkStatusDialogComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
NewMenuComponent,
|
NewMenuComponent,
|
||||||
|
|
Loading…
Reference in New Issue