-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[RAC] [RBAC] Adds bulk update route to rule registry and bulk update function to alerts client #106297
Conversation
0a42280
to
e08eb76
Compare
2e993d2
to
8860188
Compare
Pinging @elastic/apm-ui (Team:apm) |
@elasticmachine merge upstream |
96d3749
to
02b2f98
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.
LGTM
x-pack/plugins/alerting/server/authorization/alerting_authorization.ts
Outdated
Show resolved
Hide resolved
x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx
Outdated
Show resolved
Hide resolved
@elasticmachine merge upstream |
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.
Alerting changes LGTM with a small suggestion.
@@ -285,7 +286,7 @@ export class AlertingAuthorization { | |||
if (this.authorization && this.shouldCheckAuthorization()) { | |||
const { username, authorizedRuleTypes } = await this.augmentRuleTypesWithAuthorization( | |||
this.ruleTypeRegistry.list(), | |||
[ReadOperations.Find], | |||
[operation == null ? ReadOperations.Find : operation], |
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.
If you are changing the name to getAuthorizationFilter
(which makes sense to me), I would make the parameter operation
non-optional and not default to ReadOperations.Find
. Maybe maintain backwards compatibility by creating a getFindAuthorizationFilter
fn that calls this fn and passes in ReadOperations.Find
?
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.
Awesome will do! Thanks for the review @ymao1
@@ -52,11 +55,26 @@ export interface UpdateOptions<Params extends AlertTypeParams> { | |||
index: string; | |||
} | |||
|
|||
export interface BulkUpdateOptions<Params extends AlertTypeParams> { | |||
ids: string[] | undefined | null; | |||
status: 'open' | 'closed'; |
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 know there's been a lot of discussion around what statuses can be. Haven't followed, but might want to double check that these are indeed the terms we're going with and maybe create the type for it to be shared?
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.
The alerts as data spreadsheet defines it as Must be one of {open, acknowledged, closed}.
so I'll create an exported common type (maybe in the rule registry package where the technical terms are defined) and update that to include acknowledged
good catch!
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.
LGTM! Left some nits, but looks great. Thanks for all the work here!
}); | ||
return bulkUpdateResponse; | ||
} catch (exc) { | ||
this.logger.error(`error in fetchAlertAuthzAlertOperateAlert ${exc}`); |
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.
Is this supposed to be:
this.logger.error(`error in fetchAlertAuthzAlertOperateAlert ${exc}`); | |
this.logger.error(`error in fetchAlertAuditOperate ${exc}`); |
return; | ||
} | ||
|
||
const config: EsQueryConfig = { |
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.
Do we want to pull this out? Think we're using it in a couple places.
|
||
return alert; | ||
return alert.hits.hits[0]._source; |
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.
Should we add a to do to remind us that we have to follow up to remove us off of _source
?
@@ -128,12 +130,16 @@ describe('createLifecycleExecutor', () => { | |||
{ | |||
fields: { | |||
[ALERT_ID]: 'TEST_ALERT_0', | |||
[OWNER]: 'CONSUMER', |
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.
Should we update these to match so we no longer use OWNER
and instead CONSUMER
?
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.
renaming this field should happen in another PR, but yes we do want to change OWNER
to CONSUMER
.
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.
fyi: #107857
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.
For audit logging, we have two basic types of operations, write (update, delete, create) and read (get, find).
For the saved objects client, today we have these four paths:
Read operations success path:
- Check authorization
- Fetch object
- Add audit event success
- Return result
Read operations failure path:
- Check authorization
- Add audit event failure
- Return 403 error
Write operation success path:
- Check authorization
- Add audit event w/ outcome "unknown"
- Create/update/delete object
- Return result
Write operation failure path:
- Check authorization
- Add audit event w/ outcome "unknown"
- Create/update/delete object
- Return result
For consistency, I'd like for the alerts client to hew to these paths as much as possible.
It appears this PR is drafty/unfinished, which is fine, but it's very hard to review without unit tests in place. I went ahead and made a best effort at a first pass, but I'd like to ask that you add unit tests for the different positive/negative audit event scenarios. I would be happy to re-review at that point.
x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts
Outdated
Show resolved
Hide resolved
x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts
Outdated
Show resolved
Hide resolved
x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts
Outdated
Show resolved
Hide resolved
x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts
Outdated
Show resolved
Hide resolved
4036ac2
to
774c3ca
Compare
db2453b
to
4dc9a29
Compare
more WIP I think this is working now getting there.. working integration tests fixes type check errors fix jest somehow this was deleted after rebasing... fix early rejection error ids array XOR query string in bulk update route if ids, use elasticsearch bulk update method otherwise use update by query fix tests refresh true fix tests general cleanup fixes apm jest test fixes jest tests code cleanup and fixed jest tests WIP - trying to fix integration tests after refactor fix tests, throw appropriate HTTP errors in alerts client fix issues after rebasing with master updates alerts client docs general cleanup fix trial integration tests possibly fixes loader issue in package updates getSafeSortIds param type to include 'null' in param type, renames getFindAuthorizationFilter -> getAuthorizationFilter and renames usage elsewhere, utilizes const for test in apm make 'operation' parameter non-optional in getAuthorizationFilter and retain backwards compatibility by pointing the getFindAuthorizationFilter to getAuthorizationFilter and operation = ReadOperations.Find adds an exported type to be used when setting a status on an alert moves es query config out of client definition and into a function to allow for customized configurations update by query should check for siem signals status field as well return 200 when no alerts are found, not a 401 error, updates integration tests with stricter expects updates alerts clients docs fixing merge with master conflicts adds missing required space ids field to mocked alerts to fix failing apm jest tests implements suggested changes with auditing failures in bulk update by ids function, removes audit logging in catch blocks, cleans up other jest tests execute ensure authorized in the search after loop, adds jest tests to make sure we log an error to the audit log for the case when ensureAuthorized throws an authorization error
…tespace char to inline snapshots
77f9413
to
8dd966f
Compare
...lib/detection_engine/rule_execution_log/rule_registry_log_client/rule_execution_field_map.ts
Show resolved
Hide resolved
expect(auditLogger.log.mock.calls).toHaveLength(2); | ||
expect(auditLogger.log.mock.calls[0][0]).toEqual({ | ||
message: `Failed attempt to update alert [id=${unsuccessfulAuthzHit}]`, | ||
event: { | ||
action: 'alert_update', | ||
category: ['database'], | ||
outcome: 'failure', | ||
type: ['change'], | ||
}, | ||
error: { | ||
code: 'Error', | ||
message: 'Unauthorized for fake.rule and apm', | ||
}, | ||
}); | ||
expect(auditLogger.log.mock.calls[1][0]).toEqual({ | ||
message: `Failed attempt to update alert [id=${successfulAuthzHit}]`, | ||
event: { | ||
action: 'alert_update', | ||
category: ['database'], | ||
outcome: 'failure', | ||
type: ['change'], | ||
}, | ||
error: { | ||
code: 'Error', | ||
message: 'Unauthorized for fake.rule and apm', | ||
}, | ||
}); |
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.
Optional nit: Jest provides specific functions to deal with these situations, e.g.,
expect(auditLogger.log).toHaveBeenCalledTimes(2);
expect(auditLogger.log).toHaveBeenNthCalledWith(
1,
{...}
);
expect(auditLogger.log).toHaveBeenNthCalledWith(
2,
{...}
);
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.
Thanks! Fixed here: ba1857e
|
||
expect(auditLogger.log).toHaveBeenCalledWith({ | ||
error: undefined, | ||
event: { action: 'alert_get', category: ['database'], outcome: 'success', type: ['access'] }, | ||
message: 'User has accessed alert [id=1]', | ||
event: { action: 'alert_get', category: ['database'], outcome: 'unknown', type: ['access'] }, |
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.
get
should never have outcome: 'unknown'
; we know the outcome (we fetched the object and returned it to the user, this is a 'success'
outcome).
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.
Ah! Yes thank you. Good catch.
Fixed here: 36838ab
event: { action: 'alert_get', category: ['database'], outcome: 'success', type: ['access'] }, | ||
message: 'User has accessed alert [id=1]', | ||
event: { action: 'alert_get', category: ['database'], outcome: 'unknown', type: ['access'] }, | ||
message: 'User is accessing alert [id=NoxgpHkBqbdrfX07MqXV]', |
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 have a test case for a get()
that results in a successful audit event; what about an audit error when the user is unauthorized?
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.
Yes I think I actually removed the test case because with the get
we were relying on a search executed with the authorization filter from the alerting framework to only give us back the data we were authorized to view, so there would be no authorization failure because we never attempted to access it in the first place.
I changed this so we are not solely rely on the authorization filter. We also call into ensureAuthorized
on each search result (+ utilizing the authorization filter) so adding in an audit failure test makes sense again.
Fixed here: 36838ab
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.
Sorry I think I fat-fingered this and didn't submit my entire review at once!
I think a couple minor things need to be changed and hopefully a few more test cases added but otherwise this is looking great. Thanks for all of the changes so far!
@@ -181,33 +187,26 @@ describe('update()', () => { | |||
outcome: 'unknown', | |||
type: ['change'], | |||
}, | |||
message: 'User is updating alert [id=1]', | |||
message: 'User is updating alert [id=NoxgpHkBqbdrfX07MqXV]', |
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 have a test case for an update()
that results in a successful audit event; what about an audit error when the user is unauthorized?
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.
Yup very true. Added an audit failure test here: 36838ab
alertAuditEvent({ | ||
action: operationAlertAuditActionMap[operation], | ||
id: item._id, | ||
outcome: 'unknown', |
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.
As mentioned in my other comment on the unit test: the outcome should only be 'unknown' if the action is an update.
If outcome
is omitted and error
is not present, the audit event is written as a a successful event (outcome: 'success'
)
outcome: 'unknown', | |
...(operation === WriteOperations.Update ? { outcome: 'unknown' } : {}), |
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.
Fixed here: 36838ab
alertAuditEvent({ | ||
action: operationAlertAuditActionMap[operation], | ||
id, | ||
...(operation === WriteOperations.Update ? { outcome: 'unknown' } : { operation }), |
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 don't need to pass operation
in if this is not a write operation:
...(operation === WriteOperations.Update ? { outcome: 'unknown' } : { operation }), | |
...(operation === WriteOperations.Update ? { outcome: 'unknown' } : {}), |
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.
Cleaned this up here: 36838ab
…on, adds jest tests for when user is unauthorized to access given alert
💚 Build Succeeded
Metrics [docs]Async chunks
Page load bundle
Unknown metric groupsAPI count
API count missing comments
References to deprecated APIs
History
To update your PR or re-run it, just comment with: cc @dhurley14 |
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.
Great stuff, thanks again for being so responsive to feedback!
…function to alerts client (elastic#106297) Adds a bulk update route (POST /internal/rac/alerts/bulk_update) to the rule registry and bulkUpdate function to the alerts as data client.
💚 Backport successful
This backport PR will be merged automatically after passing CI. |
Summary
Adds a bulk update route (
POST /internal/rac/alerts/bulk_update
) to the rule registry andbulkUpdate
function to the alerts as data client.To be used here: #107141
To test...
./apm-server -e
) then start simple express server above.alerts-observability-apm
x-pack/plugins/rule_registry/server/scripts/bulk_update_observability_alert_by_query.sh
200
Checklist
Delete any items that are not applicable to this PR.
Risk Matrix
Delete this section if it is not applicable to this PR.
Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release.
When forming the risk matrix, consider some of the following examples and how they may potentially impact the change:
For maintainers