-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SM-949] Add Event Logs to Service Account (#6546)
* Add Event Logs to Service Account * Update bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts Co-authored-by: Thomas Avery <[email protected]> * Add takeUntil import * add service account access guard --------- Co-authored-by: Thomas Avery <[email protected]> Co-authored-by: Thomas Avery <[email protected]>
- Loading branch information
1 parent
87dbe89
commit e9f0c07
Showing
7 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
.../app/secrets-manager/service-accounts/event-logs/service-account-event-log-api.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Injectable } from "@angular/core"; | ||
|
||
import { ApiService } from "@bitwarden/common/abstractions/api.service"; | ||
import { EventResponse } from "@bitwarden/common/models/response/event.response"; | ||
import { ListResponse } from "@bitwarden/common/models/response/list.response"; | ||
|
||
@Injectable({ | ||
providedIn: "root", | ||
}) | ||
export class ServiceAccountEventLogApiService { | ||
constructor(private apiService: ApiService) {} | ||
|
||
async getEvents( | ||
serviceAccountId: string, | ||
start: string, | ||
end: string, | ||
token: string | ||
): Promise<ListResponse<EventResponse>> { | ||
const r = await this.apiService.send( | ||
"GET", | ||
this.addEventParameters("/sm/events/service-accounts/" + serviceAccountId, start, end, token), | ||
null, | ||
true, | ||
true | ||
); | ||
return new ListResponse(r, EventResponse); | ||
} | ||
|
||
private addEventParameters(base: string, start: string, end: string, token: string) { | ||
if (start != null) { | ||
base += "?start=" + start; | ||
} | ||
if (end != null) { | ||
base += base.indexOf("?") > -1 ? "&" : "?"; | ||
base += "end=" + end; | ||
} | ||
if (token != null) { | ||
base += base.indexOf("?") > -1 ? "&" : "?"; | ||
base += "continuationToken=" + token; | ||
} | ||
return base; | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
...rc/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
<div class="tw-mb-4"> | ||
<h1>{{ "eventLogs" | i18n }}</h1> | ||
<div class="tw-mt-4 tw-flex tw-items-center"> | ||
<bit-form-field> | ||
<bit-label>{{ "from" | i18n }}</bit-label> | ||
<input | ||
bitInput | ||
type="datetime-local" | ||
placeholder="{{ 'startDate' | i18n }}" | ||
[(ngModel)]="start" | ||
(change)="dirtyDates = true" | ||
/> | ||
</bit-form-field> | ||
<span class="tw-mx-2">-</span> | ||
<bit-form-field> | ||
<bit-label>{{ "to" | i18n }}</bit-label> | ||
<input | ||
bitInput | ||
type="datetime-local" | ||
placeholder="{{ 'endDate' | i18n }}" | ||
[(ngModel)]="end" | ||
(change)="dirtyDates = true" | ||
/> | ||
</bit-form-field> | ||
<form #refreshForm [appApiAction]="refreshPromise"> | ||
<button | ||
class="tw-mx-3 tw-mt-1" | ||
type="button" | ||
bitButton | ||
buttonType="primary" | ||
(click)="loadEvents(true)" | ||
[disabled]="loaded && refreshForm.loading" | ||
> | ||
{{ "update" | i18n }} | ||
</button> | ||
</form> | ||
<form #exportForm [appApiAction]="exportPromise"> | ||
<button | ||
type="button" | ||
class="tw-mt-1" | ||
bitButton | ||
[ngClass]="{ loading: exportForm.loading }" | ||
(click)="exportEvents()" | ||
[disabled]="(loaded && exportForm.loading) || dirtyDates" | ||
> | ||
<span>{{ "export" | i18n }}</span> | ||
<i | ||
class="bwi bwi-fw" | ||
aria-hidden="true" | ||
[ngClass]="{ | ||
'bwi-sign-in': !exportForm.loading, | ||
'bwi-spinner bwi-spin': exportForm.loading | ||
}" | ||
></i> | ||
</button> | ||
</form> | ||
</div> | ||
</div> | ||
<ng-container *ngIf="!loaded"> | ||
<i | ||
class="bwi bwi-spinner bwi-spin text-muted" | ||
title="{{ 'loading' | i18n }}" | ||
aria-hidden="true" | ||
></i> | ||
<span class="sr-only">{{ "loading" | i18n }}</span> | ||
</ng-container> | ||
<ng-container *ngIf="loaded"> | ||
<p *ngIf="!events || !events.length">{{ "noEventsInList" | i18n }}</p> | ||
<bit-table *ngIf="events && events.length"> | ||
<ng-container header> | ||
<tr> | ||
<th bitCell>{{ "timestamp" | i18n }}</th> | ||
<th bitCell>{{ "client" | i18n }}</th> | ||
<th bitCell>{{ "event" | i18n }}</th> | ||
</tr> | ||
</ng-container> | ||
<ng-template body> | ||
<tr bitRow *ngFor="let e of events" alignContent="top"> | ||
<td bitCell class="tw-whitespace-nowrap">{{ e.date | date : "medium" }}</td> | ||
<td bitCell> | ||
<span title="{{ e.appName }}, {{ e.ip }}">{{ e.appName }}</span> | ||
</td> | ||
<td bitCell [innerHTML]="e.message"></td> | ||
</tr> | ||
</ng-template> | ||
</bit-table> | ||
<button | ||
#moreBtn | ||
[appApiAction]="morePromise" | ||
type="button" | ||
bitButton | ||
buttonType="primary" | ||
(click)="loadEvents(false)" | ||
[disabled]="loaded && $any(moreBtn).loading" | ||
*ngIf="continuationToken" | ||
> | ||
<i | ||
class="bwi bwi-spinner bwi-spin" | ||
title="{{ 'loading' | i18n }}" | ||
aria-hidden="true" | ||
*ngIf="moreBtn.loading" | ||
></i> | ||
<span>{{ "loadMore" | i18n }}</span> | ||
</button> | ||
</ng-container> |
77 changes: 77 additions & 0 deletions
77
.../src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { Component, OnDestroy } from "@angular/core"; | ||
import { ActivatedRoute } from "@angular/router"; | ||
import { Subject, takeUntil } from "rxjs"; | ||
|
||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; | ||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; | ||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; | ||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; | ||
import { BaseEventsComponent } from "@bitwarden/web-vault/app/common/base.events.component"; | ||
import { EventService } from "@bitwarden/web-vault/app/core"; | ||
import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export"; | ||
|
||
import { ServiceAccountEventLogApiService } from "./service-account-event-log-api.service"; | ||
|
||
@Component({ | ||
selector: "sm-service-accounts-events", | ||
templateUrl: "./service-accounts-events.component.html", | ||
}) | ||
export class ServiceAccountEventsComponent extends BaseEventsComponent implements OnDestroy { | ||
exportFileName = "service-account-events"; | ||
private destroy$ = new Subject<void>(); | ||
private serviceAccountId: string; | ||
|
||
constructor( | ||
eventService: EventService, | ||
private serviceAccountEventsApiService: ServiceAccountEventLogApiService, | ||
private route: ActivatedRoute, | ||
i18nService: I18nService, | ||
exportService: EventExportService, | ||
platformUtilsService: PlatformUtilsService, | ||
logService: LogService, | ||
fileDownloadService: FileDownloadService | ||
) { | ||
super( | ||
eventService, | ||
i18nService, | ||
exportService, | ||
platformUtilsService, | ||
logService, | ||
fileDownloadService | ||
); | ||
} | ||
|
||
async ngOnInit() { | ||
// eslint-disable-next-line rxjs/no-async-subscribe | ||
this.route.params.pipe(takeUntil(this.destroy$)).subscribe(async (params) => { | ||
this.serviceAccountId = params.serviceAccountId; | ||
await this.load(); | ||
}); | ||
} | ||
|
||
async load() { | ||
await this.loadEvents(true); | ||
this.loaded = true; | ||
} | ||
|
||
protected requestEvents(startDate: string, endDate: string, continuationToken: string) { | ||
return this.serviceAccountEventsApiService.getEvents( | ||
this.serviceAccountId, | ||
startDate, | ||
endDate, | ||
continuationToken | ||
); | ||
} | ||
|
||
protected getUserName() { | ||
return { | ||
name: this.i18nService.t("serviceAccount") + " " + this.serviceAccountId, | ||
email: "", | ||
}; | ||
} | ||
|
||
ngOnDestroy() { | ||
this.destroy$.next(); | ||
this.destroy$.complete(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...e/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { inject } from "@angular/core"; | ||
import { ActivatedRouteSnapshot, CanActivateFn, createUrlTreeFromSnapshot } from "@angular/router"; | ||
|
||
import { ServiceAccountService } from "../service-account.service"; | ||
|
||
/** | ||
* Redirects to service accounts page if the user doesn't have access to service account. | ||
*/ | ||
export const serviceAccountAccessGuard: CanActivateFn = async (route: ActivatedRouteSnapshot) => { | ||
const serviceAccountService = inject(ServiceAccountService); | ||
|
||
try { | ||
const serviceAccount = await serviceAccountService.getByServiceAccountId( | ||
route.params.serviceAccountId, | ||
route.params.organizationId | ||
); | ||
if (serviceAccount) { | ||
return true; | ||
} | ||
} catch { | ||
return createUrlTreeFromSnapshot(route, [ | ||
"/sm", | ||
route.params.organizationId, | ||
"service-accounts", | ||
]); | ||
} | ||
return createUrlTreeFromSnapshot(route, ["/sm", route.params.organizationId, "service-accounts"]); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters