-
Notifications
You must be signed in to change notification settings - Fork 6.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add auditor to coreServices #26372
Conversation
Important This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior. Changed Packages
|
@Rugvip what are your thought on this implementation of the event auditor? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK some initial thoughts, we'll raise it with the team next week too
packages/backend-plugin-api/src/services/definitions/coreServices.ts
Outdated
Show resolved
Hide resolved
packages/backend-plugin-api/src/services/definitions/EventAuditorService.ts
Outdated
Show resolved
Hide resolved
packages/backend-plugin-api/src/services/definitions/EventAuditorService.ts
Outdated
Show resolved
Hide resolved
@freben, do you have any further input on the unresolved comments? Thank you for reviewing! |
@freben PTAL? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! 👍
Wanted to get some early feedback going to not have this sit completely silent, but haven't had a chance for a proper review yet. Just to set expectations I do think we have some work to do to iron out the API, it's still much too early to add a definition for this in the core APIs.
@freben do you know why the PR is failing? |
Ah yeah, sorry the nice message that explains what to do got lost, fix for that here. You were meant to see
Essentially you always need to run |
@freben I've done that a few times and it still doesn't fix the issue. 😢 Let me try again. |
packages/backend-dynamic-feature-service/src/schemas/auditorServiceFactory.ts
Outdated
Show resolved
Hide resolved
@Rugvip PTAL? In the meantime I will add the auditor to the scaffolder and catalog plugins. |
@Rugvip How should I address the type error in the legacy backend package? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another round! 🍻
packages/backend-plugin-api/src/services/definitions/AuditorService.ts
Outdated
Show resolved
Hide resolved
packages/backend-plugin-api/src/services/definitions/AuditorService.ts
Outdated
Show resolved
Hide resolved
packages/backend-plugin-api/src/services/definitions/AuditorService.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! continuing forward with the main bits. I like the new .createEvent
API 👍
packages/backend-plugin-api/src/services/definitions/AuditorService.ts
Outdated
Show resolved
Hide resolved
packages/backend-plugin-api/src/services/definitions/AuditorService.ts
Outdated
Show resolved
Hide resolved
packages/backend-plugin-api/src/services/definitions/AuditorService.ts
Outdated
Show resolved
Hide resolved
@benjdlambert done! |
ab4a3f4
to
3392fd6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright couple more bits to get this ready for merge after release on Tuesday. Main thing is still the default implementation and having it use the logger service by default.
plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts
Outdated
Show resolved
Hide resolved
import type { Request } from 'express'; | ||
import type { mockServices } from './mockServices'; | ||
|
||
export class MockAuditorService implements AuditorService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the default auditor service simply forwarding to the logger service I don't think we need a concrete mock implementation and can use the default implementation instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the auditor
mock is still valuable to retain, as its implementation details don't exactly mirror those of the rootLogger
. Let me know if I’m misunderstanding your point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks pretty much identical to me, except that it doesn't have support for the Winston transport ofc but it's not worth duplicating the code to remove that. I think it should be fine to leave this one without explicit mocks, similar to this and the ones below:
backstage/packages/backend-test-utils/src/next/services/mockServices.ts
Lines 425 to 431 in 5171d5c
export namespace rootHealth { | |
export const factory = () => rootHealthServiceFactory; | |
export const mock = simpleMock(coreServices.rootHealth, () => ({ | |
getLiveness: jest.fn(), | |
getReadiness: jest.fn(), | |
})); | |
} |
12b7f4e
to
09f48ef
Compare
@Rugvip updated! |
3453527
to
e8a5702
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright just a couple of smaller nits but basically good to go. Skip the AuditorServiceCreateEventOptions
comment, that one's intended for the future. If you do want to tackle it please do it in a separate PR.
export type AuditorServiceCreateEventOptions = { | ||
eventId: string; | ||
severityLevel?: AuditorServiceEventSeverityLevel; | ||
request?: Request_2<any, any, any, any, any>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize we likey want a way to supply credentials separately from a request, or essentially provide the equivalent fields of what we'd gather from the request manually. Thinking especially for situations when we use other protocols than plain HTTP. Happy to leave that as a followup though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this PR was initially opened, the implementation included a way to provide credentials directly. However, that approach was updated to fallback onto service that triggered it. For reference, see. Although, let's discuss this more in a follow up.
const impl = new DefaultRootAuditorService( | ||
this.impl.child({}) as WinstonLogger, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this indirection? Could just implement log
internally in DefaultAuditorService
instead right?
I'm thinking it's worth having DefaultAuditorService
just take a log
callback or something like that, receiving a message and meta, as in same signature as .info(...)
but slimmed down to exactly what we need. That way it's actually kind of simple to reimplement this service with any kind of log function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're raising a valid point about simplifying the DefaultAuditorService
by directly implementing the log
method within it and accepting a callback. While seemingly straightforward, this approach would sacrifice crucial functionalities related to context and structured logging.
Currently, the DefaultRootAuditorService
isn't just a wrapper around a Winston logger. It plays a vital role in managing the overall logging context, including consistent formatting, setting the service name, and propagating that context to individual plugin instances. This ensures uniformity across all audit logs, simplifying filtering and analysis.
By creating DefaultAuditorService
instances via forPlugin
, each plugin inherits the root context and can append its own specific details. This hierarchy makes it easy to track events originating from different plugins while maintaining a standardized format. A simple callback wouldn't allow for such structured context propagation. Consider a scenario where we want to filter logs by plugin or include common fields like service name in every log entry. The current design facilitates these use cases without requiring each plugin to re-implement the same logic.
Moreover, allowing plugins to completely customize logging via a callback could lead to inconsistencies and make analysis across plugins difficult. The present structure strikes a balance between flexibility for plugins (through metadata and events) and enforced consistency for the overall system. Therefore, while a callback approach appears simpler, it doesn't address the broader needs of a structured, consistent, and context-aware audit logging system.
let credentials: BackstageCredentials = | ||
await this.auth.getOwnServiceCredentials(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, doesn't feel quite right to default to service credentials here. I think it would be better to leave as undefined tbh. We kinda hide information by doing this, as in we don't know whether these credentials where explicitly passed or if we hit this fallback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the concern, but tracking the actor—even if it’s the fallback—is important because auditors typically require an actor in audit logs. Having more detailed information allows us to trace where an event originated. For example, in the Scaffolder plugin, there are multiple scenarios where credentials (request objects) aren’t explicitly provided, and using the fallback helps us identify the service responsible. Leaving it undefined might result in gaps in the audit trail, which could create challenges when backtracking events.
fields = meta; | ||
} | ||
|
||
this.impl.info(eventId, fields); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking the default message should probably contain a lil bit more information than just the even ID right? Even if it's just something like Audit event ${eventId} ${status}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d prefer to align with Google's format, where the event ID acts as a reference. Additionally, there’s an isAuditorEvent
field to clearly distinguish between standard logs and auditor-specific logs.
Relevant implementation: https://github.com/schultzp2020/backstage/blob/event-auditor/packages/backend-defaults/src/entrypoints/auditor/Auditor.ts#L282-L284
import type { Request } from 'express'; | ||
import type { mockServices } from './mockServices'; | ||
|
||
export class MockAuditorService implements AuditorService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks pretty much identical to me, except that it doesn't have support for the Winston transport ofc but it's not worth duplicating the code to remove that. I think it should be fine to leave this one without explicit mocks, similar to this and the ones below:
backstage/packages/backend-test-utils/src/next/services/mockServices.ts
Lines 425 to 431 in 5171d5c
export namespace rootHealth { | |
export const factory = () => rootHealthServiceFactory; | |
export const mock = simpleMock(coreServices.rootHealth, () => ({ | |
getLiveness: jest.fn(), | |
getReadiness: jest.fn(), | |
})); | |
} |
Signed-off-by: Paul Schultz <pschultz@pobox.com> add auditor service Signed-off-by: Paul Schultz <pschultz@pobox.com> create eventAuditor and rootEventAuditor mockServices Signed-off-by: Paul Schultz <pschultz@pobox.com> run api report Signed-off-by: Paul Schultz <pschultz@pobox.com> rerun api report Signed-off-by: Paul Schultz <pschultz@pobox.com> apply requested changes Signed-off-by: Paul Schultz <pschultz@pobox.com> remove rootAuditor Signed-off-by: Paul Schultz <pschultz@pobox.com> run build:api-reports Signed-off-by: Paul Schultz <pschultz@pobox.com> misc fixes Signed-off-by: Paul Schultz <pschultz@pobox.com> misc fixes Signed-off-by: Paul Schultz <pschultz@pobox.com> add dynamic plugin auditor service factory Signed-off-by: Paul Schultz <pschultz@pobox.com> fix api report Signed-off-by: Paul Schultz <pschultz@pobox.com> rerun api-reports Signed-off-by: Paul Schultz <pschultz@pobox.com> add comments to auditor service Signed-off-by: Paul Schultz <pschultz@pobox.com> update api report Signed-off-by: Paul Schultz <pschultz@pobox.com> update api report Signed-off-by: Paul Schultz <pschultz@pobox.com> run api-reports Signed-off-by: Paul Schultz <pschultz@pobox.com> apply requested changes Signed-off-by: Paul Schultz <pschultz@pobox.com> run api-reports Signed-off-by: Paul Schultz <pschultz@pobox.com> fix tests Signed-off-by: Paul Schultz <pschultz@pobox.com> add auditor to the catalog plugin Signed-off-by: Paul Schultz <pschultz@pobox.com> fix tsc issues Signed-off-by: Paul Schultz <pschultz@pobox.com> update auditor service Signed-off-by: Paul Schultz <pschultz@pobox.com> rename 'args' to 'options' Signed-off-by: Paul Schultz <pschultz@pobox.com> update auditor event shape Signed-off-by: Paul Schultz <pschultz@pobox.com> add error option in auditor event shape Signed-off-by: Paul Schultz <pschultz@pobox.com> rename events Signed-off-by: Paul Schultz <pschultz@pobox.com> demo new api? Signed-off-by: Paul Schultz <pschultz@pobox.com> update service impl Signed-off-by: Paul Schultz <pschultz@pobox.com> update auditor api Signed-off-by: Paul Schultz <pschultz@pobox.com> update auditor api Signed-off-by: Paul Schultz <pschultz@pobox.com> add the auditor service to the scaffolder Signed-off-by: Paul Schultz <pschultz@pobox.com> update tests Signed-off-by: Paul Schultz <pschultz@pobox.com> run api-reports Signed-off-by: Paul Schultz <pschultz@pobox.com> revert config secret enumerator changes Signed-off-by: Paul Schultz <pschultz@pobox.com> clean up auditor api Signed-off-by: Paul Schultz <pschultz@pobox.com> add documentation Signed-off-by: Paul Schultz <pschultz@pobox.com> update auditor Signed-off-by: Paul Schultz <pschultz@pobox.com> add auditor to sign in/out Signed-off-by: Paul Schultz <pschultz@pobox.com> remove dynamic auditor Signed-off-by: Paul Schultz <pschultz@pobox.com> add winston dep back Signed-off-by: Paul Schultz <pschultz@pobox.com> run api-reports Signed-off-by: Paul Schultz <pschultz@pobox.com> run prettier Signed-off-by: Paul Schultz <pschultz@pobox.com> add the auditor service as a default service factory Signed-off-by: Paul Schultz <pschultz@pobox.com> fix eslint errror Signed-off-by: Paul Schultz <pschultz@pobox.com> revert auth changes Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
Co-authored-by: Kashish Mittal <113269381+04kash@users.noreply.github.com> Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
Signed-off-by: Paul Schultz <pschultz@pobox.com>
e8a5702
to
ab02538
Compare
@Rugvip done! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, thank you! 🎉
Gonna ship this for today's release and handle any smaller bits in followups.
Awesome work and thank you for enduring throughout the iteration 😅
Thank you for contributing to Backstage! The changes in this pull request will be part of the |
Hey, I just made a Pull Request!
references: #23950
✔️ Checklist
Signed-off-by
line in the message. (more info)