|
1 | | -# @janus-idp/backstage-plugin-audit-log-node |
| 1 | +# ❗DEPRECATED❗ |
2 | 2 |
|
3 | | -This package contains common types and utility functions for audit logging the backend |
4 | | - |
5 | | -## Installation |
6 | | - |
7 | | -To install this plugin in a package/plugin, run the following command: |
8 | | - |
9 | | -```console |
10 | | -yarn workspace <package/plugin> add @janus-idp/backstage-plugin-audit-log-node |
11 | | -``` |
12 | | - |
13 | | -### Usage |
14 | | - |
15 | | -The audit logging node package contains a helper class for generating audit logs with a common structure, as well as logging them. |
16 | | - |
17 | | -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. |
18 | | - |
19 | | -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. |
20 | | - |
21 | | -The `DefaultAuditLogger.createAuditLogDetails` will generate the `actorId` of the actor with the following priority (highest to lowest): |
22 | | - |
23 | | -- The `actorId` provided in the arguments |
24 | | -- The actor id generated from the `express.Request` object provided in the arguments |
25 | | -- `null` if neither of the above fields were provided in the arguments |
26 | | - |
27 | | -#### Event Naming Convention |
28 | | - |
29 | | -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. |
30 | | - |
31 | | -For example, "ScaffolderTaskRead", "CatalogEntityFetch", etc. |
32 | | - |
33 | | ---- |
34 | | - |
35 | | -**IMPORTANT** |
36 | | - |
37 | | -Any fields containing secrets provided to these helper functions should have secrets redacted or else they will be logged as is. |
38 | | - |
39 | | -For the `DefaultAuditLogger`, these fields would include: |
40 | | - |
41 | | -- The `metadata` field |
42 | | -- The following fields in the `request`: |
43 | | - - `request.body` |
44 | | - - `request.params` |
45 | | - - `request.query` |
46 | | -- The `response.body` field |
47 | | - |
48 | | ---- |
49 | | - |
50 | | -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. |
51 | | - |
52 | | -### Example |
53 | | - |
54 | | -#### Audit Log Example |
55 | | - |
56 | | -In the following example, we add a simple audit log for the `/health` endpoint of a plugin's router to the `debug` log level. |
57 | | - |
58 | | -```ts plugins/test/src/service/router.ts |
59 | | -import { |
60 | | - AuthService, |
61 | | - HttpAuthService, |
62 | | - LoggerService, |
63 | | -} from '@backstage/backend-plugin-api'; |
64 | | -import { Config } from '@backstage/config'; |
65 | | - |
66 | | -/* highlight-add-start */ |
67 | | -import { DefaultAuditLogger } from '@janus-idp/backstage-plugin-audit-log-node'; |
68 | | - |
69 | | -/* highlight-add-end */ |
70 | | - |
71 | | -export interface RouterOptions { |
72 | | - logger: LoggerService; |
73 | | - config: Config; |
74 | | - auth: AuthService; |
75 | | - httpAuth: HttpAuthService; |
76 | | -} |
77 | | - |
78 | | -export async function createRouter( |
79 | | - options: RouterOptions, |
80 | | -): Promise<express.Router> { |
81 | | - const { logger, config, auth, httpAuth } = options; |
82 | | - |
83 | | - /* highlight-add-start */ |
84 | | - const auditLogger = new DefaultAuditLogger({ |
85 | | - logger, |
86 | | - auth, |
87 | | - httpAuth, |
88 | | - }); |
89 | | - /* highlight-add-end */ |
90 | | - |
91 | | - const router = Router(); |
92 | | - router.use(express.json()); |
93 | | - |
94 | | - router.get('/health', async (request, response) => { |
95 | | - logger.info('PONG!'); |
96 | | - response.json({ status: 'ok' }); |
97 | | - |
98 | | - /* highlight-add-start */ |
99 | | - // Note: if `level` is not provided, it defaults to `info` |
100 | | - auditLogger.auditLog({ |
101 | | - eventName: 'HealthEndpointHit', |
102 | | - stage: 'completion', |
103 | | - status: 'succeeded', |
104 | | - level: 'debug', |
105 | | - request, |
106 | | - response: { |
107 | | - status: 200, |
108 | | - body: { status: 'ok' }, |
109 | | - }, |
110 | | - message: `The Health Endpoint was hit by ${await auditLogger.getActorId( |
111 | | - request, |
112 | | - )}`, |
113 | | - }); |
114 | | - /* highlight-add-end */ |
115 | | - }); |
116 | | - |
117 | | - const middleware = MiddlewareFactory.create({ logger, config }); |
118 | | - |
119 | | - router.use(middleware.error()); |
120 | | - return router; |
121 | | -} |
122 | | -``` |
123 | | - |
124 | | -Assuming the `user:default/tester` user hit requested this endpoint, something similar to the following would be outputted if the logger format is JSON: |
125 | | - |
126 | | -```JSON |
127 | | -{"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"} |
128 | | -``` |
129 | | - |
130 | | -#### Audit Log Error Example |
131 | | - |
132 | | -In the following example, we utilize the `auditLog` utility function to generate and output an error log to the `error` log level: |
133 | | - |
134 | | -```ts plugins/test/src/service/router.ts |
135 | | -import { |
136 | | - AuthService, |
137 | | - HttpAuthService, |
138 | | - LoggerService, |
139 | | -} from '@backstage/backend-plugin-api'; |
140 | | - |
141 | | -/* highlight-add-start */ |
142 | | -import { DefaultAuditLogger } from '@janus-idp/backstage-plugin-audit-log-node'; |
143 | | - |
144 | | -/* highlight-add-end */ |
145 | | - |
146 | | -export interface RouterOptions { |
147 | | - logger: LoggerService; |
148 | | - auth: AuthService; |
149 | | - httpAuth: HttpAuthService; |
150 | | -} |
151 | | - |
152 | | -export async function createRouter( |
153 | | - options: RouterOptions, |
154 | | -): Promise<express.Router> { |
155 | | - const { logger, auth, httpAuth } = options; |
156 | | - |
157 | | - /* highlight-add-start */ |
158 | | - const auditLogger = new DefaultAuditLogger({ |
159 | | - logger, |
160 | | - auth, |
161 | | - httpAuth, |
162 | | - }); |
163 | | - /* highlight-add-end */ |
164 | | - |
165 | | - const router = Router(); |
166 | | - router.use(express.json()); |
167 | | - |
168 | | - router.get('/error', async (request, response) => { |
169 | | - try { |
170 | | - const customErr = new Error('Custom Error Occurred'); |
171 | | - customErr.name = 'CustomError'; |
172 | | - |
173 | | - throw customErr; |
174 | | - |
175 | | - response.json({ |
176 | | - status: 'ok', |
177 | | - }); |
178 | | - } catch (err) { |
179 | | - /* highlight-add-start */ |
180 | | - auditLogger.auditLog({ |
181 | | - eventName: 'ErrorEndpointHit', |
182 | | - stage: 'completion', |
183 | | - status: 'failed', |
184 | | - level: 'error', |
185 | | - request, |
186 | | - response: { |
187 | | - status: 501, |
188 | | - body: { |
189 | | - errors: [ |
190 | | - { |
191 | | - name: (err as Error).name, |
192 | | - message: (err as Error).message, |
193 | | - }, |
194 | | - ], |
195 | | - }, |
196 | | - }, |
197 | | - errors: [customErr], |
198 | | - message: `An error occurred when querying the '/errors' endpoint`, |
199 | | - }); |
200 | | - /* highlight-add-end */ |
201 | | - // Do something with the caught error |
202 | | - response.status(501).json({ |
203 | | - errors: [ |
204 | | - { |
205 | | - name: (err as Error).name, |
206 | | - message: (err as Error).message, |
207 | | - }, |
208 | | - ], |
209 | | - }); |
210 | | - } |
211 | | - }); |
212 | | - router.use(errorHandler()); |
213 | | - return router; |
214 | | -} |
215 | | -``` |
216 | | - |
217 | | -An example error audit log would be in the following form: |
218 | | -Note: the stack trace was removed redacted in this example due to its size. |
219 | | - |
220 | | -```JSON |
221 | | -{"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"} |
222 | | -``` |
| 3 | +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. |
0 commit comments