-
Notifications
You must be signed in to change notification settings - Fork 8.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
[Alerting] Exempt Alerts pre 7.10 from RBAC on their Action execution until updated #75563
Changes from 22 commits
917c17d
e2df4b5
5a7ee01
c564990
6f7f628
b226e32
0714965
6c99cde
f661e32
a184efa
5e58006
b0a5c87
f039444
a58eb4e
c8b8815
2330880
5afd31d
1ef7d8b
2a9694f
2940bb2
e0a6050
a0fa54e
332d14a
696a7cb
b32e62e
b486243
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ import { | |
} from './create_execute_function'; | ||
import { ActionsAuthorization } from './authorization/actions_authorization'; | ||
import { ActionType } from '../common'; | ||
import { shouldLegacyRbacApplyBySource } from './authorization/should_legacy_rbac_apply_by_source'; | ||
|
||
// We are assuming there won't be many actions. This is why we will load | ||
// all the actions in advance and assume the total count to not go over 10000. | ||
|
@@ -298,13 +299,19 @@ export class ActionsClient { | |
public async execute({ | ||
actionId, | ||
params, | ||
source, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice! Looking forward to plumbing this through to the event log, we'll finally be able to track and action execution to an alert, which we can't do today. |
||
}: Omit<ExecuteOptions, 'request'>): Promise<ActionTypeExecutorResult<unknown>> { | ||
await this.authorization.ensureAuthorized('execute'); | ||
return this.actionExecutor.execute({ actionId, params, request: this.request }); | ||
if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) { | ||
await this.authorization.ensureAuthorized('execute'); | ||
} | ||
Comment on lines
+304
to
+306
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was hoping to keep this kind of check limited to the Authorization class itself, but as this authorization will have been initialized prior to us knowing what the source is, that isn't really possible without adding the |
||
return this.actionExecutor.execute({ actionId, params, source, request: this.request }); | ||
} | ||
|
||
public async enqueueExecution(options: EnqueueExecutionOptions): Promise<void> { | ||
await this.authorization.ensureAuthorized('execute'); | ||
const { source } = options; | ||
if (!(await shouldLegacyRbacApplyBySource(this.unsecuredSavedObjectsClient, source))) { | ||
await this.authorization.ensureAuthorized('execute'); | ||
} | ||
return this.executionEnqueuer(this.unsecuredSavedObjectsClient, options); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
import { shouldLegacyRbacApplyBySource } from './should_legacy_rbac_apply_by_source'; | ||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; | ||
import uuid from 'uuid'; | ||
import { asSavedObjectExecutionSource } from '../lib'; | ||
|
||
const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); | ||
|
||
describe(`#shouldLegacyRbacApplyBySource`, () => { | ||
test('should return false if no source is provided', async () => { | ||
expect(await shouldLegacyRbacApplyBySource(unsecuredSavedObjectsClient)).toEqual(false); | ||
}); | ||
|
||
test('should return false if source is not an alert', async () => { | ||
expect( | ||
await shouldLegacyRbacApplyBySource( | ||
unsecuredSavedObjectsClient, | ||
asSavedObjectExecutionSource({ | ||
type: 'action', | ||
id: uuid.v4(), | ||
}) | ||
) | ||
).toEqual(false); | ||
}); | ||
|
||
test('should return false if source alert is not marked as legacy', async () => { | ||
const id = uuid.v4(); | ||
unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id })); | ||
expect( | ||
await shouldLegacyRbacApplyBySource( | ||
unsecuredSavedObjectsClient, | ||
asSavedObjectExecutionSource({ | ||
type: 'alert', | ||
id, | ||
}) | ||
) | ||
).toEqual(false); | ||
}); | ||
|
||
test('should return true if source alert is marked as legacy', async () => { | ||
const id = uuid.v4(); | ||
unsecuredSavedObjectsClient.get.mockResolvedValue( | ||
mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: 'pre-7.10.0' } } }) | ||
); | ||
expect( | ||
await shouldLegacyRbacApplyBySource( | ||
unsecuredSavedObjectsClient, | ||
asSavedObjectExecutionSource({ | ||
type: 'alert', | ||
id, | ||
}) | ||
) | ||
).toEqual(true); | ||
}); | ||
|
||
test('should return false if source alert is marked as modern', async () => { | ||
const id = uuid.v4(); | ||
unsecuredSavedObjectsClient.get.mockResolvedValue( | ||
mockAlert({ id, attributes: { meta: { versionApiKeyLastmodified: '7.10.0' } } }) | ||
); | ||
expect( | ||
await shouldLegacyRbacApplyBySource( | ||
unsecuredSavedObjectsClient, | ||
asSavedObjectExecutionSource({ | ||
type: 'alert', | ||
id, | ||
}) | ||
) | ||
).toEqual(false); | ||
}); | ||
|
||
test('should return false if source alert is marked with a last modified version', async () => { | ||
gmmorris marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const id = uuid.v4(); | ||
unsecuredSavedObjectsClient.get.mockResolvedValue(mockAlert({ id, attributes: { meta: {} } })); | ||
expect( | ||
await shouldLegacyRbacApplyBySource( | ||
unsecuredSavedObjectsClient, | ||
asSavedObjectExecutionSource({ | ||
type: 'alert', | ||
id, | ||
}) | ||
) | ||
).toEqual(false); | ||
}); | ||
}); | ||
|
||
const mockAlert = (overrides: Record<string, unknown> = {}) => ({ | ||
id: '1', | ||
type: 'alert', | ||
attributes: { | ||
consumer: 'myApp', | ||
schedule: { interval: '10s' }, | ||
alertTypeId: 'myType', | ||
enabled: false, | ||
actions: [ | ||
{ | ||
group: 'default', | ||
id: '1', | ||
actionTypeId: '1', | ||
actionRef: '1', | ||
params: { | ||
foo: true, | ||
}, | ||
}, | ||
], | ||
}, | ||
version: '123', | ||
references: [], | ||
...overrides, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { SavedObjectsClientContract } from 'src/core/server'; | ||
import { ActionExecutionSource, isSavedObjectExecutionSource } from '../lib'; | ||
import { ALERT_SAVED_OBJECT_TYPE } from '../saved_objects'; | ||
|
||
const LEGACY_VERSION = 'pre-7.10.0'; | ||
|
||
export async function shouldLegacyRbacApplyBySource( | ||
unsecuredSavedObjectsClient: SavedObjectsClientContract, | ||
executionSource?: ActionExecutionSource<unknown> | ||
): Promise<boolean> { | ||
return isSavedObjectExecutionSource(executionSource) && | ||
executionSource?.source?.type === ALERT_SAVED_OBJECT_TYPE | ||
? ( | ||
await unsecuredSavedObjectsClient.get<{ | ||
meta?: { | ||
versionApiKeyLastmodified?: string; | ||
}; | ||
}>(ALERT_SAVED_OBJECT_TYPE, executionSource.source.id) | ||
).attributes.meta?.versionApiKeyLastmodified === LEGACY_VERSION | ||
: false; | ||
} |
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 spaceId is not in here - presumably not needed today since alerts are space-specific and we will have the space via some other context. I don't think we need to pass it along here, but not completely sure. I think once we start using this value in the event log, we'll definitely need the spaceId, but again I think we can get that from some other context.
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.
Yeah, that's a fair point... the
reference
object doesn't support spaceId at the moment so we wouldn't be able to use it even if we add it in.Once SOs can reference SOs in other spaces, that'll make sense here too.