From 6348269a1a39e4438636c263d5b69790c46fbe2b Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Tue, 28 Feb 2023 16:31:19 +0100 Subject: [PATCH] [SM-352] Projects tab for service accounts (#4858) * Init service layer changes * refactor service to inherit abstract * refactor access-selector component * update access selector in projects * add service accounts access selector * update i18n * fix delete action; use useExisting in providers * update static permissions * service account people should be readwrite on creation * use setter instead of observable input * remove warning callout * remove abstract service * truncate name in table * remove extra comments * Add projects access policy page * Add locale * use map instead of forEach * refactor view factories * update SA people copy * map list responses * Swap to using granted policies endpoints * Remove text-xl from icon --------- Co-authored-by: Thomas Avery Co-authored-by: William Martin Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- apps/web/src/locales/en/messages.json | 8 +- .../models/view/access-policy.view.ts | 1 + .../project/project-people.component.ts | 4 +- .../project-service-accounts.component.ts | 2 +- .../service-account-people.component.ts | 4 +- .../service-account-projects.component.html | 15 +++ .../service-account-projects.component.ts | 78 +++++++++++++ .../service-account.component.html | 2 +- .../service-accounts-routing.module.ts | 7 +- .../service-accounts.module.ts | 5 +- .../access-policies/access-policy.service.ts | 107 +++++++++++++++++- .../access-selector.component.html | 4 +- .../access-selector.component.ts | 24 ++-- .../models/requests/granted-policy.request.ts | 5 + .../responses/access-policy.response.ts | 2 + 15 files changed, 249 insertions(+), 19 deletions(-) create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.html create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts create mode 100644 bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/granted-policy.request.ts diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index b06e0144e16..dbbb02e93e2 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -5864,7 +5864,7 @@ "message": "Service account name" }, "newSaSelectAccess":{ - "message": "Type or select projects or secrets" + "message": "Type or select projects" }, "newSaTypeToFilter":{ "message": "Type to filter" @@ -6365,6 +6365,12 @@ "serviceAccountPeopleDescription": { "message": "Grant groups or people access to this service account." }, + "serviceAccountProjectsDescription": { + "message": "Assign projects to this service account. " + }, + "serviceAccountEmptyProjectAccesspolicies": { + "message": "Add projects to grant access" + }, "canWrite": { "message": "Can write" }, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policy.view.ts b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policy.view.ts index e4fc1fb9426..0f93b549f3a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policy.view.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policy.view.ts @@ -34,6 +34,7 @@ export class ServiceAccountProjectAccessPolicyView extends BaseAccessPolicyView serviceAccountId: string; serviceAccountName: string; grantedProjectId: string; + grantedProjectName: string; } export class ProjectAccessPoliciesView { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts index d1ee0e3ce10..4a1ea9c7fdb 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts @@ -36,7 +36,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy { rows.push({ type: "user", name: policy.organizationUserName, - granteeId: policy.organizationUserId, + id: policy.organizationUserId, accessPolicyId: policy.id, read: policy.read, write: policy.write, @@ -48,7 +48,7 @@ export class ProjectPeopleComponent implements OnInit, OnDestroy { rows.push({ type: "group", name: policy.groupName, - granteeId: policy.groupId, + id: policy.groupId, accessPolicyId: policy.id, read: policy.read, write: policy.write, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts index e442ef4b168..e46c3038b9d 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts @@ -33,7 +33,7 @@ export class ProjectServiceAccountsComponent implements OnInit, OnDestroy { policies.serviceAccountAccessPolicies.map((policy) => ({ type: "serviceAccount", name: policy.serviceAccountName, - granteeId: policy.serviceAccountId, + id: policy.serviceAccountId, accessPolicyId: policy.id, read: policy.read, write: policy.write, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts index dbc957ebc5a..302bbd1f359 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts @@ -36,7 +36,7 @@ export class ServiceAccountPeopleComponent { rows.push({ type: "user", name: policy.organizationUserName, - granteeId: policy.organizationUserId, + id: policy.organizationUserId, accessPolicyId: policy.id, read: policy.read, write: policy.write, @@ -49,7 +49,7 @@ export class ServiceAccountPeopleComponent { rows.push({ type: "group", name: policy.groupName, - granteeId: policy.groupId, + id: policy.groupId, accessPolicyId: policy.id, read: policy.read, write: policy.write, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.html new file mode 100644 index 00000000000..6ceb2dc5f87 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.html @@ -0,0 +1,15 @@ +
+

+ {{ "serviceAccountProjectsDescription" | i18n }} +

+ + +
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts new file mode 100644 index 00000000000..c886523834a --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts @@ -0,0 +1,78 @@ +import { Component } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; +import { combineLatestWith, map, Observable, startWith, Subject, switchMap, takeUntil } from "rxjs"; + +import { SelectItemView } from "@bitwarden/components/src/multi-select/models/select-item-view"; + +import { ServiceAccountProjectAccessPolicyView } from "../../models/view/access-policy.view"; +import { AccessPolicyService } from "../../shared/access-policies/access-policy.service"; +import { + AccessSelectorComponent, + AccessSelectorRowView, +} from "../../shared/access-policies/access-selector.component"; + +@Component({ + selector: "sm-service-account-projects", + templateUrl: "./service-account-projects.component.html", +}) +export class ServiceAccountProjectsComponent { + private destroy$ = new Subject(); + private serviceAccountId: string; + private organizationId: string; + + protected rows$: Observable = + this.accessPolicyService.serviceAccountGrantedPolicyChanges$.pipe( + startWith(null), + combineLatestWith(this.route.params), + switchMap(([_, params]) => + this.accessPolicyService.getGrantedPolicies(params.serviceAccountId, params.organizationId) + ), + 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: true, + } as AccessSelectorRowView; + }); + }) + ); + + protected handleCreateAccessPolicies(selected: SelectItemView[]) { + const serviceAccountProjectAccessPolicyView = selected + .filter((selection) => AccessSelectorComponent.getAccessItemType(selection) === "project") + .map((filtered) => { + const view = new ServiceAccountProjectAccessPolicyView(); + view.serviceAccountId = this.serviceAccountId; + view.grantedProjectId = filtered.id; + view.read = true; + view.write = false; + return view; + }); + + return this.accessPolicyService.createGrantedPolicies( + this.organizationId, + this.serviceAccountId, + serviceAccountProjectAccessPolicyView + ); + } + + constructor(private route: ActivatedRoute, private accessPolicyService: AccessPolicyService) {} + + ngOnInit(): void { + this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => { + this.organizationId = params.organizationId; + this.serviceAccountId = params.serviceAccountId; + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.html index 12a26bb6cc0..fcc01ca92ac 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.html @@ -10,7 +10,7 @@ - {{ "secrets" | i18n }} + {{ "projects" | i18n }} {{ "people" | i18n }} {{ "accessTokens" | i18n }} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-routing.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-routing.module.ts index 1fdca60605d..11aa0fa8f30 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-routing.module.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-routing.module.ts @@ -3,6 +3,7 @@ import { RouterModule, Routes } from "@angular/router"; import { AccessTokenComponent } from "./access/access-tokens.component"; import { ServiceAccountPeopleComponent } from "./people/service-account-people.component"; +import { ServiceAccountProjectsComponent } from "./projects/service-account-projects.component"; import { ServiceAccountComponent } from "./service-account.component"; import { ServiceAccountsComponent } from "./service-accounts.component"; @@ -18,7 +19,7 @@ const routes: Routes = [ { path: "", pathMatch: "full", - redirectTo: "access", + redirectTo: "projects", }, { path: "access", @@ -28,6 +29,10 @@ const routes: Routes = [ path: "people", component: ServiceAccountPeopleComponent, }, + { + path: "projects", + component: ServiceAccountProjectsComponent, + }, ], }, ]; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.module.ts index e75bf2dc3fc..0019cbc0319 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.module.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.module.ts @@ -11,6 +11,7 @@ import { AccessTokenDialogComponent } from "./access/dialogs/access-token-dialog import { ExpirationOptionsComponent } from "./access/dialogs/expiration-options.component"; import { ServiceAccountDialogComponent } from "./dialog/service-account-dialog.component"; import { ServiceAccountPeopleComponent } from "./people/service-account-people.component"; +import { ServiceAccountProjectsComponent } from "./projects/service-account-projects.component"; import { ServiceAccountComponent } from "./service-account.component"; import { ServiceAccountsListComponent } from "./service-accounts-list.component"; import { ServiceAccountsRoutingModule } from "./service-accounts-routing.module"; @@ -20,12 +21,14 @@ import { ServiceAccountsComponent } from "./service-accounts.component"; imports: [SecretsManagerSharedModule, ServiceAccountsRoutingModule, BreadcrumbsModule], declarations: [ AccessListComponent, - ExpirationOptionsComponent, AccessTokenComponent, AccessTokenCreateDialogComponent, AccessTokenDialogComponent, + ExpirationOptionsComponent, ServiceAccountComponent, ServiceAccountDialogComponent, + ServiceAccountPeopleComponent, + ServiceAccountProjectsComponent, ServiceAccountsComponent, ServiceAccountsListComponent, ServiceAccountPeopleComponent, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts index c63abc1b143..79aee4a0e8c 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy.service.ts @@ -25,6 +25,7 @@ import { ServiceAccountAccessPoliciesResponse } from "../../shared/access-polici import { AccessPolicyUpdateRequest } from "./models/requests/access-policy-update.request"; import { AccessPolicyRequest } from "./models/requests/access-policy.request"; +import { GrantedPolicyRequest } from "./models/requests/granted-policy.request"; import { GroupServiceAccountAccessPolicyResponse, UserServiceAccountAccessPolicyResponse, @@ -40,6 +41,9 @@ import { PotentialGranteeResponse } from "./models/responses/potential-grantee.r export class AccessPolicyService { private _projectAccessPolicyChanges$ = new Subject(); private _serviceAccountAccessPolicyChanges$ = new Subject(); + private _serviceAccountGrantedPolicyChanges$ = new Subject< + ServiceAccountProjectAccessPolicyView[] + >(); /** * Emits when a project access policy is created or deleted. @@ -52,12 +56,56 @@ export class AccessPolicyService { readonly serviceAccountAccessPolicyChanges$ = this._serviceAccountAccessPolicyChanges$.asObservable(); + /** + * Emits when a service account granted policy is created or deleted. + */ + readonly serviceAccountGrantedPolicyChanges$ = + this._serviceAccountGrantedPolicyChanges$.asObservable(); + constructor( private cryptoService: CryptoService, protected apiService: ApiService, protected encryptService: EncryptService ) {} + async getGrantedPolicies( + serviceAccountId: string, + organizationId: string + ): Promise { + 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 { + 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( organizationId: string, projectId: string @@ -132,6 +180,7 @@ export class AccessPolicyService { await this.apiService.send("DELETE", "/access-policies/" + accessPolicyId, null, true, false); this._projectAccessPolicyChanges$.next(null); this._serviceAccountAccessPolicyChanges$.next(null); + this._serviceAccountGrantedPolicyChanges$.next(null); } async updateAccessPolicy(baseAccessPolicyView: BaseAccessPolicyView): Promise { @@ -228,6 +277,10 @@ export class AccessPolicyService { ...this.createBaseAccessPolicyView(response), grantedProjectId: response.grantedProjectId, serviceAccountId: response.serviceAccountId, + grantedProjectName: await this.encryptService.decryptToUtf8( + new EncString(response.grantedProjectName), + organizationKey + ), serviceAccountName: await this.encryptService.decryptToUtf8( new EncString(response.serviceAccountName), organizationKey @@ -318,6 +371,18 @@ export class AccessPolicyService { 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 { return await this.cryptoService.getOrgKey(organizationId); } @@ -367,7 +432,7 @@ export class AccessPolicyService { view.type = r.type; view.email = r.email; - if (r.type === "serviceAccount") { + if (r.type === "serviceAccount" || r.type === "project") { view.name = await this.encryptService.decryptToUtf8(new EncString(r.name), orgKey); } else { view.name = r.name; @@ -376,4 +441,44 @@ export class AccessPolicyService { }) ); } + + 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 { + 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 = await this.encryptService.decryptToUtf8( + new EncString(response.serviceAccountName), + orgKey + ); + view.grantedProjectName = await this.encryptService.decryptToUtf8( + new EncString(response.grantedProjectName), + orgKey + ); + return view; + }) + ); + } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.html index 68fc948659c..dd096f382a8 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.html @@ -63,8 +63,8 @@ bitIconButton="bwi-close" buttonType="main" size="default" - [attr.title]="'close' | i18n" - [attr.aria-label]="'close' | i18n" + [attr.title]="'remove' | i18n" + [attr.aria-label]="'remove' | i18n" [bitAction]="delete(row.accessPolicyId)" > diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.ts index ba7803160f8..7e79c4604d3 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-selector.component.ts @@ -12,9 +12,9 @@ import { BaseAccessPolicyView } from "../../models/view/access-policy.view"; import { AccessPolicyService } from "./access-policy.service"; export type AccessSelectorRowView = { - type: "user" | "group" | "serviceAccount"; + type: "user" | "group" | "serviceAccount" | "project"; name: string; - granteeId: string; + id: string; accessPolicyId: string; read: boolean; write: boolean; @@ -30,6 +30,7 @@ export class AccessSelectorComponent implements OnInit { static readonly userIcon = "bwi-user"; static readonly groupIcon = "bwi-family"; static readonly serviceAccountIcon = "bwi-wrench"; + static readonly projectIcon = "bwi-collection"; @Output() onCreateAccessPolicies = new EventEmitter(); @@ -37,7 +38,7 @@ export class AccessSelectorComponent implements OnInit { @Input() hint: string; @Input() columnTitle: string; @Input() emptyMessage: string; - @Input() granteeType: "people" | "serviceAccounts"; + @Input() granteeType: "people" | "serviceAccounts" | "projects"; protected rows$ = new Subject(); @Input() private set rows(value: AccessSelectorRowView[]) { @@ -57,7 +58,7 @@ export class AccessSelectorComponent implements OnInit { switchMap(([rows, params]) => this.getPotentialGrantees(params.organizationId).then((grantees) => grantees - .filter((g) => !rows.some((row) => row.granteeId === g.id)) + .filter((g) => !rows.some((row) => row.id === g.id)) .map((granteeView) => { let icon: string; let listName = granteeView.name; @@ -74,6 +75,8 @@ export class AccessSelectorComponent implements OnInit { icon = AccessSelectorComponent.groupIcon; } else if (granteeView.type === "serviceAccount") { icon = AccessSelectorComponent.serviceAccountIcon; + } else if (granteeView.type === "project") { + icon = AccessSelectorComponent.projectIcon; } return { icon: icon, @@ -144,9 +147,14 @@ export class AccessSelectorComponent implements OnInit { }; private getPotentialGrantees(organizationId: string) { - return this.granteeType === "people" - ? this.accessPolicyService.getPeoplePotentialGrantees(organizationId) - : this.accessPolicyService.getServiceAccountsPotentialGrantees(organizationId); + switch (this.granteeType) { + case "people": + return this.accessPolicyService.getPeoplePotentialGrantees(organizationId); + case "serviceAccounts": + return this.accessPolicyService.getServiceAccountsPotentialGrantees(organizationId); + case "projects": + return this.accessPolicyService.getProjectsPotentialGrantees(organizationId); + } } static getAccessItemType(item: SelectItemView) { @@ -157,6 +165,8 @@ export class AccessSelectorComponent implements OnInit { return "group"; case AccessSelectorComponent.serviceAccountIcon: return "serviceAccount"; + case AccessSelectorComponent.projectIcon: + return "project"; } } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/granted-policy.request.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/granted-policy.request.ts new file mode 100644 index 00000000000..ddfca2bfb2b --- /dev/null +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/granted-policy.request.ts @@ -0,0 +1,5 @@ +export class GrantedPolicyRequest { + grantedId: string; + read: boolean; + write: boolean; +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/responses/access-policy.response.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/responses/access-policy.response.ts index 967f1602d08..58f574f4ad2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/responses/access-policy.response.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/responses/access-policy.response.ts @@ -73,11 +73,13 @@ export class ServiceAccountProjectAccessPolicyResponse extends BaseAccessPolicyR serviceAccountId: string; serviceAccountName: string; grantedProjectId: string; + grantedProjectName: string; constructor(response: any) { super(response); this.serviceAccountId = this.getResponseProperty("ServiceAccountId"); this.serviceAccountName = this.getResponseProperty("ServiceAccountName"); this.grantedProjectId = this.getResponseProperty("GrantedProjectId"); + this.grantedProjectName = this.getResponseProperty("GrantedProjectName"); } }