Skip to content

Commit

Permalink
Revert "Revert "Prepare the Security domain HTTP APIs for Serverless (e…
Browse files Browse the repository at this point in the history
…lastic#162087)""

This reverts commit 35e777e.
  • Loading branch information
Ikuni17 committed Aug 25, 2023
1 parent 48911eb commit bc7b1ab
Show file tree
Hide file tree
Showing 56 changed files with 1,580 additions and 186 deletions.
3 changes: 3 additions & 0 deletions config/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ xpack.cloud_integrations.data_migration.enabled: false
data.search.sessions.enabled: false
advanced_settings.enabled: false

# Disable the browser-side functionality that depends on SecurityCheckupGetStateRoutes
xpack.security.showInsecureClusterWarning: false

# Disable UI of security management plugins
xpack.security.ui.userManagementEnabled: false
xpack.security.ui.roleManagementEnabled: false
Expand Down
30 changes: 17 additions & 13 deletions x-pack/plugins/encrypted_saved_objects/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,23 @@ export class EncryptedSavedObjectsPlugin
getStartServices: core.getStartServices,
});

defineRoutes({
router: core.http.createRouter(),
logger: this.initializerContext.logger.get('routes'),
encryptionKeyRotationService: Object.freeze(
new EncryptionKeyRotationService({
logger: this.logger.get('key-rotation-service'),
service,
getStartServices: core.getStartServices,
security: deps.security,
})
),
config,
});
// In the serverless environment, the encryption keys for saved objects is managed internally and never
// exposed to users and administrators, eliminating the need for any public Encrypted Saved Objects HTTP APIs
if (this.initializerContext.env.packageInfo.buildFlavor !== 'serverless') {
defineRoutes({
router: core.http.createRouter(),
logger: this.initializerContext.logger.get('routes'),
encryptionKeyRotationService: Object.freeze(
new EncryptionKeyRotationService({
logger: this.logger.get('key-rotation-service'),
service,
getStartServices: core.getStartServices,
security: deps.security,
})
),
config,
});
}

return {
canEncrypt,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/security/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ export class SecurityPlugin
getAnonymousAccessService: this.getAnonymousAccess,
getUserProfileService: this.getUserProfileService,
analyticsService: this.analyticsService.setup({ analytics: core.analytics }),
buildFlavor: this.initializerContext.env.packageInfo.buildFlavor,
});

