Skip to content

Commit

Permalink
ECS audit events for alerting (#84113) (#85093)
Browse files Browse the repository at this point in the history
* ECS audit events for alerts plugin

* added api changes

* fixed linting and testing errors

* fix test

* Fixed linting errors after prettier update

* Revert "Allow predefined ids for encrypted saved objects (#83482)"

This reverts commit 7d929fe.

* Added suggestions from code review

* Fixed unit tests

* Added suggestions from code review

* Changed names of alert events

* Changed naming as suggested in code review

* Added suggestions from PR

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
thomheymann and kibanamachine committed Dec 5, 2020
1 parent 3519a5e commit 9ef4d22
Show file tree
Hide file tree
Showing 48 changed files with 2,362 additions and 556 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Given a saved object type and id, generates the compound id that is stored in th
<b>Signature:</b>

```typescript
generateRawId(namespace: string | undefined, type: string, id?: string): string;
generateRawId(namespace: string | undefined, type: string, id: string): string;
```

## Parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) &gt; [generateId](./kibana-plugin-core-server.savedobjectsutils.generateid.md)

## SavedObjectsUtils.generateId() method

Generates a random ID for a saved objects.

<b>Signature:</b>

```typescript
static generateId(): string;
```
<b>Returns:</b>

`string`

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) &gt; [isRandomId](./kibana-plugin-core-server.savedobjectsutils.israndomid.md)

## SavedObjectsUtils.isRandomId() method

Validates that a saved object ID matches UUID format.

<b>Signature:</b>

```typescript
static isRandomId(id: string | undefined): boolean;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| id | <code>string &#124; undefined</code> | |

<b>Returns:</b>

`boolean`

Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,10 @@ export declare class SavedObjectsUtils
| [namespaceIdToString](./kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md) | <code>static</code> | <code>(namespace?: string &#124; undefined) =&gt; string</code> | Converts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with the exception of the <code>undefined</code> namespace ID (which has a namespace string of <code>'default'</code>). |
| [namespaceStringToId](./kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md) | <code>static</code> | <code>(namespace: string) =&gt; string &#124; undefined</code> | Converts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with the exception of the <code>'default'</code> namespace string (which has a namespace ID of <code>undefined</code>). |

## Methods

| Method | Modifiers | Description |
| --- | --- | --- |
| [generateId()](./kibana-plugin-core-server.savedobjectsutils.generateid.md) | <code>static</code> | Generates a random ID for a saved objects. |
| [isRandomId(id)](./kibana-plugin-core-server.savedobjectsutils.israndomid.md) | <code>static</code> | Validates that a saved object ID matches UUID format. |

68 changes: 68 additions & 0 deletions docs/user/security/audit-logging.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ Refer to the corresponding {es} logs for potential write errors.
| `unknown` | User is creating a saved object.
| `failure` | User is not authorized to create a saved object.

.2+| `connector_create`
| `unknown` | User is creating a connector.
| `failure` | User is not authorized to create a connector.

.2+| `alert_create`
| `unknown` | User is creating an alert rule.
| `failure` | User is not authorized to create an alert rule.


3+a|
====== Type: change
Expand All @@ -108,6 +116,42 @@ Refer to the corresponding {es} logs for potential write errors.
| `unknown` | User is removing references to a saved object.
| `failure` | User is not authorized to remove references to a saved object.

.2+| `connector_update`
| `unknown` | User is updating a connector.
| `failure` | User is not authorized to update a connector.

.2+| `alert_update`
| `unknown` | User is updating an alert rule.
| `failure` | User is not authorized to update an alert rule.

.2+| `alert_update_api_key`
| `unknown` | User is updating the API key of an alert rule.
| `failure` | User is not authorized to update the API key of an alert rule.

.2+| `alert_enable`
| `unknown` | User is enabling an alert rule.
| `failure` | User is not authorized to enable an alert rule.

.2+| `alert_disable`
| `unknown` | User is disabling an alert rule.
| `failure` | User is not authorized to disable an alert rule.

.2+| `alert_mute`
| `unknown` | User is muting an alert rule.
| `failure` | User is not authorized to mute an alert rule.

.2+| `alert_unmute`
| `unknown` | User is unmuting an alert rule.
| `failure` | User is not authorized to unmute an alert rule.

.2+| `alert_instance_mute`
| `unknown` | User is muting an alert instance.
| `failure` | User is not authorized to mute an alert instance.

.2+| `alert_instance_unmute`
| `unknown` | User is unmuting an alert instance.
| `failure` | User is not authorized to unmute an alert instance.


3+a|
====== Type: deletion
Expand All @@ -120,6 +164,14 @@ Refer to the corresponding {es} logs for potential write errors.
| `unknown` | User is deleting a saved object.
| `failure` | User is not authorized to delete a saved object.

.2+| `connector_delete`
| `unknown` | User is deleting a connector.
| `failure` | User is not authorized to delete a connector.

.2+| `alert_delete`
| `unknown` | User is deleting an alert rule.
| `failure` | User is not authorized to delete an alert rule.

3+a|
====== Type: access

Expand All @@ -135,6 +187,22 @@ Refer to the corresponding {es} logs for potential write errors.
| `success` | User has accessed a saved object as part of a search operation.
| `failure` | User is not authorized to search for saved objects.

.2+| `connector_get`
| `success` | User has accessed a connector.
| `failure` | User is not authorized to access a connector.

.2+| `connector_find`
| `success` | User has accessed a connector as part of a search operation.
| `failure` | User is not authorized to search for connectors.

.2+| `alert_get`
| `success` | User has accessed an alert rule.
| `failure` | User is not authorized to access an alert rule.

.2+| `alert_find`
| `success` | User has accessed an alert rule as part of a search operation.
| `failure` | User is not authorized to search for alert rules.


3+a|
===== Category: web
Expand Down
133 changes: 1 addition & 132 deletions src/core/server/saved_objects/serialization/serializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,24 +573,10 @@ describe('#savedObjectToRaw', () => {
});

describe('single-namespace type without a namespace', () => {
test('generates an id prefixed with type, if no id is specified', () => {
const v1 = singleNamespaceSerializer.savedObjectToRaw({
type: 'foo',
attributes: { bar: true },
} as any);

const v2 = singleNamespaceSerializer.savedObjectToRaw({
type: 'foo',
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`doesn't specify _source.namespace`, () => {
const actual = singleNamespaceSerializer.savedObjectToRaw({
type: '',
id: 'mock-saved-object-id',
attributes: {},
} as any);

