[Files] Add default file kind (#144803)

## Summary

Introduce a default file kind for images in Kibana. This file kind will
be the download/upload target usable for all images across Kibana.
Consider the following:

A Kibana user wants to add a branding logo to their dashboard. They need
to create a new image or select from a set of existing images (i.e.,
images already uploaded). This set of images is the "default image" set.
The idea will be this set of images can be access across dashboards and
solutions. For example, the same user can access the branding image they
uploaded in Cases.

## How it works

* We added a new default file kind specifically for images, this is
registered from the files plugin
* In order to access these files over HTTP users will need the
`files:defaultImage` privilege
* This is a distinct privilege from the file management privilege and
allows users to access HTTP endpoints controlled by
`access:files:defaultImage` as well as the underlying `file` saved
object
* Consider a dashboard user that wants to add an image embeddable: they
will need access to `file` saved object as well as the endpoints for
creating/reading/deleting the default file kind. In order to get this
their role must grant the new "Shared images" privilege.

<img width="749" alt="Screenshot 2022-11-22 at 10 34 25"
src="https://user-images.githubusercontent.com/8155004/203295230-24a0be94-9c59-4a53-8757-336e9fc8f6c4.png">
This commit is contained in:
Jean-Louis Leysens 2022-11-23 12:37:05 +01:00 committed by GitHub
parent b6dcb8b088
commit 6de90aab9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 202 additions and 8 deletions

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { FileKind } from './types';
const id = 'defaultImage' as const;
const tag = 'files:defaultImage' as const;
const tags = [`access:${tag}`];
const tenMebiBytes = 1024 * 1024 * 10;
/**
* A file kind that is available to all plugins to use for uploading images
* intended to be reused across Kibana.
*/
export const defaultImageFileKind: FileKind = {
id,
maxSizeBytes: tenMebiBytes,
blobStoreSettings: {},
// tried using "image/*" but it did not work with the HTTP endpoint (got 415 Unsupported Media Type)
allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp', 'image/avif'],
http: {
create: { tags },
delete: { tags },
download: { tags },
getById: { tags },
list: { tags },
share: { tags },
update: { tags },
},
};

View file

@ -7,6 +7,7 @@
*/
export { FILE_SO_TYPE, PLUGIN_ID, PLUGIN_NAME, ES_FIXED_SIZE_INDEX_BLOB_STORE } from './constants';
export { defaultImageFileKind } from './default_image_file_kind';
export type {
File,

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { getFileKindsRegistry } from './file_kinds_registry';
import { defaultImageFileKind } from '.';
export function registerDefaultFileKinds() {
const registry = getFileKindsRegistry();
registry.register(defaultImageFileKind);
}

View file

@ -7,6 +7,7 @@
*/
import { FilesPlugin } from './plugin';
export { defaultImageFileKind } from '../common/default_image_file_kind';
export type { FilesSetup, FilesStart } from './plugin';
export type {
FilesClient,

View file

@ -15,6 +15,7 @@ import {
import type { FilesClient, FilesClientFactory } from './types';
import { createFilesClient } from './files_client';
import { FileKind } from '../common';
import { registerDefaultFileKinds } from '../common/register_default_file_kinds';
import { ScopedFilesClient } from '.';
/**
@ -59,6 +60,7 @@ export class FilesPlugin implements Plugin<FilesSetup, FilesStart> {
return createFilesClient({ http: core.http }) as FilesClient<M>;
},
};
registerDefaultFileKinds();
return {
filesClientFactory: this.filesClientFactory,
registerFileKind: (fileKind: FileKind) => {

View file

@ -21,6 +21,7 @@ import {
getFileKindsRegistry,
FileKindsRegistryImpl,
} from '../common/file_kinds_registry';
import { registerDefaultFileKinds } from '../common/register_default_file_kinds';
import { BlobStorageService } from './blob_storage_service';
import { FileServiceFactory } from './file_service';
@ -91,6 +92,9 @@ export class FilesPlugin implements Plugin<FilesSetup, FilesStart, FilesPluginSe
getFileService: () => this.fileServiceFactory?.asInternal(),
});
// Now that everything is set up:
registerDefaultFileKinds();
return {
registerFileKind(fileKind) {
getFileKindsRegistry().register(fileKind);

View file

@ -169,6 +169,10 @@ Array [
"id": "filesManagement",
"subFeatures": undefined,
},
Object {
"id": "filesSharedImage",
"subFeatures": undefined,
},
Object {
"id": "savedObjectsManagement",
"subFeatures": undefined,
@ -457,6 +461,10 @@ Array [
"id": "filesManagement",
"subFeatures": undefined,
},
Object {
"id": "filesSharedImage",
"subFeatures": undefined,
},
Object {
"id": "savedObjectsManagement",
"subFeatures": undefined,
@ -773,6 +781,7 @@ Array [
"privilege": Object {
"api": Array [
"files:manageFiles",
"files:defaultImage",
],
"app": Array [
"kibana",
@ -784,7 +793,8 @@ Array [
},
"savedObject": Object {
"all": Array [
"files",
"file",
"fileShare",
],
"read": Array [],
},
@ -796,6 +806,7 @@ Array [
"privilege": Object {
"api": Array [
"files:manageFiles",
"files:defaultImage",
],
"app": Array [
"kibana",
@ -808,7 +819,49 @@ Array [
"savedObject": Object {
"all": Array [],
"read": Array [
"files",
"file",
"fileShare",
],
},
"ui": Array [],
},
"privilegeId": "read",
},
]
`;
exports[`buildOSSFeatures with a basic license returns the filesSharedImage feature augmented with appropriate sub feature privileges 1`] = `
Array [
Object {
"privilege": Object {
"api": Array [
"files:defaultImage",
],
"app": Array [
"kibana",
],
"savedObject": Object {
"all": Array [
"file",
],
"read": Array [],
},
"ui": Array [],
},
"privilegeId": "all",
},
Object {
"privilege": Object {
"api": Array [
"files:defaultImage",
],
"app": Array [
"kibana",
],
"savedObject": Object {
"all": Array [],
"read": Array [
"file",
],
},
"ui": Array [],
@ -1332,6 +1385,7 @@ Array [
"privilege": Object {
"api": Array [
"files:manageFiles",
"files:defaultImage",
],
"app": Array [
"kibana",
@ -1343,7 +1397,8 @@ Array [
},
"savedObject": Object {
"all": Array [
"files",
"file",
"fileShare",
],
"read": Array [],
},
@ -1355,6 +1410,7 @@ Array [
"privilege": Object {
"api": Array [
"files:manageFiles",
"files:defaultImage",
],
"app": Array [
"kibana",
@ -1367,7 +1423,49 @@ Array [
"savedObject": Object {
"all": Array [],
"read": Array [
"files",
"file",
"fileShare",
],
},
"ui": Array [],
},
"privilegeId": "read",
},
]
`;
exports[`buildOSSFeatures with a enterprise license returns the filesSharedImage feature augmented with appropriate sub feature privileges 1`] = `
Array [
Object {
"privilege": Object {
"api": Array [
"files:defaultImage",
],
"app": Array [
"kibana",
],
"savedObject": Object {
"all": Array [
"file",
],
"read": Array [],
},
"ui": Array [],
},
"privilegeId": "all",
},
Object {
"privilege": Object {
"api": Array [
"files:defaultImage",
],
"app": Array [
"kibana",
],
"savedObject": Object {
"all": Array [],
"read": Array [
"file",
],
},
"ui": Array [],

View file

@ -441,11 +441,11 @@ export const buildOSSFeatures = ({
kibana: ['filesManagement'],
},
savedObject: {
all: ['files'],
all: ['file', 'fileShare'],
read: [],
},
ui: [],
api: ['files:manageFiles'],
api: ['files:manageFiles', 'files:defaultImage'],
},
read: {
app: ['kibana'],
@ -454,10 +454,43 @@ export const buildOSSFeatures = ({
},
savedObject: {
all: [],
read: ['files'],
read: ['file', 'fileShare'],
},
ui: [],
api: ['files:manageFiles'],
api: ['files:manageFiles', 'files:defaultImage'],
},
},
},
{
id: 'filesSharedImage',
name: i18n.translate('xpack.features.filesSharedImagesFeatureName', {
defaultMessage: 'Shared images',
}),
order: 1600,
category: DEFAULT_APP_CATEGORIES.management,
app: ['kibana'],
catalogue: [],
privilegesTooltip: i18n.translate('xpack.features.filesSharedImagesPrivilegesTooltip', {
defaultMessage: 'Required to access images stored in Kibana.',
}),
privileges: {
all: {
app: ['kibana'],
savedObject: {
all: ['file'],
read: [],
},
ui: [],
api: ['files:defaultImage'],
},
read: {
app: ['kibana'],
savedObject: {
all: [],
read: ['file'],
},
ui: [],
api: ['files:defaultImage'],
},
},
},

View file

@ -67,6 +67,7 @@ describe('Features Plugin', () => {
"advancedSettings",
"indexPatterns",
"filesManagement",
"filesSharedImage",
"savedObjectsManagement",
]
`);

View file

@ -101,6 +101,7 @@ export default function ({ getService }: FtrProviderContext) {
'actions',
'enterpriseSearch',
'filesManagement',
'filesSharedImage',
'advancedSettings',
'indexPatterns',
'graph',

View file

@ -79,6 +79,7 @@ export default function ({ getService }: FtrProviderContext) {
'packs_read',
],
filesManagement: ['all', 'read', 'minimal_all', 'minimal_read'],
filesSharedImage: ['all', 'read', 'minimal_all', 'minimal_read'],
},
reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'],
};

View file

@ -46,6 +46,7 @@ export default function ({ getService }: FtrProviderContext) {
stackAlerts: ['all', 'read', 'minimal_all', 'minimal_read'],
actions: ['all', 'read', 'minimal_all', 'minimal_read'],
filesManagement: ['all', 'read', 'minimal_all', 'minimal_read'],
filesSharedImage: ['all', 'read', 'minimal_all', 'minimal_read'],
},
global: ['all', 'read'],
space: ['all', 'read'],
@ -133,6 +134,7 @@ export default function ({ getService }: FtrProviderContext) {
advancedSettings: ['all', 'read', 'minimal_all', 'minimal_read'],
indexPatterns: ['all', 'read', 'minimal_all', 'minimal_read'],
filesManagement: ['all', 'read', 'minimal_all', 'minimal_read'],
filesSharedImage: ['all', 'read', 'minimal_all', 'minimal_read'],
savedObjectsManagement: ['all', 'read', 'minimal_all', 'minimal_read'],
osquery: [
'all',