return Object.freeze<SecurityPluginSetup>({
Expand Down
46 changes: 30 additions & 16 deletions x-pack/plugins/security/server/routes/authentication/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ export function defineCommonRoutes({
basePath,
license,
logger,
buildFlavor,
}: RouteDefinitionParams) {
// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
for (const path of ['/api/security/logout', '/api/security/v1/logout']) {
// For a serverless build, do not register deprecated versioned routes
for (const path of [
'/api/security/logout',
...(buildFlavor !== 'serverless' ? ['/api/security/v1/logout'] : []),
]) {
router.get(
{
path,
Expand Down Expand Up @@ -79,7 +84,11 @@ export function defineCommonRoutes({
}

// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
for (const path of ['/internal/security/me', '/api/security/v1/me']) {
// For a serverless build, do not register deprecated versioned routes
for (const path of [
'/internal/security/me',
...(buildFlavor !== 'serverless' ? ['/api/security/v1/me'] : []),
]) {
router.get(
{ path, validate: false },
createLicensedRouteHandler((context, request, response) => {
Expand Down Expand Up @@ -123,6 +132,8 @@ export function defineCommonRoutes({
return undefined;
}

// Register the login route for serverless for the time being. Note: This route will move into the buildFlavor !== 'serverless' block below. See next line.
// ToDo: In the serverless environment, we do not support API login - the only valid authentication methodology (or maybe just method or mechanism?) is SAML
router.post(
{
path: '/internal/security/login',
Expand Down Expand Up @@ -169,20 +180,23 @@ export function defineCommonRoutes({
})
);

router.post(
{ path: '/internal/security/access_agreement/acknowledge', validate: false },
createLicensedRouteHandler(async (context, request, response) => {
// If license doesn't allow access agreement we shouldn't handle request.
if (!license.getFeatures().allowAccessAgreement) {
logger.warn(`Attempted to acknowledge access agreement when license doesn't allow it.`);
return response.forbidden({
body: { message: `Current license doesn't support access agreement.` },
});
}
if (buildFlavor !== 'serverless') {
// In the serverless offering, the access agreement functionality isn't available.
router.post(
{ path: '/internal/security/access_agreement/acknowledge', validate: false },
createLicensedRouteHandler(async (context, request, response) => {
// If license doesn't allow access agreement we shouldn't handle request.
if (!license.getFeatures().allowAccessAgreement) {
logger.warn(`Attempted to acknowledge access agreement when license doesn't allow it.`);
return response.forbidden({
body: { message: `Current license doesn't support access agreement.` },
});
}

await getAuthenticationService().acknowledgeAccessAgreement(request);
await getAuthenticationService().acknowledgeAccessAgreement(request);

return response.noContent();
})
);
return response.noContent();
})
);
}
}
7 changes: 6 additions & 1 deletion x-pack/plugins/security/server/routes/authentication/saml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ export function defineSAMLRoutes({
getAuthenticationService,
basePath,
logger,
buildFlavor,
}: RouteDefinitionParams) {
// Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used.
for (const path of ['/api/security/saml/callback', '/api/security/v1/saml']) {
// For a serverless build, do not register deprecated versioned routes
for (const path of [
'/api/security/saml/callback',
...(buildFlavor !== 'serverless' ? ['/api/security/v1/saml'] : []),
]) {
router.post(
{
path,
Expand Down
12 changes: 9 additions & 3 deletions x-pack/plugins/security/server/routes/authorization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ import { defineShareSavedObjectPermissionRoutes } from './spaces';
import type { RouteDefinitionParams } from '..';

export function defineAuthorizationRoutes(params: RouteDefinitionParams) {
defineRolesRoutes(params);
definePrivilegesRoutes(params);
// The reset session endpoint is registered with httpResources and should remain public in serverless
resetSessionPageRoutes(params);
defineShareSavedObjectPermissionRoutes(params);
defineRolesRoutes(params); // Temporarily allow role APIs (ToDo: move to non-serverless block below)

// In the serverless environment, roles, privileges, and permissions are managed internally and only
// exposed to users and administrators via control plane UI, eliminating the need for any public HTTP APIs.
if (params.buildFlavor !== 'serverless') {
definePrivilegesRoutes(params);
defineShareSavedObjectPermissionRoutes(params);
}
}
23 changes: 15 additions & 8 deletions x-pack/plugins/security/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { Observable } from 'rxjs';

import type { BuildFlavor } from '@kbn/config/src/types';
import type { HttpResources, IBasePath, Logger } from '@kbn/core/server';
import type { KibanaFeature } from '@kbn/features-plugin/server';
import type { PublicMethodsOf } from '@kbn/utility-types';
Expand Down Expand Up @@ -54,20 +55,26 @@ export interface RouteDefinitionParams {
getUserProfileService: () => UserProfileServiceStartInternal;
getAnonymousAccessService: () => AnonymousAccessServiceStart;
analyticsService: AnalyticsServiceSetup;
buildFlavor: BuildFlavor;
}

export function defineRoutes(params: RouteDefinitionParams) {
defineAnalyticsRoutes(params);
defineApiKeysRoutes(params);
defineAuthenticationRoutes(params);
defineAuthorizationRoutes(params);
defineSessionManagementRoutes(params);
defineApiKeysRoutes(params);
defineIndicesRoutes(params);
defineUsersRoutes(params);
defineUserProfileRoutes(params);
defineRoleMappingRoutes(params);
defineUsersRoutes(params); // Temporarily allow user APIs (ToDo: move to non-serverless block below)
defineViewRoutes(params);
defineDeprecationsRoutes(params);
defineAnonymousAccessRoutes(params);
defineSecurityCheckupGetStateRoutes(params);
defineAnalyticsRoutes(params);

// In the serverless environment...
if (params.buildFlavor !== 'serverless') {
defineAnonymousAccessRoutes(params); // anonymous access is disabled
defineDeprecationsRoutes(params); // deprecated kibana user roles are not applicable, these HTTP APIs are not needed
defineIndicesRoutes(params); // the ES privileges form used to help define roles (only consumer) is disabled, so there is no need for these HTTP APIs
defineRoleMappingRoutes(params); // role mappings are managed internally, based on configurations in control plane, these HTTP APIs are not needed
defineSecurityCheckupGetStateRoutes(params); // security checkup is not applicable, these HTTP APIs are not needed
// defineUsersRoutes(params); // the native realm is not enabled (there is only Elastic cloud SAML), no user HTTP API routes are needed
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,12 @@ import type { RouteDefinitionParams } from '..';
export function defineSessionManagementRoutes(params: RouteDefinitionParams) {
defineSessionInfoRoutes(params);
defineSessionExtendRoutes(params);
defineInvalidateSessionsRoutes(params);

// The invalidate session API was introduced to address situations where the session index
// could grow rapidly - when session timeouts are disabled, or with anonymous access.
// In the serverless environment, sessions timeouts are always be enabled, and there is no
// anonymous access. This eliminates the need for an invalidate session HTTP API.
if (params.buildFlavor !== 'serverless') {
defineInvalidateSessionsRoutes(params);
}
}
55 changes: 42 additions & 13 deletions x-pack/plugins/security/server/routes/views/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@ describe('View routes', () => {
it('does not register Login routes if both `basic` and `token` providers are disabled', () => {
const routeParamsMock = routeDefinitionParamsMock.create({
authc: { providers: { pki: { pki1: { order: 0 } } } },
accessAgreement: { message: 'some-message' },
});

defineViewRoutes(routeParamsMock);

expect(routeParamsMock.httpResources.register.mock.calls.map(([{ path }]) => path))
.toMatchInlineSnapshot(`
Array [
"/security/access_agreement",
"/security/account",
"/internal/security/capture-url",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
"/internal/security/capture-url",
"/security/access_agreement",
]
`);
expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(`
Expand All @@ -37,80 +38,108 @@ describe('View routes', () => {
it('registers Login routes if `basic` provider is enabled', () => {
const routeParamsMock = routeDefinitionParamsMock.create({
authc: { providers: { basic: { basic1: { order: 0 } } } },
accessAgreement: { message: 'some-message' },
});

defineViewRoutes(routeParamsMock);

expect(routeParamsMock.httpResources.register.mock.calls.map(([{ path }]) => path))
.toMatchInlineSnapshot(`
Array [
"/login",
"/security/access_agreement",
"/security/account",
"/internal/security/capture-url",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
"/internal/security/capture-url",
"/security/access_agreement",
"/login",
]
`);
expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(`
Array [
"/internal/security/login_state",
"/internal/security/access_agreement/state",
"/internal/security/login_state",
]
`);
});

it('registers Login routes if `token` provider is enabled', () => {
const routeParamsMock = routeDefinitionParamsMock.create({
authc: { providers: { token: { token1: { order: 0 } } } },
accessAgreement: { message: 'some-message' },
});

defineViewRoutes(routeParamsMock);

expect(routeParamsMock.httpResources.register.mock.calls.map(([{ path }]) => path))
.toMatchInlineSnapshot(`
Array [
"/login",
"/security/access_agreement",
"/security/account",
"/internal/security/capture-url",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
"/internal/security/capture-url",
"/security/access_agreement",
"/login",
]
`);
expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(`
Array [
"/internal/security/login_state",
"/internal/security/access_agreement/state",
"/internal/security/login_state",
]
`);
});

it('registers Login routes if Login Selector is enabled even if both `token` and `basic` providers are not enabled', () => {
const routeParamsMock = routeDefinitionParamsMock.create({
authc: { selector: { enabled: true }, providers: { pki: { pki1: { order: 0 } } } },
accessAgreement: { message: 'some-message' },
});

defineViewRoutes(routeParamsMock);

expect(routeParamsMock.httpResources.register.mock.calls.map(([{ path }]) => path))
.toMatchInlineSnapshot(`
Array [
"/login",
"/security/access_agreement",
"/security/account",
"/internal/security/capture-url",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
"/security/access_agreement",
"/login",
]
`);
expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(`
Array [
"/internal/security/access_agreement/state",
"/internal/security/login_state",
]
`);
});

it('does not register access agreement routes if access agreement is not enabled', () => {
const routeParamsMock = routeDefinitionParamsMock.create({
authc: { providers: { basic: { basic1: { order: 0 } } } },
});

defineViewRoutes(routeParamsMock);

expect(routeParamsMock.httpResources.register.mock.calls.map(([{ path }]) => path))
.toMatchInlineSnapshot(`
Array [
"/security/account",
"/internal/security/capture-url",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
"/login",
]
`);
expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(`
Array [
"/internal/security/login_state",
"/internal/security/access_agreement/state",
]
`);
});
Expand Down
20 changes: 13 additions & 7 deletions x-pack/plugins/security/server/routes/views/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@ import { defineOverwrittenSessionRoutes } from './overwritten_session';
import type { RouteDefinitionParams } from '..';

export function defineViewRoutes(params: RouteDefinitionParams) {
defineAccountManagementRoutes(params);
defineCaptureURLRoutes(params);
defineLoggedOutRoutes(params);
defineLogoutRoutes(params);
defineOverwrittenSessionRoutes(params);

if (
params.config.accessAgreement?.message ||
params.config.authc.sortedProviders.some(({ hasAccessAgreement }) => hasAccessAgreement)
) {
defineAccessAgreementRoutes(params);
}

if (
params.config.authc.selector.enabled ||
params.config.authc.sortedProviders.some(({ type }) => type === 'basic' || type === 'token')
) {
defineLoginRoutes(params);
}

defineAccessAgreementRoutes(params);
defineAccountManagementRoutes(params);
defineLoggedOutRoutes(params);
defineLogoutRoutes(params);
defineOverwrittenSessionRoutes(params);
defineCaptureURLRoutes(params);
}
Loading

0 comments on commit bc7b1ab

Please sign in to comment.