Skip to content

Commit

Permalink
Introduce read-only tenant mode logic
Browse files Browse the repository at this point in the history
Signed-off-by: Kajetan Nobel <[email protected]>
  • Loading branch information
kajetan-nobel committed Jul 18, 2023
1 parent 29a8d9c commit 87d40fa
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 7 deletions.
7 changes: 6 additions & 1 deletion 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 Expand Up @@ -287,4 +292,4 @@ export abstract class AuthenticationType implements IAuthenticationType {
request: OpenSearchDashboardsRequest
): any;
public abstract init(): Promise<void>;
}
}
117 changes: 111 additions & 6 deletions server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

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

import { SecurityPluginSetup, SecurityPluginStart } from './types';
Expand All @@ -39,15 +42,13 @@ import {
ISavedObjectTypeRegistry,
} from '../../../src/core/server/saved_objects';
import { setupIndexTemplate, migrateTenantIndices } from './multitenancy/tenant_index';
import {
IAuthenticationType,
OpenSearchDashboardsAuthState,
} from './auth/types/authentication_type';
import { IAuthenticationType } from './auth/types/authentication_type';
import { getAuthenticationHandler } from './auth/auth_handler_factory';
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, isPrivateTenant } from '../common';
import { addTenantParameterToResolvedShortLink } from './multitenancy/tenant_resolver';

export interface SecurityPluginRequestContext {
Expand Down Expand Up @@ -86,6 +87,107 @@ export class SecurityPlugin implements Plugin<SecurityPluginSetup, SecurityPlugi
this.savedObjectClientWrapper = new SecuritySavedObjectsClientWrapper();
}

isAnonymousPage(request: OpenSearchDashboardsRequest) {
if (!request.headers || !request.headers.referer) {
return false;
}

try {
const url = new URL(request.headers.referer as string);
const pathsToIgnore = ['login', 'logout', 'customerror'];
return pathsToIgnore.includes(url.pathname?.split('/').pop() || '');
} catch (error: any) {
this.logger.error(`Could not parse the referer for the 1: ${error.stack}`);
}
}

isReadOnlyTenant(authInfo: any): boolean {
const currentTenant = authInfo.user_requested_tenant || globalTenantName;

// private tenant is not affected
if (isPrivateTenant(currentTenant)) {
return false;
}

return authInfo.tenants[currentTenant] !== true;
}

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): Partial<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 {
const cookie = await securitySessionStorageFactory.asScoped(request).get();
let headers = request.headers;

if (!auth.requestIncludesAuthInfo(request) && cookie) {
headers = auth.buildAuthHeaderFromCookie(cookie, request);
}

const authInfo = await securityClient.authinfo(request, headers);

if (!authInfo.user_requested_tenant && cookie) {
authInfo.user_requested_tenant = cookie.tenant;
}

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 @@ -141,6 +243,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 Expand Up @@ -197,4 +302,4 @@ export class SecurityPlugin implements Plugin<SecurityPluginSetup, SecurityPlugi
}

public stop() {}
}
}

0 comments on commit 87d40fa

Please sign in to comment.