Add accessToSecrets to service account lists (#5749)

This commit is contained in:
Thomas Avery 2023-08-03 10:06:18 -05:00 committed by GitHub
parent 5756aa851b
commit 6c68b46acb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 29 deletions

View File

@ -5,3 +5,7 @@ export class ServiceAccountView {
creationDate: string; creationDate: string;
revisionDate: string; revisionDate: string;
} }
export class ServiceAccountSecretsDetailsView extends ServiceAccountView {
accessToSecrets: number;
}

View File

@ -130,7 +130,7 @@ export class OverviewComponent implements OnInit, OnDestroy {
orgId$, orgId$,
this.serviceAccountService.serviceAccount$.pipe(startWith(null)), this.serviceAccountService.serviceAccount$.pipe(startWith(null)),
]).pipe( ]).pipe(
switchMap(([orgId]) => this.serviceAccountService.getServiceAccounts(orgId)), switchMap(([orgId]) => this.serviceAccountService.getServiceAccounts(orgId, false)),
share() share()
); );

View File

@ -16,3 +16,12 @@ export class ServiceAccountResponse extends BaseResponse {
this.revisionDate = this.getResponseProperty("RevisionDate"); this.revisionDate = this.getResponseProperty("RevisionDate");
} }
} }
export class ServiceAccountSecretsDetailsResponse extends ServiceAccountResponse {
accessToSecrets: number;
constructor(response: any) {
super(response);
this.accessToSecrets = this.getResponseProperty("AccessToSecrets");
}
}

View File

@ -8,11 +8,17 @@ import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; 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 { BulkOperationStatus } from "../shared/dialogs/bulk-status-dialog.component";
import { ServiceAccountRequest } from "./models/requests/service-account.request"; 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({ @Injectable({
providedIn: "root", providedIn: "root",
@ -28,16 +34,24 @@ export class ServiceAccountService {
private encryptService: EncryptService private encryptService: EncryptService
) {} ) {}
async getServiceAccounts(organizationId: string): Promise<ServiceAccountView[]> { async getServiceAccounts(
organizationId: string,
includeAccessToSecrets?: boolean
): Promise<ServiceAccountSecretsDetailsView[]> {
const params = new URLSearchParams();
if (includeAccessToSecrets) {
params.set("includeAccessToSecrets", "true");
}
const r = await this.apiService.send( const r = await this.apiService.send(
"GET", "GET",
"/organizations/" + organizationId + "/service-accounts", "/organizations/" + organizationId + "/service-accounts?" + params.toString(),
null, null,
true, true,
true true
); );
const results = new ListResponse(r, ServiceAccountResponse); const results = new ListResponse(r, ServiceAccountSecretsDetailsResponse);
return await this.createServiceAccountViews(organizationId, results.data); return await this.createServiceAccountSecretsDetailsViews(organizationId, results.data);
} }
async getByServiceAccountId( async getByServiceAccountId(
@ -127,21 +141,39 @@ export class ServiceAccountService {
serviceAccountView.organizationId = serviceAccountResponse.organizationId; serviceAccountView.organizationId = serviceAccountResponse.organizationId;
serviceAccountView.creationDate = serviceAccountResponse.creationDate; serviceAccountView.creationDate = serviceAccountResponse.creationDate;
serviceAccountView.revisionDate = serviceAccountResponse.revisionDate; serviceAccountView.revisionDate = serviceAccountResponse.revisionDate;
serviceAccountView.name = await this.encryptService.decryptToUtf8( serviceAccountView.name = serviceAccountResponse.name
new EncString(serviceAccountResponse.name), ? await this.encryptService.decryptToUtf8(
organizationKey new EncString(serviceAccountResponse.name),
); organizationKey
)
: null;
return serviceAccountView; return serviceAccountView;
} }
private async createServiceAccountViews( private async createServiceAccountSecretsDetailsView(
organizationKey: SymmetricCryptoKey,
response: ServiceAccountSecretsDetailsResponse
): Promise<ServiceAccountSecretsDetailsView> {
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, organizationId: string,
serviceAccountResponses: ServiceAccountResponse[] serviceAccountResponses: ServiceAccountSecretsDetailsResponse[]
): Promise<ServiceAccountView[]> { ): Promise<ServiceAccountSecretsDetailsView[]> {
const orgKey = await this.getOrganizationKey(organizationId); const orgKey = await this.getOrganizationKey(organizationId);
return await Promise.all( return await Promise.all(
serviceAccountResponses.map(async (s: ServiceAccountResponse) => { serviceAccountResponses.map(async (s: ServiceAccountSecretsDetailsResponse) => {
return await this.createServiceAccountView(orgKey, s); return await this.createServiceAccountSecretsDetailsView(orgKey, s);
}) })
); );
} }

View File

@ -64,8 +64,7 @@
</a> </a>
</td> </td>
<td bitCell> <td bitCell>
<!-- TODO add number of secrets once mapping is implemented--> <span> {{ serviceAccount.accessToSecrets }} </span>
<span> 0 </span>
</td> </td>
<td bitCell>{{ serviceAccount.revisionDate | date : "medium" }}</td> <td bitCell>{{ serviceAccount.revisionDate | date : "medium" }}</td>
<td bitCell> <td bitCell>

View File

@ -6,25 +6,28 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { TableDataSource } from "@bitwarden/components"; import { TableDataSource } from "@bitwarden/components";
import { ServiceAccountView } from "../models/view/service-account.view"; import {
ServiceAccountSecretsDetailsView,
ServiceAccountView,
} from "../models/view/service-account.view";
@Component({ @Component({
selector: "sm-service-accounts-list", selector: "sm-service-accounts-list",
templateUrl: "./service-accounts-list.component.html", templateUrl: "./service-accounts-list.component.html",
}) })
export class ServiceAccountsListComponent implements OnDestroy { export class ServiceAccountsListComponent implements OnDestroy {
protected dataSource = new TableDataSource<ServiceAccountView>(); protected dataSource = new TableDataSource<ServiceAccountSecretsDetailsView>();
@Input() @Input()
get serviceAccounts(): ServiceAccountView[] { get serviceAccounts(): ServiceAccountSecretsDetailsView[] {
return this._serviceAccounts; return this._serviceAccounts;
} }
set serviceAccounts(serviceAccounts: ServiceAccountView[]) { set serviceAccounts(serviceAccounts: ServiceAccountSecretsDetailsView[]) {
this.selection.clear(); this.selection.clear();
this._serviceAccounts = serviceAccounts; this._serviceAccounts = serviceAccounts;
this.dataSource.data = serviceAccounts; this.dataSource.data = serviceAccounts;
} }
private _serviceAccounts: ServiceAccountView[]; private _serviceAccounts: ServiceAccountSecretsDetailsView[];
@Input() @Input()
set search(search: string) { set search(search: string) {
@ -72,8 +75,8 @@ export class ServiceAccountsListComponent implements OnDestroy {
} }
} }
delete(serviceAccount: ServiceAccountView) { delete(serviceAccount: ServiceAccountSecretsDetailsView) {
this.deleteServiceAccountsEvent.emit([serviceAccount]); this.deleteServiceAccountsEvent.emit([serviceAccount as ServiceAccountView]);
} }
bulkDeleteServiceAccounts() { bulkDeleteServiceAccounts() {

View File

@ -4,7 +4,10 @@ import { combineLatest, Observable, startWith, switchMap } from "rxjs";
import { DialogServiceAbstraction } from "@bitwarden/angular/services/dialog"; 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 { AccessPolicyService } from "../shared/access-policies/access-policy.service";
import { import {
@ -23,7 +26,7 @@ import { ServiceAccountService } from "./service-account.service";
templateUrl: "./service-accounts.component.html", templateUrl: "./service-accounts.component.html",
}) })
export class ServiceAccountsComponent implements OnInit { export class ServiceAccountsComponent implements OnInit {
protected serviceAccounts$: Observable<ServiceAccountView[]>; protected serviceAccounts$: Observable<ServiceAccountSecretsDetailsView[]>;
protected search: string; protected search: string;
private organizationId: string; private organizationId: string;
@ -78,7 +81,7 @@ export class ServiceAccountsComponent implements OnInit {
); );
} }
private async getServiceAccounts(): Promise<ServiceAccountView[]> { private async getServiceAccounts(): Promise<ServiceAccountSecretsDetailsView[]> {
return await this.serviceAccountService.getServiceAccounts(this.organizationId); return await this.serviceAccountService.getServiceAccounts(this.organizationId, true);
} }
} }