mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
* Removing circular dependency between spaces and security * Apply suggestions from code review Co-authored-by: Constance <constancecchen@users.noreply.github.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> * Tests refactor - Reorganize top level describes into 3 space-based blocks into based on spaces: - space disabled - spaces plugin unavailable - space enabled (most previous tests go under this new block) with new beforeEach - wrote new tests for uncovered lines 58, 66-69 * Review1: address PR feedback * changing fake requests for alerts/actions * Fixing tests * fixing more tests * Additional testing and refactoring * Apply suggestions from code review Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> * Review 2: Address feedback * Make ESLint happy again Co-authored-by: Constance <constancecchen@users.noreply.github.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Constance Chen <constance.chen.3@gmail.com> Co-authored-by: Constance <constancecchen@users.noreply.github.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Constance Chen <constance.chen.3@gmail.com>
158 lines
5 KiB
TypeScript
158 lines
5 KiB
TypeScript
/*
|
|
* 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 { Observable, BehaviorSubject } from 'rxjs';
|
|
import { skipWhile } from 'rxjs/operators';
|
|
import { HttpSetup } from 'src/core/public';
|
|
import { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public';
|
|
import { Space } from '../../common/model/space';
|
|
import { GetAllSpacesPurpose, GetSpaceResult } from '../../common/model/types';
|
|
import { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types';
|
|
|
|
type SavedObject = Pick<SavedObjectsManagementRecord, 'type' | 'id'>;
|
|
interface GetAllSpacesOptions {
|
|
purpose?: GetAllSpacesPurpose;
|
|
includeAuthorizedPurposes?: boolean;
|
|
}
|
|
|
|
export class SpacesManager {
|
|
private activeSpace$: BehaviorSubject<Space | null> = new BehaviorSubject<Space | null>(null);
|
|
|
|
private readonly serverBasePath: string;
|
|
|
|
public readonly onActiveSpaceChange$: Observable<Space>;
|
|
|
|
constructor(private readonly http: HttpSetup) {
|
|
this.serverBasePath = http.basePath.serverBasePath;
|
|
|
|
this.onActiveSpaceChange$ = this.activeSpace$
|
|
.asObservable()
|
|
.pipe(skipWhile((v: Space | null) => v == null)) as Observable<Space>;
|
|
|
|
this.refreshActiveSpace();
|
|
}
|
|
|
|
public async getSpaces(options: GetAllSpacesOptions = {}): Promise<GetSpaceResult[]> {
|
|
const { purpose, includeAuthorizedPurposes } = options;
|
|
const query = { purpose, include_authorized_purposes: includeAuthorizedPurposes };
|
|
return await this.http.get('/api/spaces/space', { query });
|
|
}
|
|
|
|
public async getSpace(id: string): Promise<Space> {
|
|
return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
public getActiveSpace({ forceRefresh = false } = {}) {
|
|
if (this.isAnonymousPath()) {
|
|
throw new Error(`Cannot retrieve the active space for anonymous paths`);
|
|
}
|
|
if (!forceRefresh && this.activeSpace$.value) {
|
|
return Promise.resolve(this.activeSpace$.value);
|
|
}
|
|
return this.http.get('/internal/spaces/_active_space') as Promise<Space>;
|
|
}
|
|
|
|
public async createSpace(space: Space) {
|
|
await this.http.post(`/api/spaces/space`, {
|
|
body: JSON.stringify(space),
|
|
});
|
|
}
|
|
|
|
public async updateSpace(space: Space) {
|
|
await this.http.put(`/api/spaces/space/${encodeURIComponent(space.id)}`, {
|
|
query: {
|
|
overwrite: true,
|
|
},
|
|
body: JSON.stringify(space),
|
|
});
|
|
|
|
const activeSpaceId = (await this.getActiveSpace()).id;
|
|
|
|
if (space.id === activeSpaceId) {
|
|
this.refreshActiveSpace();
|
|
}
|
|
}
|
|
|
|
public async deleteSpace(space: Space) {
|
|
await this.http.delete(`/api/spaces/space/${encodeURIComponent(space.id)}`);
|
|
}
|
|
|
|
public async copySavedObjects(
|
|
objects: SavedObject[],
|
|
spaces: string[],
|
|
includeReferences: boolean,
|
|
createNewCopies: boolean,
|
|
overwrite: boolean
|
|
): Promise<CopySavedObjectsToSpaceResponse> {
|
|
return this.http.post('/api/spaces/_copy_saved_objects', {
|
|
body: JSON.stringify({
|
|
objects,
|
|
spaces,
|
|
includeReferences,
|
|
...(createNewCopies ? { createNewCopies } : { overwrite }),
|
|
}),
|
|
});
|
|
}
|
|
|
|
public async resolveCopySavedObjectsErrors(
|
|
objects: SavedObject[],
|
|
retries: unknown,
|
|
includeReferences: boolean,
|
|
createNewCopies: boolean
|
|
): Promise<CopySavedObjectsToSpaceResponse> {
|
|
return this.http.post(`/api/spaces/_resolve_copy_saved_objects_errors`, {
|
|
body: JSON.stringify({
|
|
objects,
|
|
includeReferences,
|
|
createNewCopies,
|
|
retries,
|
|
}),
|
|
});
|
|
}
|
|
|
|
public async getShareSavedObjectPermissions(
|
|
type: string
|
|
): Promise<{ shareToAllSpaces: boolean }> {
|
|
return this.http
|
|
.get('/internal/security/_share_saved_object_permissions', { query: { type } })
|
|
.catch((err) => {
|
|
const isNotFound = err?.body?.statusCode === 404;
|
|
if (isNotFound) {
|
|
// security is not enabled
|
|
return { shareToAllSpaces: true };
|
|
}
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
public async shareSavedObjectAdd(object: SavedObject, spaces: string[]): Promise<void> {
|
|
return this.http.post(`/api/spaces/_share_saved_object_add`, {
|
|
body: JSON.stringify({ object, spaces }),
|
|
});
|
|
}
|
|
|
|
public async shareSavedObjectRemove(object: SavedObject, spaces: string[]): Promise<void> {
|
|
return this.http.post(`/api/spaces/_share_saved_object_remove`, {
|
|
body: JSON.stringify({ object, spaces }),
|
|
});
|
|
}
|
|
|
|
public redirectToSpaceSelector() {
|
|
window.location.href = `${this.serverBasePath}/spaces/space_selector`;
|
|
}
|
|
|
|
private async refreshActiveSpace() {
|
|
// Anonymous paths (such as login/logout) should not request the active space under any circumstances.
|
|
if (this.isAnonymousPath()) {
|
|
return;
|
|
}
|
|
const activeSpace = await this.getActiveSpace({ forceRefresh: true });
|
|
this.activeSpace$.next(activeSpace);
|
|
}
|
|
|
|
private isAnonymousPath() {
|
|
return this.http.anonymousPaths.isAnonymous(window.location.pathname);
|
|
}
|
|
}
|