Skip to content

Commit

Permalink
updates to read only tenant mode logic
Browse files Browse the repository at this point in the history
Signed-off-by: jakubp-eliatra <jakub.przybylski@theworkingroup.net>
  • Loading branch information
jakubp-eliatra committed Jun 22, 2023
1 parent 7f4e0f2 commit df2bbaf
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
5 changes: 5 additions & 0 deletions server/auth/types/authentication_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export interface IAuthenticationType {
type: string;
authHandler: AuthenticationHandler;
init: () => Promise<void>;
requestIncludesAuthInfo(request: OpenSearchDashboardsRequest): boolean;
buildAuthHeaderFromCookie(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): any;
}

export type IAuthHandlerConstructor = new (
Expand Down
112 changes: 112 additions & 0 deletions server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import { first } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { parse } from 'url';
import { merge } from 'lodash';
import {
PluginInitializerContext,
CoreSetup,
Expand All @@ -24,6 +26,9 @@ import {
ILegacyClusterClient,
SessionStorageFactory,
SharedGlobalConfig,
OpenSearchDashboardsRequest,
Capabilities,
CapabilitiesSwitcher,
} from '../../../src/core/server';

import { SecurityPluginSetup, SecurityPluginStart } from './types';
Expand All @@ -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;
Expand Down Expand Up @@ -84,6 +90,109 @@ export class SecurityPlugin implements Plugin<SecurityPluginSetup, SecurityPlugi
this.savedObjectClientWrapper = new SecuritySavedObjectsClientWrapper();
}

isAnonymousPage(request: OpenSearchDashboardsRequest) {
if (request.headers && request.headers.referer) {
try {
const { pathname } = parse(request.headers.referer as string);
const pathsToIgnore = ['login', 'logout', 'customerror'];
if (pathsToIgnore.indexOf(pathname?.split('/').pop() || '') > -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<Capabilities> {
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<SecuritySessionCookie>
): CapabilitiesSwitcher {
return async (
request: OpenSearchDashboardsRequest,
uiCapabilities: Capabilities
): Promise<Partial<Capabilities>> => {
// 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<SecuritySessionCookie>
) {
core.capabilities.registerSwitcher(
this.capabilitiesSwitcher(securityClient, auth, securitySessionStorageFactory)
);
}

public async setup(core: CoreSetup) {
this.logger.debug('opendistro_security: Setup');

Expand Down Expand Up @@ -131,6 +240,9 @@ export class SecurityPlugin implements Plugin<SecurityPluginSetup, SecurityPlugi
// set up multi-tenent routes
if (config.multitenancy?.enabled) {
setupMultitenantRoutes(router, securitySessionStorageFactory, this.securityClient);

const securityClient: SecurityClient = this.securityClient;
this.registerSwitcher(core, securityClient, auth, securitySessionStorageFactory);
}

if (config.multitenancy.enabled && config.multitenancy.enable_aggregation_view) {
Expand Down

0 comments on commit df2bbaf

Please sign in to comment.