Expand All @@ -599,23 +585,6 @@ describe('#savedObjectToRaw', () => {
});

describe('single-namespace type with a namespace', () => {
test('generates an id prefixed with namespace and type, if no id is specified', () => {
const v1 = singleNamespaceSerializer.savedObjectToRaw({
type: 'foo',
namespace: 'bar',
attributes: { bar: true },
} as any);

const v2 = singleNamespaceSerializer.savedObjectToRaw({
type: 'foo',
namespace: 'bar',
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^bar\:foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`it copies namespace to _source.namespace`, () => {
const actual = singleNamespaceSerializer.savedObjectToRaw({
type: 'foo',
Expand All @@ -628,23 +597,6 @@ describe('#savedObjectToRaw', () => {
});

describe('single-namespace type with namespaces', () => {
test('generates an id prefixed with type, if no id is specified', () => {
const v1 = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
namespaces: ['bar'],
attributes: { bar: true },
} as any);

const v2 = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
namespaces: ['bar'],
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`doesn't specify _source.namespaces`, () => {
const actual = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
Expand All @@ -657,23 +609,6 @@ describe('#savedObjectToRaw', () => {
});

describe('namespace-agnostic type with a namespace', () => {
test('generates an id prefixed with type, if no id is specified', () => {
const v1 = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
namespace: 'bar',
attributes: { bar: true },
} as any);

const v2 = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
namespace: 'bar',
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`doesn't specify _source.namespace`, () => {
const actual = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
Expand All @@ -686,23 +621,6 @@ describe('#savedObjectToRaw', () => {
});

describe('namespace-agnostic type with namespaces', () => {
test('generates an id prefixed with type, if no id is specified', () => {
const v1 = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
namespaces: ['bar'],
attributes: { bar: true },
} as any);

const v2 = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
namespaces: ['bar'],
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`doesn't specify _source.namespaces`, () => {
const actual = namespaceAgnosticSerializer.savedObjectToRaw({
type: 'foo',
Expand All @@ -715,23 +633,6 @@ describe('#savedObjectToRaw', () => {
});

describe('multi-namespace type with a namespace', () => {
test('generates an id prefixed with type, if no id is specified', () => {
const v1 = multiNamespaceSerializer.savedObjectToRaw({
type: 'foo',
namespace: 'bar',
attributes: { bar: true },
} as any);

const v2 = multiNamespaceSerializer.savedObjectToRaw({
type: 'foo',
namespace: 'bar',
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`doesn't specify _source.namespace`, () => {
const actual = multiNamespaceSerializer.savedObjectToRaw({
type: 'foo',
Expand All @@ -744,23 +645,6 @@ describe('#savedObjectToRaw', () => {
});

describe('multi-namespace type with namespaces', () => {
test('generates an id prefixed with type, if no id is specified', () => {
const v1 = multiNamespaceSerializer.savedObjectToRaw({
type: 'foo',
namespaces: ['bar'],
attributes: { bar: true },
} as any);

const v2 = multiNamespaceSerializer.savedObjectToRaw({
type: 'foo',
namespaces: ['bar'],
attributes: { bar: true },
} as any);

expect(v1._id).toMatch(/^foo\:[\w-]+$/);
expect(v1._id).not.toEqual(v2._id);
});

test(`it copies namespaces to _source.namespaces`, () => {
const actual = multiNamespaceSerializer.savedObjectToRaw({
type: 'foo',
Expand Down Expand Up @@ -1064,35 +948,20 @@ describe('#isRawSavedObject', () => {

describe('#generateRawId', () => {
describe('single-namespace type without a namespace', () => {
test('generates an id if none is specified', () => {
const id = singleNamespaceSerializer.generateRawId('', 'goodbye');
expect(id).toMatch(/^goodbye\:[\w-]+$/);
});

test('uses the id that is specified', () => {
const id = singleNamespaceSerializer.generateRawId('', 'hello', 'world');
expect(id).toEqual('hello:world');
});
});

describe('single-namespace type with a namespace', () => {
test('generates an id if none is specified and prefixes namespace', () => {
const id = singleNamespaceSerializer.generateRawId('foo', 'goodbye');
expect(id).toMatch(/^foo:goodbye\:[\w-]+$/);
});

test('uses the id that is specified and prefixes the namespace', () => {
const id = singleNamespaceSerializer.generateRawId('foo', 'hello', 'world');
expect(id).toEqual('foo:hello:world');
});
});

describe('namespace-agnostic type with a namespace', () => {
test(`generates an id if none is specified and doesn't prefix namespace`, () => {
const id = namespaceAgnosticSerializer.generateRawId('foo', 'goodbye');
expect(id).toMatch(/^goodbye\:[\w-]+$/);
});

test(`uses the id that is specified and doesn't prefix the namespace`, () => {
const id = namespaceAgnosticSerializer.generateRawId('foo', 'hello', 'world');
expect(id).toEqual('hello:world');
Expand Down
5 changes: 2 additions & 3 deletions src/core/server/saved_objects/serialization/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/

import uuid from 'uuid';
import { decodeVersion, encodeVersion } from '../version';
import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry';
import { SavedObjectsRawDoc, SavedObjectSanitizedDoc } from './types';
Expand Down Expand Up @@ -127,10 +126,10 @@ export class SavedObjectsSerializer {
* @param {string} type - The saved object type
* @param {string} id - The id of the saved object
*/
public generateRawId(namespace: string | undefined, type: string, id?: string) {
public generateRawId(namespace: string | undefined, type: string, id: string) {
const namespacePrefix =
namespace && this.registry.isSingleNamespace(type) ? `${namespace}:` : '';
return `${namespacePrefix}${type}:${id || uuid.v1()}`;
return `${namespacePrefix}${type}:${id}`;
}

private trimIdPrefix(namespace: string | undefined, type: string, id: string) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/saved_objects/serialization/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface SavedObjectsRawDocSource {
*/
interface SavedObjectDoc<T = unknown> {
attributes: T;
id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional
id: string;
type: string;
namespace?: string;
namespaces?: string[];
Expand Down
Loading

0 comments on commit 9ef4d22

Please sign in to comment.