From cb20889a94eb56c7f49dfb1c3d874faf1f06280a Mon Sep 17 00:00:00 2001
From: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com>
Date: Fri, 3 Oct 2025 03:52:00 -0400
Subject: [PATCH] [SM-1489] machine account event logs (#15997)
* Adding enums for additional event logs for secrets
* updating messages
* Updating messages to be consistent for logs
* Displaying project logs, and fixing search query param searching in projects list, having deleted log for secrets and projects not show as a link
* Viewing secret and project event logs in event modal, adding to the context menu for secrets and projects the ability to view the logs if user has permission. Restricting logs to SM projs and Secs if the logged in user has event log access but not SM access.
* lint
* Lint Fixes
* fix to messages file
* fixing lint
* Adding machine account event logs
* lint fix
* Update event.service.ts
* removing duplicate function issue from merge
* Update service-accounts-list.component.ts
* fixing message
* Fixes to QA bugs
* lint fix
* linter for messages is annoying
* lint
---
.../manage/entity-events.component.ts | 10 +-
apps/web/src/app/core/event.service.ts | 104 +++++++++++++++++-
apps/web/src/locales/en/messages.json | 86 ++++++++++++++-
.../service-accounts-list.component.html | 9 ++
.../service-accounts-list.component.ts | 62 ++++++++++-
.../service-accounts.component.ts | 85 ++++++++++++--
libs/common/src/abstractions/api.service.ts | 7 ++
libs/common/src/enums/event-type.enum.ts | 7 ++
.../src/models/response/event.response.ts | 2 +
libs/common/src/services/api.service.ts | 22 ++++
10 files changed, 375 insertions(+), 19 deletions(-)
diff --git a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts
index 8484b05283..b4c5a273ac 100644
--- a/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts
+++ b/apps/web/src/app/admin-console/organizations/manage/entity-events.component.ts
@@ -28,7 +28,7 @@ import { EventService } from "../../../core";
import { SharedModule } from "../../../shared";
export interface EntityEventsDialogParams {
- entity: "user" | "cipher" | "secret" | "project";
+ entity: "user" | "cipher" | "secret" | "project" | "service-account";
entityId: string;
organizationId?: string;
@@ -174,6 +174,14 @@ export class EntityEventsComponent implements OnInit, OnDestroy {
dates[1],
clearExisting ? null : this.continuationToken,
);
+ } else if (this.params.entity === "service-account") {
+ response = await this.apiService.getEventsServiceAccount(
+ this.params.organizationId,
+ this.params.entityId,
+ dates[0],
+ dates[1],
+ clearExisting ? null : this.continuationToken,
+ );
} else if (this.params.entity === "project") {
response = await this.apiService.getEventsProject(
this.params.organizationId,
diff --git a/apps/web/src/app/core/event.service.ts b/apps/web/src/app/core/event.service.ts
index 7b1e598a77..05a7f5aa64 100644
--- a/apps/web/src/app/core/event.service.ts
+++ b/apps/web/src/app/core/event.service.ts
@@ -559,9 +559,12 @@ export class EventService {
humanReadableMsg = this.i18nService.t("editedSecretWithId", this.getShortId(ev.secretId));
break;
case EventType.Project_Retrieved:
- msg = this.i18nService.t("accessedProjectWithId", this.formatProjectId(ev, options));
+ msg = this.i18nService.t(
+ "accessedProjectWithIdentifier",
+ this.formatProjectId(ev, options),
+ );
humanReadableMsg = this.i18nService.t(
- "accessedProjectWithId",
+ "accessedProjectWithIdentifier",
this.getShortId(ev.projectId),
);
break;
@@ -583,6 +586,74 @@ export class EventService {
msg = this.i18nService.t("editedProjectWithId", this.formatProjectId(ev, options));
humanReadableMsg = this.i18nService.t("editedProjectWithId", this.getShortId(ev.projectId));
break;
+ case EventType.ServiceAccount_UserAdded:
+ msg = this.i18nService.t(
+ "addedUserToServiceAccountWithId",
+ this.formatUserId(ev, options),
+ this.formatServiceAccountId(ev, options),
+ );
+ humanReadableMsg = this.i18nService.t(
+ "addedUserToServiceAccountWithId",
+ this.formatUserId(ev, options),
+ this.formatServiceAccountId(ev, options),
+ );
+ break;
+ case EventType.ServiceAccount_UserRemoved:
+ msg = this.i18nService.t(
+ "removedUserToServiceAccountWithId",
+ this.formatUserId(ev, options),
+ this.formatServiceAccountId(ev, options),
+ );
+ humanReadableMsg = this.i18nService.t(
+ "removedUserToServiceAccountWithId",
+ this.formatUserId(ev, options),
+ this.formatServiceAccountId(ev, options),
+ );
+ break;
+ case EventType.ServiceAccount_GroupRemoved:
+ msg = this.i18nService.t(
+ "removedGroupFromServiceAccountWithId",
+ this.formatGroupId(ev),
+ this.formatServiceAccountId(ev, options),
+ );
+ humanReadableMsg = this.i18nService.t(
+ "removedGroupFromServiceAccountWithId",
+ this.formatGroupId(ev),
+ this.formatServiceAccountId(ev, options),
+ );
+ break;
+ case EventType.ServiceAccount_GroupAdded:
+ msg = this.i18nService.t(
+ "addedGroupToServiceAccountId",
+ this.formatGroupId(ev),
+ this.formatServiceAccountId(ev, options),
+ );
+ humanReadableMsg = this.i18nService.t(
+ "addedGroupToServiceAccountId",
+ this.formatGroupId(ev),
+ this.formatServiceAccountId(ev, options),
+ );
+ break;
+ case EventType.ServiceAccount_Created:
+ msg = this.i18nService.t(
+ "serviceAccountCreatedWithId",
+ this.formatServiceAccountId(ev, options),
+ );
+ humanReadableMsg = this.i18nService.t(
+ "serviceAccountCreatedWithId",
+ this.formatServiceAccountId(ev, options),
+ );
+ break;
+ case EventType.ServiceAccount_Deleted:
+ msg = this.i18nService.t(
+ "serviceAccountDeletedWithId",
+ this.formatServiceAccountId(ev, options),
+ );
+ humanReadableMsg = this.i18nService.t(
+ "serviceAccountDeletedWithId",
+ this.formatServiceAccountId(ev, options),
+ );
+ break;
default:
break;
}
@@ -757,6 +828,35 @@ export class EventService {
return a.outerHTML;
}
+ formatServiceAccountId(ev: EventResponse, options: EventOptions): string {
+ const shortId = this.getShortId(ev.grantedServiceAccountId);
+ if (options.disableLink) {
+ return shortId;
+ }
+ const a = this.makeAnchor(shortId);
+ a.setAttribute(
+ "href",
+ "#/sm/" +
+ ev.organizationId +
+ "/machine-accounts?search=" +
+ shortId +
+ "&viewEvents=" +
+ ev.grantedServiceAccountId +
+ "&type=all",
+ );
+ return a.outerHTML;
+ }
+
+ formatUserId(ev: EventResponse, options: EventOptions): string {
+ const shortId = this.getShortId(ev.userId);
+ if (options.disableLink) {
+ return shortId;
+ }
+ const a = this.makeAnchor(shortId);
+ a.setAttribute("href", "#/organizations/" + ev.organizationId + "/members?search=" + shortId);
+ return a.outerHTML;
+ }
+
formatProjectId(ev: EventResponse, options: EventOptions): string {
const shortId = this.getShortId(ev.projectId);
if (options.disableLink) {
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index d3fcad411e..9b26ec271c 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -7219,6 +7219,9 @@
"unknownSecret": {
"message": "Unknown secret, you may need to request permission to access this secret."
},
+ "unknownServiceAccount": {
+ "message": "Unknown machine account, you may need to request permission to access this machine account."
+ },
"unknownProject": {
"message": "Unknown project, you may need to request permission to access this project."
},
@@ -8569,8 +8572,8 @@
}
}
},
- "accessedProjectWithId": {
- "message": "Accessed a project with Id: $PROJECT_ID$.",
+ "accessedProjectWithIdentifier": {
+ "message": "Accessed a project with identifier: $PROJECT_ID$.",
"placeholders": {
"project_id": {
"content": "$1",
@@ -8595,6 +8598,15 @@
"example": "4d34e8a8"
}
}
+ },
+ "nameUnavailableServiceAccountDeleted": {
+ "message": "Deleted machine account Id: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "service_account_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ }
+ }
},
"editedProjectWithId": {
"message": "Edited a project with identifier: $PROJECT_ID$",
@@ -8604,6 +8616,76 @@
"example": "4d34e8a8"
}
}
+ },
+ "addedUserToServiceAccountWithId": {
+ "message": "Added user: $USER_ID$ to machine account with identifier: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "user_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ },
+ "service_account_id": {
+ "content": "$2",
+ "example": "4d34e8a8"
+ }
+ }
+ },
+ "removedUserToServiceAccountWithId": {
+ "message": "Removed user: $USER_ID$ from machine account with identifier: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "user_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ },
+ "service_account_id": {
+ "content": "$2",
+ "example": "4d34e8a8"
+ }
+ }
+ },
+ "removedGroupFromServiceAccountWithId": {
+ "message": "Removed group: $GROUP_ID$ from machine account with identifier: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "group_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ },
+ "service_account_id": {
+ "content": "$2",
+ "example": "4d34e8a8"
+ }
+ }
+ },
+ "serviceAccountCreatedWithId": {
+ "message": "Created machine account with identifier: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "service_account_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ }
+ }
+ },
+ "addedGroupToServiceAccountId": {
+ "message": "Added group: $GROUP_ID$ to machine account with identifier: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "group_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ },
+ "service_account_id": {
+ "content": "$2",
+ "example": "4d34e8a8"
+ }
+ }
+ },
+ "serviceAccountDeletedWithId": {
+ "message": "Deleted machine account with identifier: $SERVICE_ACCOUNT_ID$",
+ "placeholders": {
+ "service_account_id": {
+ "content": "$1",
+ "example": "4d34e8a8"
+ }
+ }
},
"deletedProjectWithId": {
"message": "Deleted a project with identifier: $PROJECT_ID$",
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 3d7fc9715c..f2fb49b73f 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
@@ -84,6 +84,15 @@
{{ "editMachineAccount" | i18n }}
+