Skip to content
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] Hides the alert SavedObjects type #66719

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
472894f
make alert saved object type hidden
gmmorris May 15, 2020
6f94c02
fix support for hidden alert type in alerting tests
gmmorris May 15, 2020
31ded81
updated api docs
gmmorris May 15, 2020
3cccf63
fixed some missing types and unused imports
gmmorris May 15, 2020
d5bddce
Merge branch 'master' into saved-objects/scoped-client-with-extra-types
gmmorris May 15, 2020
278b375
fixed test broken by field rename
gmmorris May 18, 2020
5ac7e88
Merge branch 'master' into saved-objects/scoped-client-with-extra-types
gmmorris May 18, 2020
4f8e589
added support for including hidden types in saved objects client
gmmorris May 18, 2020
3d828fd
Merge branch 'saved-objects/scoped-client-with-included-hidden-types'…
gmmorris May 18, 2020
0865068
fixed merge conflict
gmmorris May 18, 2020
60e2593
cleaned up some test descriptions
gmmorris May 18, 2020
ba90e13
adds a getClient api to Encrypted Saved Objects
gmmorris May 18, 2020
996a739
Merge branch 'saved-objects/scoped-client-with-included-hidden-types'…
gmmorris May 19, 2020
fd44d7e
fixed alerts fixture
gmmorris May 19, 2020
c037724
added missing plugin type in alerting
gmmorris May 19, 2020
cc95823
Merge branch 'master' into saved-objects/scoped-client-with-extra-types
gmmorris May 19, 2020
ff9d788
removed unused field
gmmorris May 19, 2020
244da07
Merge branch 'master' into saved-objects/scoped-client-with-extra-types
gmmorris May 20, 2020
cdf1206
chaged ESO api to an options object as per Security teams request
gmmorris May 20, 2020
a3eeb5d
fixed usage of eso client
gmmorris May 20, 2020
a99d16a
fixed typos and oversights
gmmorris May 20, 2020
b826313
split alerts file into two - for actions and alerts
gmmorris May 21, 2020
0660d44
Merge branch 'master' into saved-objects/scoped-client-with-extra-types
gmmorris May 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions x-pack/plugins/alerting/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ export interface AlertingPluginsSetup {
}
export interface AlertingPluginsStart {
actions: ActionsPluginStartContract;
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
taskManager: TaskManagerStartContract;
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
}

export class AlertingPlugin {
Expand Down Expand Up @@ -126,6 +126,7 @@ export class AlertingPlugin {
this.licenseState = new LicenseState(plugins.licensing.license$);
this.spaces = plugins.spaces?.spacesService;
this.security = plugins.security;

this.isESOUsingEphemeralEncryptionKey =
plugins.encryptedSavedObjects.usingEphemeralEncryptionKey;

Expand Down Expand Up @@ -164,7 +165,7 @@ export class AlertingPlugin {
});
}

core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext());
core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext(core));

// Routes
const router = core.http.createRouter();
Expand Down Expand Up @@ -201,7 +202,9 @@ export class AlertingPlugin {
security,
} = this;

const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient();
const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient({
includedHiddenTypes: ['alert'],
});

