diff --git a/plugins/audit-log-node/.eslintignore b/plugins/audit-log-node/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/audit-log-node/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/audit-log-node/.eslintrc.js b/plugins/audit-log-node/.eslintrc.js deleted file mode 100644 index e2a53a6ad2..0000000000 --- a/plugins/audit-log-node/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/audit-log-node/.lintstagedrc.json b/plugins/audit-log-node/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/audit-log-node/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/audit-log-node/.prettierignore b/plugins/audit-log-node/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/audit-log-node/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/audit-log-node/.prettierrc.js b/plugins/audit-log-node/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/audit-log-node/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/audit-log-node/.versionhistory.md b/plugins/audit-log-node/.versionhistory.md deleted file mode 100644 index 558300db82..0000000000 --- a/plugins/audit-log-node/.versionhistory.md +++ /dev/null @@ -1,2 +0,0 @@ -- Bumped to 1.5.0 in main branch for next release 1.3.0 -- Bumped to 1.8.0 in main branch, in prep for release of 1.4.0 diff --git a/plugins/audit-log-node/CHANGELOG.md b/plugins/audit-log-node/CHANGELOG.md deleted file mode 100644 index 7d70b23ac4..0000000000 --- a/plugins/audit-log-node/CHANGELOG.md +++ /dev/null @@ -1,61 +0,0 @@ -## @janus-idp/backstage-plugin-audit-log-node [1.4.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-audit-log-node@1.3.0...@janus-idp/backstage-plugin-audit-log-node@1.4.0) (2024-07-25) - -## 1.8.1 - -### Patch Changes - -- 837d5d0: bump express to 4.21.2 - -## 1.8.0 - -### Minor Changes - -- 9671df5: Bump plugins/audit-log-node to 1.8.0 in main branch, in prep for release of 1.4.0 - -## 1.7.1 - -### Patch Changes - -- 0e6bfd3: feat: update Backstage to the latest version - - Update to Backstage 1.32.5 - -## 1.7.0 - -### Minor Changes - -- 8244f28: chore(deps): update to backstage 1.32 - -## 1.6.0 - -### Minor Changes - -- d9551ae: feat(deps): update to backstage 1.31 - -### Patch Changes - -- d9551ae: upgrade to yarn v3 - -### Features - -- **deps:** update to backstage 1.29 ([#1900](https://github.com/janus-idp/backstage-plugins/issues/1900)) ([f53677f](https://github.com/janus-idp/backstage-plugins/commit/f53677fb02d6df43a9de98c43a9f101a6db76802)) - -## @janus-idp/backstage-plugin-audit-log-node [1.3.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-audit-log-node@1.2.0...@janus-idp/backstage-plugin-audit-log-node@1.3.0) (2024-07-23) - -### Features - -- **deps:** update to backstage 1.28 ([#1891](https://github.com/janus-idp/backstage-plugins/issues/1891)) ([1ba1108](https://github.com/janus-idp/backstage-plugins/commit/1ba11088e0de60e90d138944267b83600dc446e5)) - -## @janus-idp/backstage-plugin-audit-log-node [1.2.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-audit-log-node@1.1.0...@janus-idp/backstage-plugin-audit-log-node@1.2.0) (2024-06-13) - -### Features - -- **deps:** update to backstage 1.27 ([#1683](https://github.com/janus-idp/backstage-plugins/issues/1683)) ([a14869c](https://github.com/janus-idp/backstage-plugins/commit/a14869c3f4177049cb8d6552b36c3ffd17e7997d)) - -## @janus-idp/backstage-plugin-audit-log-node [1.1.0](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-audit-log-node@1.0.3...@janus-idp/backstage-plugin-audit-log-node@1.1.0) (2024-06-05) - -### Features - -- **rbac:** add type checks with generics for audit log ([#1789](https://github.com/janus-idp/backstage-plugins/issues/1789)) ([ac69838](https://github.com/janus-idp/backstage-plugins/commit/ac698382f64fe91e0f9f9232dd3eecd9cc9247be)) - -## @janus-idp/backstage-plugin-audit-log-node [1.0.3](https://github.com/janus-idp/backstage-plugins/compare/@janus-idp/backstage-plugin-audit-log-node@1.0.2...@janus-idp/backstage-plugin-audit-log-node@1.0.3) (2024-06-04) diff --git a/plugins/audit-log-node/README.md b/plugins/audit-log-node/README.md index dfa8815935..726984fcc6 100644 --- a/plugins/audit-log-node/README.md +++ b/plugins/audit-log-node/README.md @@ -1,222 +1,3 @@ -# @janus-idp/backstage-plugin-audit-log-node +# ❗DEPRECATED❗ -This package contains common types and utility functions for audit logging the backend - -## Installation - -To install this plugin in a package/plugin, run the following command: - -```console -yarn workspace add @janus-idp/backstage-plugin-audit-log-node -``` - -### Usage - -The audit logging node package contains a helper class for generating audit logs with a common structure, as well as logging them. - -The `auditLog` function can be used to log out an audit log using the backstage `LoggerService`. You can provide a log level to the `auditLog` function. The supported levels are: `info`, `debug`, `warn`, and `error`. If no log level is provided, it defaults to the `info` level. - -Alternatively, if you want to generate the audit log object (does not contain message) without it being logged out for you, the `createAuditLogDetails` helper function of the `DefaultAuditLogger` can be used. - -The `DefaultAuditLogger.createAuditLogDetails` will generate the `actorId` of the actor with the following priority (highest to lowest): - -- The `actorId` provided in the arguments -- The actor id generated from the `express.Request` object provided in the arguments -- `null` if neither of the above fields were provided in the arguments - -#### Event Naming Convention - -It is recommended that you prefix the `eventName` value with the name of the component you are audit logging. This will help with searchability in the central log collector. - -For example, "ScaffolderTaskRead", "CatalogEntityFetch", etc. - ---- - -**IMPORTANT** - -Any fields containing secrets provided to these helper functions should have secrets redacted or else they will be logged as is. - -For the `DefaultAuditLogger`, these fields would include: - -- The `metadata` field -- The following fields in the `request`: - - `request.body` - - `request.params` - - `request.query` -- The `response.body` field - ---- - -The `getActorId` helper function grabs the specified entityRef of the user or service associated with the provided credentials in the provided express Request object. If no request is provided or no user/service was associated to the request, `undefined` is returned. - -### Example - -#### Audit Log Example - -In the following example, we add a simple audit log for the `/health` endpoint of a plugin's router to the `debug` log level. - -```ts plugins/test/src/service/router.ts -import { - AuthService, - HttpAuthService, - LoggerService, -} from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; - -/* highlight-add-start */ -import { DefaultAuditLogger } from '@janus-idp/backstage-plugin-audit-log-node'; - -/* highlight-add-end */ - -export interface RouterOptions { - logger: LoggerService; - config: Config; - auth: AuthService; - httpAuth: HttpAuthService; -} - -export async function createRouter( - options: RouterOptions, -): Promise { - const { logger, config, auth, httpAuth } = options; - - /* highlight-add-start */ - const auditLogger = new DefaultAuditLogger({ - logger, - auth, - httpAuth, - }); - /* highlight-add-end */ - - const router = Router(); - router.use(express.json()); - - router.get('/health', async (request, response) => { - logger.info('PONG!'); - response.json({ status: 'ok' }); - - /* highlight-add-start */ - // Note: if `level` is not provided, it defaults to `info` - auditLogger.auditLog({ - eventName: 'HealthEndpointHit', - stage: 'completion', - status: 'succeeded', - level: 'debug', - request, - response: { - status: 200, - body: { status: 'ok' }, - }, - message: `The Health Endpoint was hit by ${await auditLogger.getActorId( - request, - )}`, - }); - /* highlight-add-end */ - }); - - const middleware = MiddlewareFactory.create({ logger, config }); - - router.use(middleware.error()); - return router; -} -``` - -Assuming the `user:default/tester` user hit requested this endpoint, something similar to the following would be outputted if the logger format is JSON: - -```JSON -{"actor":{"actorId":"user:default/tester","hostname":"localhost","ip":"::1","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"},"eventName":"HealthEndpointHit","isAuditLog":true,"level":"debug","message":"The Health Endpoint was hit by user:default/tester","meta":{},"plugin":"test","request":{"body": "","method":"GET","params":{},"query":{},"url":"/api/test/health"},"service":"backstage","stage":"completion","status":"succeeded","timestamp":"2024-05-17 11:17:07","type":"plugin"} -``` - -#### Audit Log Error Example - -In the following example, we utilize the `auditLog` utility function to generate and output an error log to the `error` log level: - -```ts plugins/test/src/service/router.ts -import { - AuthService, - HttpAuthService, - LoggerService, -} from '@backstage/backend-plugin-api'; - -/* highlight-add-start */ -import { DefaultAuditLogger } from '@janus-idp/backstage-plugin-audit-log-node'; - -/* highlight-add-end */ - -export interface RouterOptions { - logger: LoggerService; - auth: AuthService; - httpAuth: HttpAuthService; -} - -export async function createRouter( - options: RouterOptions, -): Promise { - const { logger, auth, httpAuth } = options; - - /* highlight-add-start */ - const auditLogger = new DefaultAuditLogger({ - logger, - auth, - httpAuth, - }); - /* highlight-add-end */ - - const router = Router(); - router.use(express.json()); - - router.get('/error', async (request, response) => { - try { - const customErr = new Error('Custom Error Occurred'); - customErr.name = 'CustomError'; - - throw customErr; - - response.json({ - status: 'ok', - }); - } catch (err) { - /* highlight-add-start */ - auditLogger.auditLog({ - eventName: 'ErrorEndpointHit', - stage: 'completion', - status: 'failed', - level: 'error', - request, - response: { - status: 501, - body: { - errors: [ - { - name: (err as Error).name, - message: (err as Error).message, - }, - ], - }, - }, - errors: [customErr], - message: `An error occurred when querying the '/errors' endpoint`, - }); - /* highlight-add-end */ - // Do something with the caught error - response.status(501).json({ - errors: [ - { - name: (err as Error).name, - message: (err as Error).message, - }, - ], - }); - } - }); - router.use(errorHandler()); - return router; -} -``` - -An example error audit log would be in the following form: -Note: the stack trace was removed redacted in this example due to its size. - -```JSON -{"actor":{"actorId":"user:development/guest","hostname":"localhost","ip":"::1","userAgent":"curl/8.2.1"},"errors":[{"message":"Custom Error Occurred","name":"CustomError","stack":"CustomError: Custom Error Occurred\n at STACK_TRACE]"}],"eventName":"ErrorEndpointHit","isAuditLog":true,"level":"error","message":"An error occurred when querying the '/errors' endpoint","meta":{},"plugin":"test","request":{"body":{},"method":"GET","params":{},"query":{},"url":"/api/test/error"},"response":{"body":{"errors":[{"name":"CustomError","message":"Custom Error Occurred"}]},"status":501},"service":"backstage","stage":"completion","status":"failed","timestamp":"2024-05-23 10:09:04"} -``` +Starting from Backstage v1.36.0, the Auditor Service will be included in Backstage's core services. As a result, this package is now deprecated. \ No newline at end of file diff --git a/plugins/audit-log-node/catalog-info.yaml b/plugins/audit-log-node/catalog-info.yaml deleted file mode 100644 index b2cd5f0260..0000000000 --- a/plugins/audit-log-node/catalog-info.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: janus-idp-audit-log - title: Audit Log plugin - description: Backend for the audit-log - annotations: - backstage.io/source-location: url:https://github.com/janus-idp/backstage-plugins/tree/main/plugins/audit-log-node - backstage.io/view-url: https://github.com/janus-idp/backstage-plugins/blob/main/plugins/audit-log-node/catalog-info.yaml - backstage.io/edit-url: https://github.com/janus-idp/backstage-plugins/edit/main/plugins/audit-log-node/catalog-info.yaml - github.com/project-slug: janus-idp/backstage-plugins - github.com/team-slug: janus-idp/maintainers-plugins - sonarqube.org/project-key: janus-idp_backstage-plugins - links: - - url: https://github.com/janus-idp/backstage-plugins/tree/main/plugins/audit-log-node - title: GitHub Source - icon: source - type: source -spec: - type: backstage-plugin - lifecycle: production - owner: rhdh-team - system: rhdh - subcomponentOf: janus-idp-backstage-plugins ---- -# https://backstage.io/docs/features/software-catalog/descriptor-format#kind-component -apiVersion: backstage.io/v1alpha1 -kind: Component -metadata: - name: janus-idp-audit-log-node - title: '@janus-idp/backstage-plugin-audit-log-node' - description: Node.js library for the audit-log plugin - annotations: - backstage.io/source-location: url:https://github.com/janus-idp/backstage-plugins/tree/main/plugins/audit-log-node - backstage.io/view-url: https://github.com/janus-idp/backstage-plugins/blob/main/plugins/audit-log-node/catalog-info.yaml - backstage.io/edit-url: https://github.com/janus-idp/backstage-plugins/edit/main/plugins/audit-log-node/catalog-info.yaml - github.com/project-slug: janus-idp/backstage-plugins - github.com/team-slug: janus-idp/maintainers-plugins - sonarqube.org/project-key: janus-idp_backstage-plugins - links: - - url: https://github.com/janus-idp/backstage-plugins/tree/main/plugins/audit-log-node - title: GitHub Source - icon: source - type: source -spec: - type: backstage-node-library - lifecycle: production - owner: rhdh-team - system: rhdh - subcomponentOf: janus-idp-audit-log diff --git a/plugins/audit-log-node/package.json b/plugins/audit-log-node/package.json deleted file mode 100644 index 9f6581e2bb..0000000000 --- a/plugins/audit-log-node/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "@janus-idp/backstage-plugin-audit-log-node", - "description": "Node.js library for the audit-log plugin", - "version": "1.8.1", - "main": "src/index.ts", - "types": "src/index.ts", - "license": "Apache-2.0", - "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" - }, - "backstage": { - "role": "node-library", - "supported-versions": "1.32.5", - "pluginId": "audit-log", - "pluginPackage": "@janus-idp/backstage-plugin-audit-log-node", - "pluginPackages": [ - "@janus-idp/backstage-plugin-audit-log-node" - ] - }, - "scripts": { - "build": "backstage-cli package build", - "clean": "backstage-cli package clean", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", - "postpack": "backstage-cli package postpack", - "prepack": "backstage-cli package prepack", - "start": "backstage-cli package start", - "test": "backstage-cli package test --passWithNoTests --coverage", - "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write ." - }, - "devDependencies": { - "@backstage/backend-plugin-api": "1.0.1", - "@backstage/backend-test-utils": "1.0.2", - "@backstage/cli": "0.28.2", - "@backstage/errors": "1.2.4", - "@backstage/types": "1.1.1", - "@types/lodash": "4.17.5", - "express": "4.21.2", - "jest-express": "1.12.0", - "prettier": "3.3.3" - }, - "files": [ - "dist" - ], - "repository": { - "type": "git", - "url": "https://github.com/janus-idp/backstage-plugins", - "directory": "plugins/audit-log-node" - }, - "keywords": [ - "support:production", - "lifecycle:active", - "backstage", - "plugin" - ], - "homepage": "https://red.ht/rhdh", - "bugs": "https://github.com/janus-idp/backstage-plugins/issues", - "dependencies": { - "lodash": "^4.17.21" - }, - "maintainers": [ - "@janus-idp/maintainers-plugins" - ], - "author": "Red Hat" -} diff --git a/plugins/audit-log-node/src/DefaultAuditLogger.test.ts b/plugins/audit-log-node/src/DefaultAuditLogger.test.ts deleted file mode 100644 index 6ac67b2cb7..0000000000 --- a/plugins/audit-log-node/src/DefaultAuditLogger.test.ts +++ /dev/null @@ -1,295 +0,0 @@ -import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; - -import type { Request } from 'express'; -import { Request as JestRequest } from 'jest-express/lib/request'; - -import { DefaultAuditLogger } from './DefaultAuditLogger'; - -describe('DefaultAuditLogger', () => { - const logger = mockServices.logger.mock(); - const loggerSpy: jest.SpyInstance = jest.spyOn(logger, 'info'); - const loggerErrorSpy: jest.SpyInstance = jest.spyOn(logger, 'error'); - - const auditLogger = new DefaultAuditLogger({ - logger, - authService: mockServices.auth({ - pluginId: 'scaffolder', - disableDefaultAuthPolicy: false, - }), - httpAuthService: mockServices.httpAuth({ - pluginId: 'scaffolder', - defaultCredentials: mockCredentials.user(), - }), - }); - - describe('getActorId', () => { - it('Returns nothing if no request is provided', async () => { - const actorId = await auditLogger.getActorId(); - expect(actorId).toBeUndefined(); - }); - it('Returns a user if a request is provided', async () => { - const cred = mockCredentials.user.header('user:default/test'); - const request = new JestRequest(); - request.setHeaders('Authorization', cred); - const actorId = await auditLogger.getActorId( - request as unknown as Request, - ); - - expect(actorId).toEqual('user:default/test'); - }); - it('Returns nothing if request has invalid token', async () => { - const cred = mockCredentials.user.invalidHeader(); - const request = new JestRequest(); - request.setHeaders('Authorization', cred); - const actorId = await auditLogger.getActorId( - request as unknown as Request, - ); - - expect(actorId).toBeUndefined(); - }); - }); - - describe('createAuditLogDetails', () => { - it('Generates an audit log meta object with a null actor if neither actor id or request is provided', async () => { - const auditLogDetails = await auditLogger.createAuditLogDetails({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - }); - expect(auditLogDetails).toEqual({ - actor: { - actorId: null, - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - meta: {}, - isAuditLog: true, - }); - }); - it('Generates an audit log meta object with a specified actor if actor id is provided', async () => { - const auditLogDetails = await auditLogger.createAuditLogDetails({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - actorId: 'user:default/tester', - }); - expect(auditLogDetails).toEqual({ - actor: { - actorId: 'user:default/tester', - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - meta: {}, - isAuditLog: true, - }); - }); - it('Utilizes the provided actor id over the generated one from request', async () => { - const cred = mockCredentials.user.header('user:default/test'); - const request = new JestRequest('/api/endpoint', { - headers: { - Authorization: cred, - 'User-Agent': - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', - }, - }); - request.setIps('::1'); - request.setMethod('GET'); - request.setHostname('localhost'); - - const auditLogDetails = await auditLogger.createAuditLogDetails({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - actorId: 'user:default/tester', - request: request as unknown as Request, - }); - expect(auditLogDetails).toEqual({ - actor: { - actorId: 'user:default/tester', - ip: '::1', - hostname: 'localhost', - userAgent: - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - meta: {}, - isAuditLog: true, - request: { - method: 'GET', - body: '', - params: {}, - query: {}, - url: '/api/endpoint', - }, - }); - }); - it('Generates an audit log meta object with relevant audit information from a request and response', async () => { - const cred = mockCredentials.user.header('user:default/test'); - - const request = new JestRequest( - '/api/endpoint/a-random-id?query=test&other=thing', - { - headers: { - Authorization: cred, - 'User-Agent': - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', - }, - }, - ); - request.setIps('::1'); - request.setMethod('POST'); - request.setParams({ id: 'a-random-id' }); - request.setHostname('localhost'); - request.setBody({ - value: { - greatValue: 'blueberries', - }, - }); - - const response = { - status: 200, - body: { - content: 'wow', - }, - }; - - const auditLogDetails = await auditLogger.createAuditLogDetails({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - request: request as unknown as Request, - response, - metadata: { - test: 'value', - nested: { - inner: 'field', - }, - }, - }); - expect(auditLogDetails).toEqual({ - actor: { - actorId: 'user:default/test', - ip: '::1', - hostname: 'localhost', - userAgent: - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - meta: { - test: 'value', - nested: { - inner: 'field', - }, - }, - isAuditLog: true, - request: { - method: 'POST', - body: { - value: { - greatValue: 'blueberries', - }, - }, - params: { id: 'a-random-id' }, - query: { - query: 'test', - other: 'thing', - }, - url: '/api/endpoint/a-random-id?query=test&other=thing', - }, - response: response, - }); - }); - it('Generates audit logs for errors', async () => { - const customError = new Error( - 'This is a test error, so pay no attention to it', - ); - customError.name = 'TestError'; - - const auditLogDetails = await auditLogger.createAuditLogDetails({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'failed', - actorId: 'user:default/tester', - errors: [customError], - }); - - expect(auditLogDetails).toEqual({ - actor: { - actorId: 'user:default/tester', - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'failed', - errors: [ - { - name: customError.name, - message: customError.message, - stack: customError.stack, - }, - ], - meta: {}, - isAuditLog: true, - }); - }); - }); - - describe('auditLog', () => { - it('Outputs the audit log to the info level of the logger', async () => { - await auditLogger.auditLog({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - actorId: 'user:default/tester', - message: 'Audit Log Triggered', - }); - expect(loggerSpy).toHaveBeenCalledWith('Audit Log Triggered', { - actor: { - actorId: 'user:default/tester', - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'succeeded', - isAuditLog: true, - meta: {}, - }); - }); - it('Outputs the audit error log to the error level of the logger', async () => { - const customError = new Error( - 'This is a test error, so pay no attention to it', - ); - customError.name = 'TestError'; - await auditLogger.auditLog({ - eventName: 'TestAuditLog', - stage: 'completion', - status: 'failed', - level: 'error', - actorId: 'user:default/tester', - errors: [customError], - message: 'Audit Log Triggered', - }); - expect(loggerErrorSpy).toHaveBeenCalledWith('Audit Log Triggered', { - actor: { - actorId: 'user:default/tester', - }, - eventName: 'TestAuditLog', - stage: 'completion', - status: 'failed', - isAuditLog: true, - meta: {}, - errors: [ - { - name: customError.name, - message: customError.message, - stack: customError.stack, - }, - ], - }); - }); - }); -}); diff --git a/plugins/audit-log-node/src/DefaultAuditLogger.ts b/plugins/audit-log-node/src/DefaultAuditLogger.ts deleted file mode 100644 index f3821652f0..0000000000 --- a/plugins/audit-log-node/src/DefaultAuditLogger.ts +++ /dev/null @@ -1,152 +0,0 @@ -import type { - AuthService, - HttpAuthService, - LoggerService, -} from '@backstage/backend-plugin-api'; -import type { ErrorLike } from '@backstage/errors'; -import type { JsonObject, JsonValue } from '@backstage/types'; - -import type { Request } from 'express'; -import { cloneDeep } from 'lodash'; - -import { - ActorDetails, - AuditLogDetails, - AuditLogDetailsOptions, - AuditLogger, - AuditLoggerOptions, - AuditLogOptions, -} from './types'; - -export class DefaultAuditLogger implements AuditLogger { - private readonly logger: LoggerService; - private readonly authService: AuthService; - private readonly httpAuthService: HttpAuthService; - - constructor(options: AuditLoggerOptions) { - this.logger = options.logger; - this.authService = options.authService; - this.httpAuthService = options.httpAuthService; - } - - async getActorId(request?: Request): Promise { - if (!(request && this.httpAuthService && this.authService)) { - return undefined; - } - try { - const credentials = await this.httpAuthService.credentials(request); - const userEntityRef = this.authService.isPrincipal(credentials, 'user') - ? credentials.principal.userEntityRef - : undefined; - - const serviceEntityRef = this.authService.isPrincipal( - credentials, - 'service', - ) - ? credentials.principal.subject - : undefined; - - return userEntityRef ?? serviceEntityRef; - } catch { - return undefined; - } - } - async createAuditLogDetails( - options: AuditLogDetailsOptions, - ): Promise { - const { eventName, stage, metadata, request, response, status } = options; - - const actorId = options.actorId || (await this.getActorId(request)) || null; - - // Secrets in the body field should be redacted by the user before passing in the request object - const auditRequest = request - ? { - method: request.method, - url: request.originalUrl, - params: cloneDeep(request.params), - query: cloneDeep(request.query), - body: cloneDeep(request.body), - } - : undefined; - - const actor: ActorDetails = { actorId }; - if (request) { - actor.ip = request.ip; - actor.hostname = request.hostname; - actor.userAgent = request.get('user-agent'); - } - - const auditLogCommonDetails = { - actor: cloneDeep(actor), - meta: cloneDeep(metadata) || {}, - request: auditRequest, - isAuditLog: true as const, - response: cloneDeep(response), - eventName, - stage, - }; - - if (status === 'failed') { - const errs = cloneDeep(options.errors) as ErrorLike[]; - return { - ...auditLogCommonDetails, - status, - errors: errs.map(err => { - return { - name: err.name, - message: err.message, - stack: err.stack, - }; - }), - }; - } - - return { - ...auditLogCommonDetails, - status, - }; - } - async auditLog( - options: AuditLogOptions, - ): Promise { - let auditLogDetails: AuditLogDetails; - const logLevel = options.level || 'info'; - const auditLogCommonDetails = { - eventName: options.eventName, - stage: options.stage, - actorId: options.actorId, - request: options.request, - response: options.response, - metadata: options.metadata, - }; - if (options.status === 'failed') { - auditLogDetails = await this.createAuditLogDetails({ - ...auditLogCommonDetails, - status: options.status, - errors: options.errors, - }); - } else { - auditLogDetails = await this.createAuditLogDetails({ - ...auditLogCommonDetails, - status: options.status, - }); - } - - switch (logLevel) { - case 'info': - this.logger.info(options.message, auditLogDetails as JsonObject); - return; - case 'debug': - this.logger.debug(options.message, auditLogDetails as JsonObject); - return; - case 'warn': - this.logger.warn(options.message, auditLogDetails as JsonObject); - return; - case 'error': - this.logger.error(options.message, auditLogDetails as JsonObject); - return; - default: - throw new Error(`Log level of ${logLevel} is not supported`); - } - } -} diff --git a/plugins/audit-log-node/src/index.ts b/plugins/audit-log-node/src/index.ts deleted file mode 100644 index ea04300032..0000000000 --- a/plugins/audit-log-node/src/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/***/ -/** - * Node.js library for common audit logging types and functions - * - * @packageDocumentation - */ - -/** - * Default Audit Logger for constructing audit logs - * @public - */ -export * from './DefaultAuditLogger'; -export * from './types'; diff --git a/plugins/audit-log-node/src/setupTests.ts b/plugins/audit-log-node/src/setupTests.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/plugins/audit-log-node/src/setupTests.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/plugins/audit-log-node/src/types.ts b/plugins/audit-log-node/src/types.ts deleted file mode 100644 index 22e9c6b264..0000000000 --- a/plugins/audit-log-node/src/types.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { - AuthService, - HttpAuthService, - LoggerService, -} from '@backstage/backend-plugin-api'; -import type { ErrorLike } from '@backstage/errors'; -import type { JsonValue } from '@backstage/types'; - -import type { Request } from 'express'; - -export type ActorDetails = { - actorId: string | null; - ip?: string; - hostname?: string; - userAgent?: string; -}; - -export type AuditRequest = { - body: any; - url: string; - method: string; - params?: any; - query?: any; -}; - -export type AuditResponse = { - status: number; - body?: any; -}; -export type AuditLogSuccessStatus = { status: 'succeeded' }; -export type AuditLogFailureStatus = { - status: 'failed'; - errors: ErrorLike[]; -}; -export type AuditLogUnknownFailureStatus = { - status: 'failed'; - errors: unknown[]; -}; - -export type AuditLogStatus = AuditLogSuccessStatus | AuditLogFailureStatus; - -/** - * Common fields of an audit log. Note: timestamp and pluginId are automatically added at log creation. - * - * @public - */ -export type AuditLogDetails = { - actor: ActorDetails; - eventName: string; - stage: string; - request?: AuditRequest; - response?: AuditResponse; - meta: JsonValue; - isAuditLog: true; -} & AuditLogStatus; - -export type AuditLogDetailsOptions = { - eventName: string; - stage: string; - metadata?: T; - response?: AuditResponse; - actorId?: string; - request?: Request; -} & (AuditLogSuccessStatus | AuditLogUnknownFailureStatus); - -export type AuditLogOptions = { - eventName: string; - message: string; - stage: string; - level?: 'info' | 'debug' | 'warn' | 'error'; - actorId?: string; - metadata?: T; - response?: AuditResponse; - request?: Request; -} & (AuditLogSuccessStatus | AuditLogUnknownFailureStatus); - -export type AuditLoggerOptions = { - logger: LoggerService; - authService: AuthService; - httpAuthService: HttpAuthService; -}; - -export interface AuditLogger { - /** - * Processes an express request and obtains the actorId from it. Returns undefined if actorId is not obtainable. - * - * @public - */ - getActorId(request?: Request): Promise; - - /** - * Generates the audit log details to place in the metadata argument of the logger - * - * Secrets in the metadata field and request body, params and query field should be redacted by the user before passing in the request object - * @public - */ - createAuditLogDetails( - options: AuditLogDetailsOptions, - ): Promise; - - /** - * Generates an Audit Log and logs it at the level passed by the user. - * Supports `info`, `debug`, `warn` or `error` level. Defaults to `info` if no level is passed. - * - * Secrets in the metadata field and request body, params and query field should be redacted by the user before passing in the request object - * @public - */ - auditLog(options: AuditLogOptions): Promise; -} diff --git a/plugins/audit-log-node/tsconfig.json b/plugins/audit-log-node/tsconfig.json deleted file mode 100644 index 9213e846e0..0000000000 --- a/plugins/audit-log-node/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "../../dist-types/plugins/audit-log-node", - "rootDir": "." - } -} diff --git a/plugins/audit-log-node/turbo.json b/plugins/audit-log-node/turbo.json deleted file mode 100644 index 21e5295a89..0000000000 --- a/plugins/audit-log-node/turbo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "tsc": { - "outputs": ["../../dist-types/plugins/audit-log-node/**"] - } - } -} diff --git a/yarn.lock b/yarn.lock index c4b5f2c5e0..b94263ba8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3021,7 +3021,7 @@ __metadata: languageName: node linkType: hard -"@backstage/backend-plugin-api@npm:1.0.1, @backstage/backend-plugin-api@npm:^1.0.0, @backstage/backend-plugin-api@npm:^1.0.1": +"@backstage/backend-plugin-api@npm:^1.0.0, @backstage/backend-plugin-api@npm:^1.0.1": version: 1.0.1 resolution: "@backstage/backend-plugin-api@npm:1.0.1" dependencies: @@ -3459,7 +3459,7 @@ __metadata: languageName: node linkType: hard -"@backstage/errors@npm:1.2.4, @backstage/errors@npm:^1.2.4": +"@backstage/errors@npm:^1.2.4": version: 1.2.4 resolution: "@backstage/errors@npm:1.2.4" dependencies: @@ -5362,7 +5362,7 @@ __metadata: languageName: node linkType: hard -"@backstage/types@npm:1.1.1, @backstage/types@npm:^1.1.1": +"@backstage/types@npm:^1.1.1": version: 1.1.1 resolution: "@backstage/types@npm:1.1.1" checksum: 54bd9e53570cf2a7a8d9ae30e7181ee6b669b7f543949391a2168f616e1f7b13f0419f324941a87aa15f723d0313eda8f212db2077675421d6f91484f477c4f5 @@ -7602,22 +7602,14 @@ __metadata: languageName: unknown linkType: soft -"@janus-idp/backstage-plugin-audit-log-node@^1.7.1, @janus-idp/backstage-plugin-audit-log-node@workspace:plugins/audit-log-node": - version: 0.0.0-use.local - resolution: "@janus-idp/backstage-plugin-audit-log-node@workspace:plugins/audit-log-node" +"@janus-idp/backstage-plugin-audit-log-node@npm:^1.7.1": + version: 1.8.1 + resolution: "@janus-idp/backstage-plugin-audit-log-node@npm:1.8.1" dependencies: - "@backstage/backend-plugin-api": 1.0.1 - "@backstage/backend-test-utils": 1.0.2 - "@backstage/cli": 0.28.2 - "@backstage/errors": 1.2.4 - "@backstage/types": 1.1.1 - "@types/lodash": 4.17.5 - express: 4.21.2 - jest-express: 1.12.0 lodash: ^4.17.21 - prettier: 3.3.3 - languageName: unknown - linkType: soft + checksum: 56e854c94663e14c963dbf43564fe1d94f8b55e827aa6b962a5740ad41b1cc100fb729f91245546f305cd9189127426d4c054fa80bbd92bfd5ec191bc73687ee + languageName: node + linkType: hard "@janus-idp/backstage-plugin-rbac-backend@npm:*": version: 5.2.2 @@ -15300,13 +15292,6 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:4.17.5": - version: 4.17.5 - resolution: "@types/lodash@npm:4.17.5" - checksum: 3c9bb15772509f0ecb40428531863dbc3f064f2bf34bbccc2ce2b2923c69fb0868aec7e357b1d97fd0d7f7e435a014ea5c1adef8a64715529887179c97a5a823 - languageName: node - linkType: hard - "@types/lodash@npm:^4.14.167, @types/lodash@npm:^4.14.175": version: 4.17.12 resolution: "@types/lodash@npm:4.17.12" @@ -22643,7 +22628,7 @@ __metadata: languageName: node linkType: hard -"express@npm:4.21.2, express@npm:^4.14.0, express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.2, express@npm:^4.19.2": +"express@npm:^4.14.0, express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.2, express@npm:^4.19.2": version: 4.21.2 resolution: "express@npm:4.21.2" dependencies: @@ -26291,15 +26276,6 @@ __metadata: languageName: node linkType: hard -"jest-express@npm:1.12.0": - version: 1.12.0 - resolution: "jest-express@npm:1.12.0" - dependencies: - jest-node-http: 1.0.0 - checksum: 55de11d258f06778d8928c9b2ecf838646837c6e6df97c55d179b6b678e3c76beba314bdaf42b174b203b0163c7fe8b9b652160dd007a6c0145c84a803d9f94f - languageName: node - linkType: hard - "jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" @@ -26390,13 +26366,6 @@ __metadata: languageName: node linkType: hard -"jest-node-http@npm:1.0.0": - version: 1.0.0 - resolution: "jest-node-http@npm:1.0.0" - checksum: f8e12aa5717c1c7a5202878a20fa98fd26ff4db60ff9ea74e0cd3d4d2767f5e44b200c30ba2a26ea95b526f25ecb88ade102fc128338789b5c5ca59546cd759c - languageName: node - linkType: hard - "jest-pnp-resolver@npm:^1.2.2": version: 1.2.3 resolution: "jest-pnp-resolver@npm:1.2.3"