diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/service-account.view.ts b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/service-account.view.ts index 6f77be4f44..a0ce182a02 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/service-account.view.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/service-account.view.ts @@ -5,3 +5,7 @@ export class ServiceAccountView { creationDate: string; revisionDate: string; } + +export class ServiceAccountSecretsDetailsView extends ServiceAccountView { + accessToSecrets: number; +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts index 6007cbce68..b2caa9c0c2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts @@ -130,7 +130,7 @@ export class OverviewComponent implements OnInit, OnDestroy { orgId$, this.serviceAccountService.serviceAccount$.pipe(startWith(null)), ]).pipe( - switchMap(([orgId]) => this.serviceAccountService.getServiceAccounts(orgId)), + switchMap(([orgId]) => this.serviceAccountService.getServiceAccounts(orgId, false)), share() ); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/models/responses/service-account.response.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/models/responses/service-account.response.ts index 7ece18f7d8..8339b4f2cd 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/models/responses/service-account.response.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/models/responses/service-account.response.ts @@ -16,3 +16,12 @@ export class ServiceAccountResponse extends BaseResponse { this.revisionDate = this.getResponseProperty("RevisionDate"); } } + +export class ServiceAccountSecretsDetailsResponse extends ServiceAccountResponse { + accessToSecrets: number; + + constructor(response: any) { + super(response); + this.accessToSecrets = this.getResponseProperty("AccessToSecrets"); + } +} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts index 1faf0ff193..c3354c70b2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.service.ts @@ -8,11 +8,17 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt. import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { ServiceAccountView } from "../models/view/service-account.view"; +import { + ServiceAccountSecretsDetailsView, + ServiceAccountView, +} from "../models/view/service-account.view"; import { BulkOperationStatus } from "../shared/dialogs/bulk-status-dialog.component"; import { ServiceAccountRequest } from "./models/requests/service-account.request"; -import { ServiceAccountResponse } from "./models/responses/service-account.response"; +import { + ServiceAccountResponse, + ServiceAccountSecretsDetailsResponse, +} from "./models/responses/service-account.response"; @Injectable({ providedIn: "root", @@ -28,16 +34,24 @@ export class ServiceAccountService { private encryptService: EncryptService ) {} - async getServiceAccounts(organizationId: string): Promise { + async getServiceAccounts( + organizationId: string, + includeAccessToSecrets?: boolean + ): Promise { + const params = new URLSearchParams(); + if (includeAccessToSecrets) { + params.set("includeAccessToSecrets", "true"); + } + const r = await this.apiService.send( "GET", - "/organizations/" + organizationId + "/service-accounts", + "/organizations/" + organizationId + "/service-accounts?" + params.toString(), null, true, true ); - const results = new ListResponse(r, ServiceAccountResponse); - return await this.createServiceAccountViews(organizationId, results.data); + const results = new ListResponse(r, ServiceAccountSecretsDetailsResponse); + return await this.createServiceAccountSecretsDetailsViews(organizationId, results.data); } async getByServiceAccountId( @@ -127,21 +141,39 @@ export class ServiceAccountService { serviceAccountView.organizationId = serviceAccountResponse.organizationId; serviceAccountView.creationDate = serviceAccountResponse.creationDate; serviceAccountView.revisionDate = serviceAccountResponse.revisionDate; - serviceAccountView.name = await this.encryptService.decryptToUtf8( - new EncString(serviceAccountResponse.name), - organizationKey - ); + serviceAccountView.name = serviceAccountResponse.name + ? await this.encryptService.decryptToUtf8( + new EncString(serviceAccountResponse.name), + organizationKey + ) + : null; return serviceAccountView; } - private async createServiceAccountViews( + private async createServiceAccountSecretsDetailsView( + organizationKey: SymmetricCryptoKey, + response: ServiceAccountSecretsDetailsResponse + ): Promise { + const view = new ServiceAccountSecretsDetailsView(); + view.id = response.id; + view.organizationId = response.organizationId; + view.creationDate = response.creationDate; + view.revisionDate = response.revisionDate; + view.accessToSecrets = response.accessToSecrets; + view.name = response.name + ? await this.encryptService.decryptToUtf8(new EncString(response.name), organizationKey) + : null; + return view; + } + + private async createServiceAccountSecretsDetailsViews( organizationId: string, - serviceAccountResponses: ServiceAccountResponse[] - ): Promise { + serviceAccountResponses: ServiceAccountSecretsDetailsResponse[] + ): Promise { const orgKey = await this.getOrganizationKey(organizationId); return await Promise.all( - serviceAccountResponses.map(async (s: ServiceAccountResponse) => { - return await this.createServiceAccountView(orgKey, s); + serviceAccountResponses.map(async (s: ServiceAccountSecretsDetailsResponse) => { + return await this.createServiceAccountSecretsDetailsView(orgKey, s); }) ); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html index e3e71ed85e..bff5cbf4d3 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.html @@ -64,8 +64,7 @@ - - 0 + {{ serviceAccount.accessToSecrets }} {{ serviceAccount.revisionDate | date : "medium" }} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts index 1d79ae0685..34e38e4603 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts @@ -6,25 +6,28 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { TableDataSource } from "@bitwarden/components"; -import { ServiceAccountView } from "../models/view/service-account.view"; +import { + ServiceAccountSecretsDetailsView, + ServiceAccountView, +} from "../models/view/service-account.view"; @Component({ selector: "sm-service-accounts-list", templateUrl: "./service-accounts-list.component.html", }) export class ServiceAccountsListComponent implements OnDestroy { - protected dataSource = new TableDataSource(); + protected dataSource = new TableDataSource(); @Input() - get serviceAccounts(): ServiceAccountView[] { + get serviceAccounts(): ServiceAccountSecretsDetailsView[] { return this._serviceAccounts; } - set serviceAccounts(serviceAccounts: ServiceAccountView[]) { + set serviceAccounts(serviceAccounts: ServiceAccountSecretsDetailsView[]) { this.selection.clear(); this._serviceAccounts = serviceAccounts; this.dataSource.data = serviceAccounts; } - private _serviceAccounts: ServiceAccountView[]; + private _serviceAccounts: ServiceAccountSecretsDetailsView[]; @Input() set search(search: string) { @@ -72,8 +75,8 @@ export class ServiceAccountsListComponent implements OnDestroy { } } - delete(serviceAccount: ServiceAccountView) { - this.deleteServiceAccountsEvent.emit([serviceAccount]); + delete(serviceAccount: ServiceAccountSecretsDetailsView) { + this.deleteServiceAccountsEvent.emit([serviceAccount as ServiceAccountView]); } bulkDeleteServiceAccounts() { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts index 9800a14fe5..973447b441 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts @@ -4,7 +4,10 @@ import { combineLatest, Observable, startWith, switchMap } from "rxjs"; import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; -import { ServiceAccountView } from "../models/view/service-account.view"; +import { + ServiceAccountSecretsDetailsView, + ServiceAccountView, +} from "../models/view/service-account.view"; import { AccessPolicyService } from "../shared/access-policies/access-policy.service"; import { @@ -23,7 +26,7 @@ import { ServiceAccountService } from "./service-account.service"; templateUrl: "./service-accounts.component.html", }) export class ServiceAccountsComponent implements OnInit { - protected serviceAccounts$: Observable; + protected serviceAccounts$: Observable; protected search: string; private organizationId: string; @@ -78,7 +81,7 @@ export class ServiceAccountsComponent implements OnInit { ); } - private async getServiceAccounts(): Promise { - return await this.serviceAccountService.getServiceAccounts(this.organizationId); + private async getServiceAccounts(): Promise { + return await this.serviceAccountService.getServiceAccounts(this.organizationId, true); } }