diff --git a/apps/web/src/app/core/event.service.ts b/apps/web/src/app/core/event.service.ts index 47f4344ec366..99b2a3585064 100644 --- a/apps/web/src/app/core/event.service.ts +++ b/apps/web/src/app/core/event.service.ts @@ -14,7 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic @Injectable() export class EventService { - private policies: Policy[]; + private policies: Policy[] = []; constructor( private i18nService: I18nService, @@ -200,6 +200,10 @@ export class EventService { this.getShortId(ev.cipherId), ); break; + case EventType.Cipher_ClientToggledTOTPSeedVisible: + msg = this.i18nService.t("viewedTOTPSeedItemId", this.formatCipherId(ev, options)); + humanReadableMsg = this.i18nService.t("viewedTOTPSeedItemId", this.getShortId(ev.cipherId)); + break; // Collection case EventType.Collection_Created: msg = this.i18nService.t("createdCollectionId", this.formatCollectionId(ev)); @@ -669,6 +673,246 @@ export class EventService { this.formatServiceAccountId(ev, options), ); break; + case EventType.ServiceAccount_UserPermissionUpdated: + msg = this.i18nService.t( + "updatedUserPermissionForServiceAccountWithId", + this.formatUserId(ev, options), + this.formatServiceAccountId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedUserPermissionForServiceAccountWithId", + this.getShortId(ev.userId), + this.getShortId(ev.serviceAccountId ?? ev.grantedServiceAccountId), + ); + break; + case EventType.ServiceAccount_GroupPermissionUpdated: + msg = this.i18nService.t( + "updatedGroupPermissionForServiceAccountWithId", + this.formatGroupId(ev), + this.formatServiceAccountId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedGroupPermissionForServiceAccountWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.serviceAccountId ?? ev.grantedServiceAccountId), + ); + break; + case EventType.Secret_UserAccessGranted: + msg = this.i18nService.t( + "grantedUserAccessToSecretWithId", + this.formatOrgUserId(ev), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "grantedUserAccessToSecretWithId", + this.getShortId(ev.organizationUserId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_UserAccessRevoked: + msg = this.i18nService.t( + "revokedUserAccessToSecretWithId", + this.formatOrgUserId(ev), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "revokedUserAccessToSecretWithId", + this.getShortId(ev.organizationUserId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_UserAccessUpdated: + msg = this.i18nService.t( + "updatedUserAccessToSecretWithId", + this.formatOrgUserId(ev), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedUserAccessToSecretWithId", + this.getShortId(ev.organizationUserId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_GroupAccessGranted: + msg = this.i18nService.t( + "grantedGroupAccessToSecretWithId", + this.formatGroupId(ev), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "grantedGroupAccessToSecretWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_GroupAccessRevoked: + msg = this.i18nService.t( + "revokedGroupAccessToSecretWithId", + this.formatGroupId(ev), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "revokedGroupAccessToSecretWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_GroupAccessUpdated: + msg = this.i18nService.t( + "updatedGroupAccessToSecretWithId", + this.formatGroupId(ev), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedGroupAccessToSecretWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_ServiceAccountAccessGranted: + msg = this.i18nService.t( + "grantedMachineAccountAccessToSecretWithId", + this.formatServiceAccountId(ev, options), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "grantedMachineAccountAccessToSecretWithId", + this.getShortId(ev.grantedServiceAccountId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_ServiceAccountAccessRevoked: + msg = this.i18nService.t( + "revokedMachineAccountAccessToSecretWithId", + this.formatServiceAccountId(ev, options), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "revokedMachineAccountAccessToSecretWithId", + this.getShortId(ev.grantedServiceAccountId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Secret_ServiceAccountAccessUpdated: + msg = this.i18nService.t( + "updatedMachineAccountAccessToSecretWithId", + this.formatServiceAccountId(ev, options), + this.formatSecretId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedMachineAccountAccessToSecretWithId", + this.getShortId(ev.grantedServiceAccountId), + this.getShortId(ev.secretId), + ); + break; + case EventType.Project_UserAccessGranted: + msg = this.i18nService.t( + "grantedUserAccessToProjectWithId", + this.formatOrgUserId(ev), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "grantedUserAccessToProjectWithId", + this.getShortId(ev.organizationUserId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_UserAccessRevoked: + msg = this.i18nService.t( + "revokedUserAccessToProjectWithId", + this.formatOrgUserId(ev), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "revokedUserAccessToProjectWithId", + this.getShortId(ev.organizationUserId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_UserAccessUpdated: + msg = this.i18nService.t( + "updatedUserAccessToProjectWithId", + this.formatOrgUserId(ev), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedUserAccessToProjectWithId", + this.getShortId(ev.organizationUserId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_GroupAccessGranted: + msg = this.i18nService.t( + "grantedGroupAccessToProjectWithId", + this.formatGroupId(ev), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "grantedGroupAccessToProjectWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_GroupAccessRevoked: + msg = this.i18nService.t( + "revokedGroupAccessToProjectWithId", + this.formatGroupId(ev), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "revokedGroupAccessToProjectWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_GroupAccessUpdated: + msg = this.i18nService.t( + "updatedGroupAccessToProjectWithId", + this.formatGroupId(ev), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedGroupAccessToProjectWithId", + this.getShortId(ev.groupId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_ServiceAccountAccessGranted: + msg = this.i18nService.t( + "grantedMachineAccountAccessToProjectWithId", + this.formatServiceAccountId(ev, options), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "grantedMachineAccountAccessToProjectWithId", + this.getShortId(ev.grantedServiceAccountId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_ServiceAccountAccessRevoked: + msg = this.i18nService.t( + "revokedMachineAccountAccessToProjectWithId", + this.formatServiceAccountId(ev, options), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "revokedMachineAccountAccessToProjectWithId", + this.getShortId(ev.grantedServiceAccountId), + this.getShortId(ev.projectId), + ); + break; + case EventType.Project_ServiceAccountAccessUpdated: + msg = this.i18nService.t( + "updatedMachineAccountAccessToProjectWithId", + this.formatServiceAccountId(ev, options), + this.formatProjectId(ev, options), + ); + humanReadableMsg = this.i18nService.t( + "updatedMachineAccountAccessToProjectWithId", + this.getShortId(ev.grantedServiceAccountId), + this.getShortId(ev.projectId), + ); + break; default: break; } @@ -846,7 +1090,8 @@ export class EventService { } formatServiceAccountId(ev: EventResponse, options: EventOptions): string { - const shortId = this.getShortId(ev.grantedServiceAccountId); + const id = ev.serviceAccountId ?? ev.grantedServiceAccountId; + const shortId = this.getShortId(id); if (options.disableLink) { return shortId; } @@ -858,7 +1103,7 @@ export class EventService { "/machine-accounts?search=" + shortId + "&viewEvents=" + - ev.grantedServiceAccountId + + id + "&type=all", ); return a.outerHTML; diff --git a/apps/web/src/app/tools/event-export/event.export.ts b/apps/web/src/app/tools/event-export/event.export.ts index b68f67d3d871..a052f1cacac1 100644 --- a/apps/web/src/app/tools/event-export/event.export.ts +++ b/apps/web/src/app/tools/event-export/event.export.ts @@ -1,4 +1,4 @@ -import { EventType } from "@bitwarden/common/enums"; +import { eventTypeNames } from "@bitwarden/common/enums"; import { EventView } from "@bitwarden/common/models/view/event.view"; export class EventExport { @@ -22,7 +22,7 @@ export class EventExport { this.userEmail = event.userEmail; this.date = event.date; this.ip = event.ip; - this.type = EventType[event.type]; + this.type = eventTypeNames[event.type]; this.installationId = event.installationId; } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index b257a68052dd..b204743fabbc 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -8689,7 +8689,7 @@ }, "canRead": { "message": "Can read", - "description": "Label for the access level of an access token (Read only)." + "description": "Access policy selector permission option for read-only access." }, "accessTokensNoItemsTitle": { "message": "No access tokens to show", @@ -9127,7 +9127,12 @@ "message": "Add projects to grant access" }, "canReadWrite": { - "message": "Can read, write" + "message": "Can read, write", + "description": "Access policy selector permission option for read and write access." + }, + "canManage": { + "message": "Can manage", + "description": "Access policy selector permission option for full manage access." }, "groupSlashUser": { "message": "Group/User" @@ -9350,6 +9355,266 @@ } } }, + "updatedUserPermissionForServiceAccountWithId": { + "message": "Updated user: $USER_ID$ permission for machine account with identifier: $SERVICE_ACCOUNT_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "service_account_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedGroupPermissionForServiceAccountWithId": { + "message": "Updated group: $GROUP_ID$ permission for machine account with identifier: $SERVICE_ACCOUNT_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "service_account_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "grantedUserAccessToSecretWithId": { + "message": "Granted user: $USER_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "revokedUserAccessToSecretWithId": { + "message": "Revoked user: $USER_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedUserAccessToSecretWithId": { + "message": "Updated user: $USER_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "grantedGroupAccessToSecretWithId": { + "message": "Granted group: $GROUP_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "revokedGroupAccessToSecretWithId": { + "message": "Revoked group: $GROUP_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedGroupAccessToSecretWithId": { + "message": "Updated group: $GROUP_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "grantedMachineAccountAccessToSecretWithId": { + "message": "Granted machine account: $SERVICE_ACCOUNT_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "service_account_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "revokedMachineAccountAccessToSecretWithId": { + "message": "Revoked machine account: $SERVICE_ACCOUNT_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "service_account_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedMachineAccountAccessToSecretWithId": { + "message": "Updated machine account: $SERVICE_ACCOUNT_ID$ access to secret with identifier: $SECRET_ID$", + "placeholders": { + "service_account_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "secret_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "grantedUserAccessToProjectWithId": { + "message": "Granted user: $USER_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "revokedUserAccessToProjectWithId": { + "message": "Revoked user: $USER_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedUserAccessToProjectWithId": { + "message": "Updated user: $USER_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "user_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "grantedGroupAccessToProjectWithId": { + "message": "Granted group: $GROUP_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "revokedGroupAccessToProjectWithId": { + "message": "Revoked group: $GROUP_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedGroupAccessToProjectWithId": { + "message": "Updated group: $GROUP_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "group_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "grantedMachineAccountAccessToProjectWithId": { + "message": "Granted machine account: $SERVICE_ACCOUNT_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "service_account_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "revokedMachineAccountAccessToProjectWithId": { + "message": "Revoked machine account: $SERVICE_ACCOUNT_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "service_account_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, + "updatedMachineAccountAccessToProjectWithId": { + "message": "Updated machine account: $SERVICE_ACCOUNT_ID$ access to project with identifier: $PROJECT_ID$", + "placeholders": { + "service_account_id": { + "content": "$1", + "example": "4d34e8a8" + }, + "project_id": { + "content": "$2", + "example": "4d34e8a8" + } + } + }, "deletedProjectWithId": { "message": "Deleted a project with identifier: $PROJECT_ID$", "placeholders": { @@ -12969,5 +13234,15 @@ "example": "Jan 1, 1970" } } + }, + "viewedTOTPSeedItemId": { + "message": "Viewed TOTP seed for item $ID$.", + "description": "Event log entry for when a user views the TOTP seed for a cipher.", + "placeholders": { + "id": { + "content": "$1", + "example": "xxxxxxxx" + } + } } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policies/access-policy.view.ts b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policies/access-policy.view.ts index 676f1447f3e5..3da86e982b75 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policies/access-policy.view.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/models/view/access-policies/access-policy.view.ts @@ -3,6 +3,7 @@ class BaseAccessPolicyView { read: boolean; write: boolean; + manage: boolean; } export class UserAccessPolicyView extends BaseAccessPolicyView { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts index 2bb4d6cb37f0..c7bce7e560fb 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts @@ -134,6 +134,7 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn @Input() permissionList = [ { perm: ApPermissionEnum.CanRead, labelId: "canRead" }, { perm: ApPermissionEnum.CanReadWrite, labelId: "canReadWrite" }, + { perm: ApPermissionEnum.CanManage, labelId: "canManage" }, ]; // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.service.ts index 7fbf2bc01a21..8aa7339b1f95 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.service.ts @@ -19,14 +19,14 @@ import { ApPermissionEnum } from "./models/enums/ap-permission.enum"; export class AccessPolicySelectorService { constructor( private organizationService: OrganizationService, - private accountServcie: AccountService, + private accountService: AccountService, ) {} async showAccessRemovalWarning( organizationId: string, selectedPoliciesValues: ApItemValueType[], ): Promise { - const userId = await firstValueFrom(getUserId(this.accountServcie.activeAccount$)); + const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); const organization = await firstValueFrom( this.organizationService.organizations$(userId).pipe(getOrganizationById(organizationId)), ); @@ -37,7 +37,7 @@ export class AccessPolicySelectorService { return false; } - if (!this.userHasReadWriteAccess(selectedPoliciesValues)) { + if (!this.userHasWriteOrManageAccess(selectedPoliciesValues)) { return true; } @@ -53,18 +53,18 @@ export class AccessPolicySelectorService { return false; } - const userId = await firstValueFrom(getUserId(this.accountServcie.activeAccount$)); + const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); const organization = await firstValueFrom( this.organizationService.organizations$(userId).pipe(getOrganizationById(organizationId)), ); if (!organization) { return false; } - if (organization.isOwner || organization.isAdmin || !this.userHasReadWriteAccess(current)) { + if (organization.isOwner || organization.isAdmin || !this.userHasWriteOrManageAccess(current)) { return false; } - if (!this.userHasReadWriteAccess(selectedPoliciesValues)) { + if (!this.userHasWriteOrManageAccess(selectedPoliciesValues)) { return true; } @@ -92,18 +92,20 @@ export class AccessPolicySelectorService { return !currentIds.every((id) => selectedIds.includes(id)); } - private userHasReadWriteAccess(policies: ApItemValueType[] | ApItemViewType[]): boolean { + private userHasWriteOrManageAccess(policies: ApItemValueType[] | ApItemViewType[]): boolean { const userReadWritePolicy = (policies as Array).find( (s) => s.type === ApItemEnum.User && s.currentUser && - s.permission === ApPermissionEnum.CanReadWrite, + (s.permission === ApPermissionEnum.CanReadWrite || + s.permission === ApPermissionEnum.CanManage), ); const groupReadWritePolicies = (policies as Array).filter( (s) => s.type === ApItemEnum.Group && - s.permission === ApPermissionEnum.CanReadWrite && + (s.permission === ApPermissionEnum.CanReadWrite || + s.permission === ApPermissionEnum.CanManage) && s.currentUserInGroup, ); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-value.type.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-value.type.ts index 2de071fb2e8a..8f60640e0344 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-value.type.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-value.type.ts @@ -42,6 +42,7 @@ export function convertToServiceAccountGrantedPoliciesView( policyView.grantedProjectId = filtered.id; policyView.read = ApPermissionEnumUtil.toRead(filtered.permission); policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission); + policyView.manage = ApPermissionEnumUtil.toManage(filtered.permission); detailView.accessPolicy = policyView; return detailView; @@ -76,6 +77,7 @@ function convertToUserAccessPolicyViews(apItemValues: ApItemValueType[]): UserAc policyView.organizationUserId = filtered.id; policyView.read = ApPermissionEnumUtil.toRead(filtered.permission); policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission); + policyView.manage = ApPermissionEnumUtil.toManage(filtered.permission); return policyView; }); } @@ -88,6 +90,7 @@ function convertToGroupAccessPolicyViews(apItemValues: ApItemValueType[]): Group policyView.groupId = filtered.id; policyView.read = ApPermissionEnumUtil.toRead(filtered.permission); policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission); + policyView.manage = ApPermissionEnumUtil.toManage(filtered.permission); return policyView; }); } @@ -102,6 +105,7 @@ function convertToServiceAccountAccessPolicyViews( policyView.serviceAccountId = filtered.id; policyView.read = ApPermissionEnumUtil.toRead(filtered.permission); policyView.write = ApPermissionEnumUtil.toWrite(filtered.permission); + policyView.manage = ApPermissionEnumUtil.toManage(filtered.permission); return policyView; }); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-view.type.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-view.type.ts index 607336e47475..0b2e8f606d9b 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-view.type.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/ap-item-view.type.ts @@ -28,18 +28,18 @@ export type ApItemViewType = SelectItemView & { readOnly: boolean; } & ( | { - type: ApItemEnum.User; + type: typeof ApItemEnum.User; currentUser?: boolean; } | { - type: ApItemEnum.Group; + type: typeof ApItemEnum.Group; currentUserInGroup?: boolean; } | { - type: ApItemEnum.ServiceAccount; + type: typeof ApItemEnum.ServiceAccount; } | { - type: ApItemEnum.Project; + type: typeof ApItemEnum.Project; } ); @@ -67,6 +67,7 @@ export function convertGrantedPoliciesToAccessPolicyItemViews( permission: ApPermissionEnumUtil.toApPermissionEnum( detailView.accessPolicy.read, detailView.accessPolicy.write, + detailView.accessPolicy.manage, ), readOnly: !detailView.hasPermission, }); @@ -145,7 +146,7 @@ function toUserApItemViews(policies: UserAccessPolicyView[]): ApItemViewType[] { id: policy.organizationUserId, labelName: policy.organizationUserName, listName: policy.organizationUserName, - permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write), + permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write, policy.manage), currentUser: policy.currentUser, readOnly: false, }; @@ -160,7 +161,7 @@ function toGroupApItemViews(policies: GroupAccessPolicyView[]): ApItemViewType[] id: policy.groupId, labelName: policy.groupName, listName: policy.groupName, - permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write), + permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write, policy.manage), currentUserInGroup: policy.currentUserInGroup, readOnly: false, }; @@ -177,7 +178,7 @@ function toServiceAccountsApItemViews( id: policy.serviceAccountId, labelName: policy.serviceAccountName, listName: policy.serviceAccountName, - permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write), + permission: ApPermissionEnumUtil.toApPermissionEnum(policy.read, policy.write, policy.manage), readOnly: false, }; }); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts index 6b92fd7458a0..988d327de6c8 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-item.enum.ts @@ -1,11 +1,10 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum ApItemEnum { - User, - Group, - ServiceAccount, - Project, -} +export const ApItemEnum = Object.freeze({ + User: 0, + Group: 1, + ServiceAccount: 2, + Project: 3, +} as const); +export type ApItemEnum = (typeof ApItemEnum)[keyof typeof ApItemEnum]; export class ApItemEnumUtil { static itemIcon(type: ApItemEnum): string { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts index a57f96361783..2d3faefffd9f 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/models/enums/ap-permission.enum.ts @@ -1,32 +1,41 @@ -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum ApPermissionEnum { - CanRead = "canRead", - CanReadWrite = "canReadWrite", -} +export const ApPermissionEnum = Object.freeze({ + CanRead: "canRead", + CanReadWrite: "canReadWrite", + CanManage: "canManage", +} as const); +export type ApPermissionEnum = (typeof ApPermissionEnum)[keyof typeof ApPermissionEnum]; export class ApPermissionEnumUtil { - static toApPermissionEnum(read: boolean, write: boolean): ApPermissionEnum { - if (read && write) { + static toApPermissionEnum(read: boolean, write: boolean, manage: boolean): ApPermissionEnum { + if (manage) { + return ApPermissionEnum.CanManage; + } else if (read && write) { return ApPermissionEnum.CanReadWrite; } else if (read) { return ApPermissionEnum.CanRead; } else { - throw new Error("Unsupported Access Policy Permission option"); + // write=true/read=false or all-false is not a valid state + // eslint-disable-next-line no-console + console.warn("toApPermissionEnum: unexpected permission state", { read, write, manage }); + return ApPermissionEnum.CanRead; } } static toRead(permission: ApPermissionEnum): boolean { - if (permission == ApPermissionEnum.CanRead || permission == ApPermissionEnum.CanReadWrite) { - return true; - } - return false; + return ( + permission === ApPermissionEnum.CanRead || + permission === ApPermissionEnum.CanReadWrite || + permission === ApPermissionEnum.CanManage + ); } static toWrite(permission: ApPermissionEnum): boolean { - if (permission === ApPermissionEnum.CanReadWrite) { - return true; - } - return false; + return ( + permission === ApPermissionEnum.CanReadWrite || permission === ApPermissionEnum.CanManage + ); + } + + static toManage(permission: ApPermissionEnum): boolean { + return permission === ApPermissionEnum.CanManage; } } 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 18f6313b95c8..085f29a2e3ac 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 @@ -32,6 +32,7 @@ import { PeopleAccessPoliciesRequest } from "../../shared/access-policies/models import { ServiceAccountGrantedPoliciesRequest } from "../access-policies/models/requests/service-account-granted-policies.request"; import { AccessPolicyRequest } from "./models/requests/access-policy.request"; +import { GrantedPolicyRequest } from "./models/requests/granted-policy.request"; import { ProjectServiceAccountsAccessPoliciesRequest } from "./models/requests/project-service-accounts-access-policies.request"; import { SecretAccessPoliciesRequest } from "./models/requests/secret-access-policies.request"; import { @@ -235,6 +236,23 @@ export class AccessPolicyService { return await this.createSecretAccessPoliciesView(result, organizationId); } + async putSecretAccessPolicies( + organizationId: string, + secretId: string, + policies: SecretAccessPoliciesView, + ): Promise { + const request = this.getSecretAccessPoliciesRequest(policies); + const r = await this.apiService.send( + "PUT", + "/secrets/" + secretId + "/access-policies", + request, + true, + true, + ); + const result = new SecretAccessPoliciesResponse(r); + return await this.createSecretAccessPoliciesView(result, organizationId); + } + async getPeoplePotentialGrantees(organizationId: string) { const r = await this.apiService.send( "GET", @@ -293,6 +311,7 @@ export class AccessPolicyService { request.granteeId = granteeId; request.read = view.read; request.write = view.write; + request.manage = view.manage; return request; } @@ -301,11 +320,14 @@ export class AccessPolicyService { ): ServiceAccountGrantedPoliciesRequest { const request = new ServiceAccountGrantedPoliciesRequest(); - request.projectGrantedPolicyRequests = policies.grantedProjectPolicies.map((detailView) => ({ - grantedId: detailView.accessPolicy.grantedProjectId, - read: detailView.accessPolicy.read, - write: detailView.accessPolicy.write, - })); + request.projectGrantedPolicyRequests = policies.grantedProjectPolicies.map((detailView) => { + const r = new GrantedPolicyRequest(); + r.grantedId = detailView.accessPolicy.grantedProjectId; + r.read = detailView.accessPolicy.read; + r.write = detailView.accessPolicy.write; + r.manage = detailView.accessPolicy.manage; + return r; + }); return request; } @@ -352,6 +374,7 @@ export class AccessPolicyService { return { read: response.read, write: response.write, + manage: response.manage, }; } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/access-policy.request.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/access-policy.request.ts index ab8e096d5d5a..c450d7549588 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/access-policy.request.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/models/requests/access-policy.request.ts @@ -4,4 +4,5 @@ export class AccessPolicyRequest { granteeId: string; read: boolean; write: boolean; + manage: boolean; } 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 index 888863c7efe6..3e780d00aa71 100644 --- 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 @@ -4,4 +4,5 @@ export class GrantedPolicyRequest { grantedId: string; read: boolean; write: boolean; + manage: 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 88399b4e1215..17a5bfcfb85c 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 @@ -3,11 +3,13 @@ import { BaseResponse } from "@bitwarden/common/models/response/base.response"; class BaseAccessPolicyResponse extends BaseResponse { read: boolean; write: boolean; + manage: boolean; constructor(response: any) { super(response); - this.read = this.getResponseProperty("Read"); - this.write = this.getResponseProperty("Write"); + this.read = this.getResponseProperty("Read") ?? false; + this.write = this.getResponseProperty("Write") ?? false; + this.manage = this.getResponseProperty("Manage") ?? false; } } diff --git a/libs/common/src/enums/event-type.enum.spec.ts b/libs/common/src/enums/event-type.enum.spec.ts new file mode 100644 index 000000000000..783d1be122d5 --- /dev/null +++ b/libs/common/src/enums/event-type.enum.spec.ts @@ -0,0 +1,86 @@ +import { EventType, eventTypeNames } from "./event-type.enum"; + +describe("EventType", () => { + describe("value preservation", () => { + it("preserves values for the User group", () => { + expect(EventType.User_LoggedIn).toBe(1000); + expect(EventType.User_TdeOffboardingPasswordSet).toBe(1011); + }); + + it("preserves values for the Cipher group", () => { + expect(EventType.Cipher_Created).toBe(1100); + expect(EventType.Cipher_ClientToggledTOTPSeedVisible).toBe(1118); + }); + + it("preserves values for the Collection group", () => { + expect(EventType.Collection_Created).toBe(1300); + }); + + it("preserves values for the Group group", () => { + expect(EventType.Group_Created).toBe(1400); + }); + + it("preserves values for the OrganizationUser group", () => { + expect(EventType.OrganizationUser_Invited).toBe(1500); + expect(EventType.OrganizationUser_Left).toBe(1516); + }); + + it("preserves values for the Organization group", () => { + expect(EventType.Organization_Updated).toBe(1600); + expect(EventType.Organization_ItemOrganization_Declined).toBe(1619); + }); + + it("preserves values for the Policy group", () => { + expect(EventType.Policy_Updated).toBe(1700); + }); + + it("preserves values for the ProviderUser group", () => { + expect(EventType.ProviderUser_Invited).toBe(1800); + }); + + it("preserves values for the ProviderOrganization group", () => { + expect(EventType.ProviderOrganization_Created).toBe(1900); + }); + + it("preserves values for the OrganizationDomain group", () => { + expect(EventType.OrganizationDomain_Added).toBe(2000); + }); + + it("preserves values for the Secret group", () => { + expect(EventType.Secret_Retrieved).toBe(2100); + expect(EventType.Secret_ServiceAccountAccessUpdated).toBe(2114); + }); + + it("preserves values for the Project group", () => { + expect(EventType.Project_Retrieved).toBe(2200); + expect(EventType.Project_ServiceAccountAccessUpdated).toBe(2212); + }); + + it("preserves values for the ServiceAccount group", () => { + expect(EventType.ServiceAccount_UserAdded).toBe(2300); + expect(EventType.ServiceAccount_GroupPermissionUpdated).toBe(2307); + }); + }); + + describe("eventTypeNames", () => { + it("maps numeric values back to key names", () => { + expect(eventTypeNames[1000]).toBe("User_LoggedIn"); + expect(eventTypeNames[1100]).toBe("Cipher_Created"); + expect(eventTypeNames[1300]).toBe("Collection_Created"); + expect(eventTypeNames[1400]).toBe("Group_Created"); + expect(eventTypeNames[1500]).toBe("OrganizationUser_Invited"); + expect(eventTypeNames[1600]).toBe("Organization_Updated"); + expect(eventTypeNames[1700]).toBe("Policy_Updated"); + expect(eventTypeNames[1800]).toBe("ProviderUser_Invited"); + expect(eventTypeNames[1900]).toBe("ProviderOrganization_Created"); + expect(eventTypeNames[2000]).toBe("OrganizationDomain_Added"); + expect(eventTypeNames[2100]).toBe("Secret_Retrieved"); + expect(eventTypeNames[2200]).toBe("Project_Retrieved"); + expect(eventTypeNames[2300]).toBe("ServiceAccount_UserAdded"); + }); + + it("returns undefined for an unknown value", () => { + expect(eventTypeNames[9999 as EventType]).toBeUndefined(); + }); + }); +}); diff --git a/libs/common/src/enums/event-type.enum.ts b/libs/common/src/enums/event-type.enum.ts index 4750c881f061..bb49f2cc5afa 100644 --- a/libs/common/src/enums/event-type.enum.ts +++ b/libs/common/src/enums/event-type.enum.ts @@ -1,120 +1,169 @@ // Increment by 100 for each new set of events -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum EventType { - User_LoggedIn = 1000, - User_ChangedPassword = 1001, - User_Updated2fa = 1002, - User_Disabled2fa = 1003, - User_Recovered2fa = 1004, - User_FailedLogIn = 1005, - User_FailedLogIn2fa = 1006, - User_ClientExportedVault = 1007, - User_UpdatedTempPassword = 1008, - User_MigratedKeyToKeyConnector = 1009, - User_RequestedDeviceApproval = 1010, - User_TdeOffboardingPasswordSet = 1011, - - Cipher_Created = 1100, - Cipher_Updated = 1101, - Cipher_Deleted = 1102, - Cipher_AttachmentCreated = 1103, - Cipher_AttachmentDeleted = 1104, - Cipher_Shared = 1105, - Cipher_UpdatedCollections = 1106, - Cipher_ClientViewed = 1107, - Cipher_ClientToggledPasswordVisible = 1108, - Cipher_ClientToggledHiddenFieldVisible = 1109, - Cipher_ClientToggledCardCodeVisible = 1110, - Cipher_ClientCopiedPassword = 1111, - Cipher_ClientCopiedHiddenField = 1112, - Cipher_ClientCopiedCardCode = 1113, - Cipher_ClientAutofilled = 1114, - Cipher_SoftDeleted = 1115, - Cipher_Restored = 1116, - Cipher_ClientToggledCardNumberVisible = 1117, - Cipher_ClientToggledTOTPSeedVisible = 1118, - - Collection_Created = 1300, - Collection_Updated = 1301, - Collection_Deleted = 1302, - - Group_Created = 1400, - Group_Updated = 1401, - Group_Deleted = 1402, - - OrganizationUser_Invited = 1500, - OrganizationUser_Confirmed = 1501, - OrganizationUser_Updated = 1502, - OrganizationUser_Removed = 1503, - OrganizationUser_UpdatedGroups = 1504, - OrganizationUser_UnlinkedSso = 1505, - OrganizationUser_ResetPassword_Enroll = 1506, - OrganizationUser_ResetPassword_Withdraw = 1507, - OrganizationUser_AdminResetPassword = 1508, - OrganizationUser_ResetSsoLink = 1509, - OrganizationUser_FirstSsoLogin = 1510, - OrganizationUser_Revoked = 1511, - OrganizationUser_Restored = 1512, - OrganizationUser_ApprovedAuthRequest = 1513, - OrganizationUser_RejectedAuthRequest = 1514, - OrganizationUser_Deleted = 1515, - OrganizationUser_Left = 1516, - - Organization_Updated = 1600, - Organization_PurgedVault = 1601, - Organization_ClientExportedVault = 1602, - Organization_VaultAccessed = 1603, - Organization_EnabledSso = 1604, - Organization_DisabledSso = 1605, - Organization_EnabledKeyConnector = 1606, - Organization_DisabledKeyConnector = 1607, - Organization_SponsorshipsSynced = 1608, - Organization_CollectionManagementUpdated = 1609, - Organization_CollectionManagement_LimitCollectionCreationEnabled = 1610, - Organization_CollectionManagement_LimitCollectionCreationDisabled = 1611, - Organization_CollectionManagement_LimitCollectionDeletionEnabled = 1612, - Organization_CollectionManagement_LimitCollectionDeletionDisabled = 1613, - Organization_CollectionManagement_LimitItemDeletionEnabled = 1614, - Organization_CollectionManagement_LimitItemDeletionDisabled = 1615, - Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled = 1616, - Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled = 1617, - Organization_ItemOrganization_Accepted = 1618, - Organization_ItemOrganization_Declined = 1619, - - Policy_Updated = 1700, - - ProviderUser_Invited = 1800, - ProviderUser_Confirmed = 1801, - ProviderUser_Updated = 1802, - ProviderUser_Removed = 1803, - - ProviderOrganization_Created = 1900, - ProviderOrganization_Added = 1901, - ProviderOrganization_Removed = 1902, - ProviderOrganization_VaultAccessed = 1903, - - OrganizationDomain_Added = 2000, - OrganizationDomain_Removed = 2001, - OrganizationDomain_Verified = 2002, - OrganizationDomain_NotVerified = 2003, - - Secret_Retrieved = 2100, - Secret_Created = 2101, - Secret_Edited = 2102, - Secret_Deleted = 2103, - Secret_Permanently_Deleted = 2104, - Secret_Restored = 2105, - - Project_Retrieved = 2200, - Project_Created = 2201, - Project_Edited = 2202, - Project_Deleted = 2203, - - ServiceAccount_UserAdded = 2300, - ServiceAccount_UserRemoved = 2301, - ServiceAccount_GroupAdded = 2302, - ServiceAccount_GroupRemoved = 2303, - ServiceAccount_Created = 2304, - ServiceAccount_Deleted = 2305, -} +const _EventType = Object.freeze({ + // User (1000) + User_LoggedIn: 1000, + User_ChangedPassword: 1001, + User_Updated2fa: 1002, + User_Disabled2fa: 1003, + User_Recovered2fa: 1004, + User_FailedLogIn: 1005, + User_FailedLogIn2fa: 1006, + User_ClientExportedVault: 1007, + User_UpdatedTempPassword: 1008, + User_MigratedKeyToKeyConnector: 1009, + User_RequestedDeviceApproval: 1010, + User_TdeOffboardingPasswordSet: 1011, + + // Cipher (1100) + Cipher_Created: 1100, + Cipher_Updated: 1101, + Cipher_Deleted: 1102, + Cipher_AttachmentCreated: 1103, + Cipher_AttachmentDeleted: 1104, + Cipher_Shared: 1105, + Cipher_UpdatedCollections: 1106, + Cipher_ClientViewed: 1107, + Cipher_ClientToggledPasswordVisible: 1108, + Cipher_ClientToggledHiddenFieldVisible: 1109, + Cipher_ClientToggledCardCodeVisible: 1110, + Cipher_ClientCopiedPassword: 1111, + Cipher_ClientCopiedHiddenField: 1112, + Cipher_ClientCopiedCardCode: 1113, + Cipher_ClientAutofilled: 1114, + Cipher_SoftDeleted: 1115, + Cipher_Restored: 1116, + Cipher_ClientToggledCardNumberVisible: 1117, + Cipher_ClientToggledTOTPSeedVisible: 1118, + + // Collection (1300) + Collection_Created: 1300, + Collection_Updated: 1301, + Collection_Deleted: 1302, + + // Group (1400) + Group_Created: 1400, + Group_Updated: 1401, + Group_Deleted: 1402, + + // OrganizationUser (1500) + OrganizationUser_Invited: 1500, + OrganizationUser_Confirmed: 1501, + OrganizationUser_Updated: 1502, + OrganizationUser_Removed: 1503, + OrganizationUser_UpdatedGroups: 1504, + OrganizationUser_UnlinkedSso: 1505, + OrganizationUser_ResetPassword_Enroll: 1506, + OrganizationUser_ResetPassword_Withdraw: 1507, + OrganizationUser_AdminResetPassword: 1508, + OrganizationUser_ResetSsoLink: 1509, + OrganizationUser_FirstSsoLogin: 1510, + OrganizationUser_Revoked: 1511, + OrganizationUser_Restored: 1512, + OrganizationUser_ApprovedAuthRequest: 1513, + OrganizationUser_RejectedAuthRequest: 1514, + OrganizationUser_Deleted: 1515, + OrganizationUser_Left: 1516, + + // Organization (1600) + Organization_Updated: 1600, + Organization_PurgedVault: 1601, + Organization_ClientExportedVault: 1602, + Organization_VaultAccessed: 1603, + Organization_EnabledSso: 1604, + Organization_DisabledSso: 1605, + Organization_EnabledKeyConnector: 1606, + Organization_DisabledKeyConnector: 1607, + Organization_SponsorshipsSynced: 1608, + Organization_CollectionManagementUpdated: 1609, + Organization_CollectionManagement_LimitCollectionCreationEnabled: 1610, + Organization_CollectionManagement_LimitCollectionCreationDisabled: 1611, + Organization_CollectionManagement_LimitCollectionDeletionEnabled: 1612, + Organization_CollectionManagement_LimitCollectionDeletionDisabled: 1613, + Organization_CollectionManagement_LimitItemDeletionEnabled: 1614, + Organization_CollectionManagement_LimitItemDeletionDisabled: 1615, + Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsEnabled: 1616, + Organization_CollectionManagement_AllowAdminAccessToAllCollectionItemsDisabled: 1617, + Organization_ItemOrganization_Accepted: 1618, + Organization_ItemOrganization_Declined: 1619, + + // Policy (1700) + Policy_Updated: 1700, + + // ProviderUser (1800) + ProviderUser_Invited: 1800, + ProviderUser_Confirmed: 1801, + ProviderUser_Updated: 1802, + ProviderUser_Removed: 1803, + + // ProviderOrganization (1900) + ProviderOrganization_Created: 1900, + ProviderOrganization_Added: 1901, + ProviderOrganization_Removed: 1902, + ProviderOrganization_VaultAccessed: 1903, + + // OrganizationDomain (2000) + OrganizationDomain_Added: 2000, + OrganizationDomain_Removed: 2001, + OrganizationDomain_Verified: 2002, + OrganizationDomain_NotVerified: 2003, + + // Secret (2100) + Secret_Retrieved: 2100, + Secret_Created: 2101, + Secret_Edited: 2102, + Secret_Deleted: 2103, + Secret_Permanently_Deleted: 2104, + Secret_Restored: 2105, + Secret_UserAccessGranted: 2106, + Secret_UserAccessRevoked: 2107, + Secret_UserAccessUpdated: 2108, + Secret_GroupAccessGranted: 2109, + Secret_GroupAccessRevoked: 2110, + Secret_GroupAccessUpdated: 2111, + Secret_ServiceAccountAccessGranted: 2112, + Secret_ServiceAccountAccessRevoked: 2113, + Secret_ServiceAccountAccessUpdated: 2114, + + // Project (2200) + Project_Retrieved: 2200, + Project_Created: 2201, + Project_Edited: 2202, + Project_Deleted: 2203, + Project_UserAccessGranted: 2204, + Project_UserAccessRevoked: 2205, + Project_UserAccessUpdated: 2206, + Project_GroupAccessGranted: 2207, + Project_GroupAccessRevoked: 2208, + Project_GroupAccessUpdated: 2209, + Project_ServiceAccountAccessGranted: 2210, + Project_ServiceAccountAccessRevoked: 2211, + Project_ServiceAccountAccessUpdated: 2212, + + // ServiceAccount (2300) + ServiceAccount_UserAdded: 2300, + ServiceAccount_UserRemoved: 2301, + ServiceAccount_GroupAdded: 2302, + ServiceAccount_GroupRemoved: 2303, + ServiceAccount_Created: 2304, + ServiceAccount_Deleted: 2305, + ServiceAccount_UserPermissionUpdated: 2306, + ServiceAccount_GroupPermissionUpdated: 2307, +} as const); + +type _EventType = typeof _EventType; + +export type EventType = _EventType[keyof _EventType]; + +// FIXME: Update typing of `EventType` to be `Record` +// which is ADR-0025 compliant when the TypeScript version is at least 5.8. +export const EventType: typeof _EventType = _EventType; + +/** + * Reverse mapping of EventType numeric values to their key names. + * + * When represented as an enum in TypeScript, this reverse mapping was provided + * by default. Now using a constant object it needs to be defined manually. + */ +export const eventTypeNames = Object.freeze( + Object.fromEntries(Object.entries(EventType).map(([key, value]) => [value, key])), +) as Readonly>;