From df2bbaf565d5055a87aa26a9511b414fff7696d7 Mon Sep 17 00:00:00 2001 From: jakubp-eliatra Date: Thu, 22 Jun 2023 14:34:28 +0200 Subject: [PATCH] updates to read only tenant mode logic Signed-off-by: jakubp-eliatra --- server/auth/types/authentication_type.ts | 5 + server/plugin.ts | 112 +++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/server/auth/types/authentication_type.ts b/server/auth/types/authentication_type.ts index 56ec21463..f752f349b 100755 --- a/server/auth/types/authentication_type.ts +++ b/server/auth/types/authentication_type.ts @@ -37,6 +37,11 @@ export interface IAuthenticationType { type: string; authHandler: AuthenticationHandler; init: () => Promise; + requestIncludesAuthInfo(request: OpenSearchDashboardsRequest): boolean; + buildAuthHeaderFromCookie( + cookie: SecuritySessionCookie, + request: OpenSearchDashboardsRequest + ): any; } export type IAuthHandlerConstructor = new ( diff --git a/server/plugin.ts b/server/plugin.ts index 84d94e4f1..0357da231 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -15,6 +15,8 @@ import { first } from 'rxjs/operators'; import { Observable } from 'rxjs'; +import { parse } from 'url'; +import { merge } from 'lodash'; import { PluginInitializerContext, CoreSetup, @@ -24,6 +26,9 @@ import { ILegacyClusterClient, SessionStorageFactory, SharedGlobalConfig, + OpenSearchDashboardsRequest, + Capabilities, + CapabilitiesSwitcher, } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; @@ -47,6 +52,7 @@ import { setupMultitenantRoutes } from './multitenancy/routes'; import { defineAuthTypeRoutes } from './routes/auth_type_routes'; import { createMigrationOpenSearchClient } from '../../../src/core/server/saved_objects/migrations/core'; import { SecuritySavedObjectsClientWrapper } from './saved_objects/saved_objects_wrapper'; +import { globalTenantName } from '../common'; export interface SecurityPluginRequestContext { logger: Logger; @@ -84,6 +90,109 @@ export class SecurityPlugin implements Plugin -1) { + return true; + } + } catch (error: any) { + this.logger.error(`Could not parse the referer for the capabilites: ${error.stack}`); + } + } + + return false; + } + + isReadOnlyTenant(authInfo: any) { + // global tenant is '' + const currentTenant = authInfo.user_requested_tenant || globalTenantName; + + if (currentTenant === '__user__') { + // private tenant is not affected + return false; + } + + const isReadOnlyTenant = authInfo.tenants[currentTenant] !== true ? true : false; + + return isReadOnlyTenant; + } + + toggleReadOnlyCapabilities(capabilities: any): Partial { + return Object.entries(capabilities).reduce((acc, cur) => { + const [key, value] = cur; + + return { ...acc, [key]: capabilities.hide_for_read_only.includes(key) ? false : value }; + }, {}); + } + + toggleForReadOnlyTenant(uiCapabilities: Capabilities) { + const defaultTenantOnlyCapabilities = Object.entries(uiCapabilities).reduce((acc, cur) => { + const [key, value] = cur; + + if (!value.hide_for_read_only) { + return acc; + } + + const updatedValue = this.toggleReadOnlyCapabilities(value); + + return { ...acc, [key]: updatedValue }; + }, {}); + + const finalCapabilities = merge(uiCapabilities, defaultTenantOnlyCapabilities); + + return finalCapabilities; + } + + capabilitiesSwitcher( + securityClient: SecurityClient, + auth: IAuthenticationType, + securitySessionStorageFactory: SessionStorageFactory + ): CapabilitiesSwitcher { + return async ( + request: OpenSearchDashboardsRequest, + uiCapabilities: Capabilities + ): Promise> => { + // omit for anonymous pages to avoid authentication errors + if (this.isAnonymousPage(request)) { + return uiCapabilities; + } + + try { + let headers = request.headers; + + if (!auth.requestIncludesAuthInfo(request)) { + const cookie = await securitySessionStorageFactory.asScoped(request).get(); + // @ts-ignore + headers = auth.buildAuthHeaderFromCookie(cookie, request); + } + + const authInfo = await securityClient.authinfo(request, headers); + + if (this.isReadOnlyTenant(authInfo)) { + return this.toggleForReadOnlyTenant(uiCapabilities); + } + } catch (error: any) { + this.logger.error(`Could not check auth info: ${error.stack}`); + } + + return uiCapabilities; + }; + } + + registerSwitcher( + core: CoreSetup, + securityClient: SecurityClient, + auth: IAuthenticationType, + securitySessionStorageFactory: SessionStorageFactory + ) { + core.capabilities.registerSwitcher( + this.capabilitiesSwitcher(securityClient, auth, securitySessionStorageFactory) + ); + } + public async setup(core: CoreSetup) { this.logger.debug('opendistro_security: Setup'); @@ -131,6 +240,9 @@ export class SecurityPlugin implements Plugin