alertsClientFactory.initialize({
alertTypeRegistry: alertTypeRegistry!,
Expand Down Expand Up @@ -231,26 +234,32 @@ export class AlertingPlugin {
return {
listTypes: alertTypeRegistry!.list.bind(this.alertTypeRegistry!),
// Ability to get an alerts client from legacy code
getAlertsClientWithRequest(request: KibanaRequest) {
getAlertsClientWithRequest: (request: KibanaRequest) => {
if (isESOUsingEphemeralEncryptionKey === true) {
throw new Error(
`Unable to create alerts client due to the Encrypted Saved Objects plugin using an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml`
);
}
return alertsClientFactory!.create(request, core.savedObjects.getScopedClient(request));
return alertsClientFactory!.create(
request,
this.getScopedClientWithAlertSavedObjectType(core.savedObjects, request)
);
},
};
}

private createRouteHandlerContext = (): IContextProvider<
RequestHandler<unknown, unknown, unknown>,
'alerting'
> => {
private createRouteHandlerContext = (
core: CoreSetup
): IContextProvider<RequestHandler<unknown, unknown, unknown>, 'alerting'> => {
const { alertTypeRegistry, alertsClientFactory } = this;
return async function alertsRouteHandlerContext(context, request) {
return async (context, request) => {
const [{ savedObjects }] = await core.getStartServices();
return {
getAlertsClient: () => {
return alertsClientFactory!.create(request, context.core.savedObjects.client);
return alertsClientFactory!.create(
request,
this.getScopedClientWithAlertSavedObjectType(savedObjects, request)
);
},
listTypes: alertTypeRegistry!.list.bind(alertTypeRegistry!),
};
Expand All @@ -263,7 +272,7 @@ export class AlertingPlugin {
): (request: KibanaRequest) => Services {
return request => ({
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
savedObjectsClient: savedObjects.getScopedClient(request),
savedObjectsClient: this.getScopedClientWithAlertSavedObjectType(savedObjects, request),
getScopedCallCluster(clusterClient: IClusterClient) {
return clusterClient.asScoped(request).callAsCurrentUser;
},
Expand All @@ -278,6 +287,13 @@ export class AlertingPlugin {
return this.spaces && spaceId ? this.spaces.getBasePath(spaceId) : this.serverBasePath!;
};

private getScopedClientWithAlertSavedObjectType(
savedObjects: SavedObjectsServiceStart,
request: KibanaRequest
) {
return savedObjects.getScopedClient(request, { includedHiddenTypes: ['alert'] });
}

public stop() {
if (this.licenseState) {
this.licenseState.clean();
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/alerting/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function setupSavedObjects(
) {
savedObjects.registerType({
name: 'alert',
hidden: false,
hidden: true,
namespaceType: 'single',
mappings: mappings.alert,
});
Expand Down
16 changes: 14 additions & 2 deletions x-pack/plugins/encrypted_saved_objects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,19 @@ router.get(
...
```

5. To retrieve Saved Object with decrypted content use the dedicated `getDecryptedAsInternalUser` API method.
5. Instantiate an EncryptedSavedObjects client so that you can interact with Saved Objects whose content has been encrypted.

```typescript
const esoClient = encryptedSavedObjects.getClient();
```

If your SavedObject type is a _hidden_ type, then you will have to specify it as an included type:

```typescript
const esoClient = encryptedSavedObjects.getClient({ includedHiddenTypes: ['myHiddenType'] });
```

6. To retrieve Saved Object with decrypted content use the dedicated `getDecryptedAsInternalUser` API method.

**Note:** As name suggests the method will retrieve the encrypted values and decrypt them on behalf of the internal Kibana
user to make it possible to use this method even when user request context is not available (e.g. in background tasks).
Expand All @@ -77,7 +89,7 @@ and preferably only as a part of the Kibana server routines that are outside of
user has control over.

```typescript
const savedObjectWithDecryptedContent = await encryptedSavedObjects.getDecryptedAsInternalUser(
const savedObjectWithDecryptedContent = await esoClient.getDecryptedAsInternalUser(
'my-saved-object-type',
'saved-object-id'
);
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/encrypted_saved_objects/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { EncryptedSavedObjectsPluginSetup, EncryptedSavedObjectsPluginStart } from './plugin';
import { EncryptedSavedObjectsClient } from './saved_objects';
import { EncryptedSavedObjectsClient, EncryptedSavedObjectsClientOptions } from './saved_objects';

function createEncryptedSavedObjectsSetupMock() {
return {
Expand All @@ -18,11 +18,11 @@ function createEncryptedSavedObjectsSetupMock() {
function createEncryptedSavedObjectsStartMock() {
return {
isEncryptionError: jest.fn(),
getClient: jest.fn(() => createEncryptedSavedObjectsClienttMock()),
getClient: jest.fn(opts => createEncryptedSavedObjectsClienttMock(opts)),
} as jest.Mocked<EncryptedSavedObjectsPluginStart>;
}

function createEncryptedSavedObjectsClienttMock() {
function createEncryptedSavedObjectsClienttMock(opts?: EncryptedSavedObjectsClientOptions) {
return {
getDecryptedAsInternalUser: jest.fn(),
} as jest.Mocked<EncryptedSavedObjectsClient>;
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/encrypted_saved_objects/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
EncryptionError,
} from './crypto';
import { EncryptedSavedObjectsAuditLogger } from './audit';
import { SavedObjectsSetup, setupSavedObjects } from './saved_objects';
import { setupSavedObjects, ClientInstanciator } from './saved_objects';

export interface PluginsSetup {
security?: SecurityPluginSetup;
Expand All @@ -28,7 +28,7 @@ export interface EncryptedSavedObjectsPluginSetup {

export interface EncryptedSavedObjectsPluginStart {
isEncryptionError: (error: Error) => boolean;
getClient: SavedObjectsSetup;
getClient: ClientInstanciator;
}

/**
Expand All @@ -46,7 +46,7 @@ export interface LegacyAPI {
*/
export class Plugin {
private readonly logger: Logger;
private savedObjectsSetup!: SavedObjectsSetup;
private savedObjectsSetup!: ClientInstanciator;

private legacyAPI?: LegacyAPI;
private readonly getLegacyAPI = () => {
Expand Down Expand Up @@ -95,7 +95,7 @@ export class Plugin {
this.logger.debug('Starting plugin');
return {
isEncryptionError: (error: Error) => error instanceof EncryptionError,
getClient: (includedHiddenTypes?: string[]) => this.savedObjectsSetup(includedHiddenTypes),
getClient: (options = {}) => this.savedObjectsSetup(options),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { SavedObjectsSetup, setupSavedObjects } from '.';
import { ClientInstanciator, setupSavedObjects } from '.';

import {
coreMock,
Expand All @@ -24,7 +24,7 @@ import {
import { EncryptedSavedObjectsService } from '../crypto';

describe('#setupSavedObjects', () => {
let setupContract: SavedObjectsSetup;
let setupContract: ClientInstanciator;
let coreStartMock: ReturnType<typeof coreMock.createStart>;
let coreSetupMock: ReturnType<typeof coreMock.createSetup>;
let mockSavedObjectsRepository: jest.Mocked<ISavedObjectsRepository>;
Expand Down Expand Up @@ -91,7 +91,7 @@ describe('#setupSavedObjects', () => {

describe('#setupContract', () => {
it('includes hiddenTypes when specified', async () => {
await setupContract(['hiddenType']);
await setupContract({ includedHiddenTypes: ['hiddenType'] });
expect(coreStartMock.savedObjects.createInternalRepository).toHaveBeenCalledWith([
'hiddenType',
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ interface SetupSavedObjectsParams {
getStartServices: StartServicesAccessor;
}

export type SavedObjectsSetup = (includedHiddenTypes?: string[]) => EncryptedSavedObjectsClient;
export type ClientInstanciator = (
options?: EncryptedSavedObjectsClientOptions
) => EncryptedSavedObjectsClient;

export interface EncryptedSavedObjectsClientOptions {
includedHiddenTypes?: string[];
}

export interface EncryptedSavedObjectsClient {
getDecryptedAsInternalUser: <T = unknown>(
Expand All @@ -38,7 +44,7 @@ export function setupSavedObjects({
savedObjects,
security,
getStartServices,
}: SetupSavedObjectsParams): SavedObjectsSetup {
}: SetupSavedObjectsParams): ClientInstanciator {
// Register custom saved object client that will encrypt, decrypt and strip saved object
// attributes where appropriate for any saved object repository request. We choose max possible
// priority for this wrapper to allow all other wrappers to set proper `namespace` for the Saved
Expand All @@ -56,11 +62,11 @@ export function setupSavedObjects({
})
);

return (includedHiddenTypes?: string[]) => {
return clientOpts => {
const internalRepositoryAndTypeRegistryPromise = getStartServices().then(
([core]) =>
[
core.savedObjects.createInternalRepository(includedHiddenTypes),
core.savedObjects.createInternalRepository(clientOpts?.includedHiddenTypes),
core.savedObjects.getTypeRegistry(),
] as [ISavedObjectsRepository, ISavedObjectTypeRegistry]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server';
interface FixtureSetupDeps {
spaces?: SpacesPluginSetup;
}

interface FixtureStartDeps {
encryptedSavedObjects: EncryptedSavedObjectsPluginStart;
}
Expand All @@ -44,12 +43,14 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
): Promise<IKibanaResponse<any>> {
try {
let namespace: string | undefined;
const [, { encryptedSavedObjects }] = await core.getStartServices();
if (spaces && req.body.spaceId) {
namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId);
}
const [, { encryptedSavedObjects }] = await core.getStartServices();
await encryptedSavedObjects
.getClient()
.getClient({
includedHiddenTypes: ['alert'],
})
.getDecryptedAsInternalUser(req.body.type, req.body.id, {
namespace,
});
Expand Down
Loading