mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Prepare the Security domain HTTP APIs for Serverless (#162087)
Closes #161337 ## Summary Uses build flavor(see #161930) to disable specific Kibana security, spaces, and encrypted saved objects HTTP API routes in serverless (see details in #161337). HTTP APIs that will be public in serverless have been handled in #162523. **IMPORTANT: This PR leaves login, user, and role routes enabled. The primary reason for this is due to several testing mechanisms that rely on basic authentication and custom roles (UI, Cypress). These tests will be modified to use SAML authentication and serverless roles in the immediate future. Once this occurs, we will disable these routes.** ### Testing This PR also implements testing API access in serverless. - The testing strategy for disabled routes in serverless is to verify a `404 not found `response. - The testing strategy for internal access routes in serverless is to verify that without the internal request header (`x-elastic-internal-origin`), a `400 bad request response` is received, then verify that with the internal request header, a `200 ok response` is received. - The strategy for public routes in serverless is to verify a `200 ok` or `203 redirect` is received. ~~blocked by #161930~~ ~~blocked by #162149 for test implementation~~ --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@elastic.co>
This commit is contained in:
parent
f4f286f965
commit
fe0ffab1da
56 changed files with 1578 additions and 183 deletions
|
@ -48,6 +48,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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>({
|
||||
|
|
|
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -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',
|
||||
|
@ -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();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ 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);
|
||||
|
@ -19,12 +20,12 @@ describe('View routes', () => {
|
|||
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(`
|
||||
|
@ -37,6 +38,7 @@ 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);
|
||||
|
@ -44,19 +46,19 @@ describe('View routes', () => {
|
|||
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",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
@ -64,6 +66,7 @@ describe('View routes', () => {
|
|||
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);
|
||||
|
@ -71,19 +74,19 @@ describe('View routes', () => {
|
|||
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",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
@ -91,6 +94,7 @@ describe('View routes', () => {
|
|||
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);
|
||||
|
@ -98,19 +102,44 @@ describe('View routes', () => {
|
|||
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",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ export class SpacesPlugin
|
|||
|
||||
private defaultSpaceService?: DefaultSpaceService;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {
|
||||
this.config$ = initializerContext.config.create<ConfigType>();
|
||||
this.log = initializerContext.logger.get();
|
||||
this.spacesService = new SpacesService();
|
||||
|
@ -148,18 +148,21 @@ export class SpacesPlugin
|
|||
logger: this.log,
|
||||
});
|
||||
|
||||
const externalRouter = core.http.createRouter<SpacesRequestHandlerContext>();
|
||||
initExternalSpacesApi({
|
||||
externalRouter,
|
||||
log: this.log,
|
||||
getStartServices: core.getStartServices,
|
||||
getSpacesService,
|
||||
usageStatsServicePromise,
|
||||
});
|
||||
const router = core.http.createRouter<SpacesRequestHandlerContext>();
|
||||
|
||||
initExternalSpacesApi(
|
||||
{
|
||||
router,
|
||||
log: this.log,
|
||||
getStartServices: core.getStartServices,
|
||||
getSpacesService,
|
||||
usageStatsServicePromise,
|
||||
},
|
||||
this.initializerContext.env.packageInfo.buildFlavor
|
||||
);
|
||||
|
||||
const internalRouter = core.http.createRouter<SpacesRequestHandlerContext>();
|
||||
initInternalSpacesApi({
|
||||
internalRouter,
|
||||
router,
|
||||
getSpacesService,
|
||||
});
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ describe('copy to space', () => {
|
|||
});
|
||||
|
||||
initCopyToSpacesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -24,10 +24,10 @@ const areObjectsUnique = (objects: SavedObjectIdentifier[]) =>
|
|||
_.uniqBy(objects, (o: SavedObjectIdentifier) => `${o.type}:${o.id}`).length === objects.length;
|
||||
|
||||
export function initCopyToSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, getSpacesService, usageStatsServicePromise, getStartServices } = deps;
|
||||
const { router, getSpacesService, usageStatsServicePromise, getStartServices } = deps;
|
||||
const usageStatsClientPromise = usageStatsServicePromise.then(({ getClient }) => getClient());
|
||||
|
||||
externalRouter.post(
|
||||
router.post(
|
||||
{
|
||||
path: '/api/spaces/_copy_saved_objects',
|
||||
options: {
|
||||
|
@ -137,7 +137,7 @@ export function initCopyToSpacesApi(deps: ExternalRouteDeps) {
|
|||
})
|
||||
);
|
||||
|
||||
externalRouter.post(
|
||||
router.post(
|
||||
{
|
||||
path: '/api/spaces/_resolve_copy_saved_objects_errors',
|
||||
options: {
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('Spaces Public API', () => {
|
|||
});
|
||||
|
||||
initDeleteSpacesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -15,9 +15,9 @@ import { wrapError } from '../../../lib/errors';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initDeleteSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, log, getSpacesService } = deps;
|
||||
const { router, log, getSpacesService } = deps;
|
||||
|
||||
externalRouter.delete(
|
||||
router.delete(
|
||||
{
|
||||
path: '/api/spaces/space/{id}',
|
||||
validate: {
|
||||
|
|
|
@ -65,7 +65,7 @@ describe('_disable_legacy_url_aliases', () => {
|
|||
});
|
||||
|
||||
initDisableLegacyUrlAliasesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -12,10 +12,10 @@ import { wrapError } from '../../../lib/errors';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initDisableLegacyUrlAliasesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, getSpacesService, usageStatsServicePromise } = deps;
|
||||
const { router, getSpacesService, usageStatsServicePromise } = deps;
|
||||
const usageStatsClientPromise = usageStatsServicePromise.then(({ getClient }) => getClient());
|
||||
|
||||
externalRouter.post(
|
||||
router.post(
|
||||
{
|
||||
path: '/api/spaces/_disable_legacy_url_aliases',
|
||||
validate: {
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('GET space', () => {
|
|||
});
|
||||
|
||||
initGetSpaceApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -13,9 +13,9 @@ import { wrapError } from '../../../lib/errors';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initGetSpaceApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, getSpacesService } = deps;
|
||||
const { router, getSpacesService } = deps;
|
||||
|
||||
externalRouter.get(
|
||||
router.get(
|
||||
{
|
||||
path: '/api/spaces/space/{id}',
|
||||
validate: {
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('GET /spaces/space', () => {
|
|||
});
|
||||
|
||||
initGetAllSpacesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -13,9 +13,9 @@ import { wrapError } from '../../../lib/errors';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initGetAllSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, log, getSpacesService } = deps;
|
||||
const { router, log, getSpacesService } = deps;
|
||||
|
||||
externalRouter.get(
|
||||
router.get(
|
||||
{
|
||||
path: '/api/spaces/space',
|
||||
validate: {
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('get shareable references', () => {
|
|||
spacesClientService: clientServiceStart,
|
||||
});
|
||||
initGetShareableReferencesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -12,9 +12,9 @@ import { wrapError } from '../../../lib/errors';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initGetShareableReferencesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, getStartServices } = deps;
|
||||
const { router, getStartServices } = deps;
|
||||
|
||||
externalRouter.post(
|
||||
router.post(
|
||||
{
|
||||
path: '/api/spaces/_get_shareable_references',
|
||||
validate: {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { BuildFlavor } from '@kbn/config/src/types';
|
||||
import type { CoreSetup, Logger } from '@kbn/core/server';
|
||||
|
||||
import { initCopyToSpacesApi } from './copy_to_space';
|
||||
|
@ -21,21 +22,28 @@ import type { SpacesRouter } from '../../../types';
|
|||
import type { UsageStatsServiceSetup } from '../../../usage_stats';
|
||||
|
||||
export interface ExternalRouteDeps {
|
||||
externalRouter: SpacesRouter;
|
||||
router: SpacesRouter;
|
||||
getStartServices: CoreSetup['getStartServices'];
|
||||
getSpacesService: () => SpacesServiceStart;
|
||||
usageStatsServicePromise: Promise<UsageStatsServiceSetup>;
|
||||
log: Logger;
|
||||
}
|
||||
|
||||
export function initExternalSpacesApi(deps: ExternalRouteDeps) {
|
||||
initDeleteSpacesApi(deps);
|
||||
export function initExternalSpacesApi(deps: ExternalRouteDeps, buildFlavor: BuildFlavor) {
|
||||
// These two routes are always registered, internal in serverless by default
|
||||
initGetSpaceApi(deps);
|
||||
initGetAllSpacesApi(deps);
|
||||
initPostSpacesApi(deps);
|
||||
initPutSpacesApi(deps);
|
||||
initCopyToSpacesApi(deps);
|
||||
initUpdateObjectsSpacesApi(deps);
|
||||
initGetShareableReferencesApi(deps);
|
||||
initDisableLegacyUrlAliasesApi(deps);
|
||||
|
||||
// In the serverless environment, Spaces are enabled but are effectively hidden from the user. We
|
||||
// do not support more than 1 space: the default space. These HTTP APIs for creating, deleting,
|
||||
// updating, and manipulating saved objects across multiple spaces are not needed.
|
||||
if (buildFlavor !== 'serverless') {
|
||||
initPutSpacesApi(deps);
|
||||
initDeleteSpacesApi(deps);
|
||||
initPostSpacesApi(deps);
|
||||
initCopyToSpacesApi(deps);
|
||||
initUpdateObjectsSpacesApi(deps);
|
||||
initGetShareableReferencesApi(deps);
|
||||
initDisableLegacyUrlAliasesApi(deps);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('Spaces Public API', () => {
|
|||
});
|
||||
|
||||
initPostSpacesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -15,9 +15,9 @@ import { spaceSchema } from '../../../lib/space_schema';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initPostSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, log, getSpacesService } = deps;
|
||||
const { router, log, getSpacesService } = deps;
|
||||
|
||||
externalRouter.post(
|
||||
router.post(
|
||||
{
|
||||
path: '/api/spaces/space',
|
||||
validate: {
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('PUT /api/spaces/space', () => {
|
|||
});
|
||||
|
||||
initPutSpacesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -15,9 +15,9 @@ import { spaceSchema } from '../../../lib/space_schema';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initPutSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, getSpacesService } = deps;
|
||||
const { router, getSpacesService } = deps;
|
||||
|
||||
externalRouter.put(
|
||||
router.put(
|
||||
{
|
||||
path: '/api/spaces/space/{id}',
|
||||
validate: {
|
||||
|
|
|
@ -62,7 +62,7 @@ describe('update_objects_spaces', () => {
|
|||
spacesClientService: clientServiceStart,
|
||||
});
|
||||
initUpdateObjectsSpacesApi({
|
||||
externalRouter: router,
|
||||
router,
|
||||
getStartServices: async () => [coreStart, {}, {}],
|
||||
log,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
|
|
|
@ -14,7 +14,7 @@ import { SPACE_ID_REGEX } from '../../../lib/space_schema';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initUpdateObjectsSpacesApi(deps: ExternalRouteDeps) {
|
||||
const { externalRouter, getStartServices } = deps;
|
||||
const { router, getStartServices } = deps;
|
||||
|
||||
const spacesSchema = schema.arrayOf(
|
||||
schema.string({
|
||||
|
@ -33,7 +33,7 @@ export function initUpdateObjectsSpacesApi(deps: ExternalRouteDeps) {
|
|||
}
|
||||
);
|
||||
|
||||
externalRouter.post(
|
||||
router.post(
|
||||
{
|
||||
path: '/api/spaces/_update_objects_spaces',
|
||||
validate: {
|
||||
|
|
|
@ -26,7 +26,7 @@ describe('GET /internal/spaces/_active_space', () => {
|
|||
});
|
||||
|
||||
initGetActiveSpaceApi({
|
||||
internalRouter: router,
|
||||
router,
|
||||
getSpacesService: () =>
|
||||
service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
|
|
|
@ -10,9 +10,9 @@ import { wrapError } from '../../../lib/errors';
|
|||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
export function initGetActiveSpaceApi(deps: InternalRouteDeps) {
|
||||
const { internalRouter, getSpacesService } = deps;
|
||||
const { router, getSpacesService } = deps;
|
||||
|
||||
internalRouter.get(
|
||||
router.get(
|
||||
{
|
||||
path: '/internal/spaces/_active_space',
|
||||
validate: false,
|
||||
|
|
|
@ -10,7 +10,7 @@ import type { SpacesServiceStart } from '../../../spaces_service/spaces_service'
|
|||
import type { SpacesRouter } from '../../../types';
|
||||
|
||||
export interface InternalRouteDeps {
|
||||
internalRouter: SpacesRouter;
|
||||
router: SpacesRouter;
|
||||
getSpacesService: () => SpacesServiceStart;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,11 @@ const createMockDebugLogger = () => {
|
|||
};
|
||||
|
||||
const createMockConfig = (
|
||||
mockConfig: ConfigType = { enabled: true, maxSpaces: 1000, allowFeatureVisibility: true }
|
||||
mockConfig: ConfigType = {
|
||||
enabled: true,
|
||||
maxSpaces: 1000,
|
||||
allowFeatureVisibility: true,
|
||||
}
|
||||
) => {
|
||||
return ConfigSchema.validate(mockConfig, { serverless: !mockConfig.allowFeatureVisibility });
|
||||
};
|
||||
|
@ -209,7 +213,11 @@ describe('#create', () => {
|
|||
total: maxSpaces - 1,
|
||||
} as any);
|
||||
|
||||
const mockConfig = createMockConfig({ enabled: true, maxSpaces, allowFeatureVisibility: true });
|
||||
const mockConfig = createMockConfig({
|
||||
enabled: true,
|
||||
maxSpaces,
|
||||
allowFeatureVisibility: true,
|
||||
});
|
||||
|
||||
const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []);
|
||||
|
||||
|
@ -235,7 +243,11 @@ describe('#create', () => {
|
|||
total: maxSpaces,
|
||||
} as any);
|
||||
|
||||
const mockConfig = createMockConfig({ enabled: true, maxSpaces, allowFeatureVisibility: true });
|
||||
const mockConfig = createMockConfig({
|
||||
enabled: true,
|
||||
maxSpaces,
|
||||
allowFeatureVisibility: true,
|
||||
});
|
||||
|
||||
const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []);
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"@kbn/core-custom-branding-browser-mocks",
|
||||
"@kbn/core-custom-branding-common",
|
||||
"@kbn/shared-ux-link-redirect-app",
|
||||
"@kbn/config",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -12,6 +12,7 @@ import { services as svlSharedServices } from '../../shared/services';
|
|||
|
||||
import { SvlCommonApiServiceProvider } from './svl_common_api';
|
||||
import { AlertingApiProvider } from './alerting_api';
|
||||
import { SamlToolsProvider } from './saml_tools';
|
||||
import { DataViewApiProvider } from './data_view_api';
|
||||
|
||||
export const services = {
|
||||
|
@ -20,6 +21,7 @@ export const services = {
|
|||
|
||||
svlCommonApi: SvlCommonApiServiceProvider,
|
||||
alertingApi: AlertingApiProvider,
|
||||
samlTools: SamlToolsProvider,
|
||||
dataViewApi: DataViewApiProvider,
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
// eslint-disable-next-line @kbn/imports/no_boundary_crossing
|
||||
import { getSAMLResponse } from '@kbn/security-api-integration-helpers/saml/saml_tools';
|
||||
import { kbnTestConfig } from '@kbn/test';
|
||||
|
||||
import { parse as parseCookie } from 'tough-cookie';
|
||||
|
||||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export function SamlToolsProvider({ getService }: FtrProviderContext) {
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const randomness = getService('randomness');
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
|
||||
function createSAMLResponse(options = {}) {
|
||||
return getSAMLResponse({
|
||||
destination: `http://localhost:${kbnTestConfig.getPort()}/api/security/saml/callback`,
|
||||
sessionIndex: String(randomness.naturalNumber()),
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
async login(username: string) {
|
||||
const samlAuthenticationResponse = await supertestWithoutAuth
|
||||
.post('/api/security/saml/callback')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send({ SAMLResponse: await createSAMLResponse({ username }) });
|
||||
expect(samlAuthenticationResponse.status).to.equal(302);
|
||||
expect(samlAuthenticationResponse.header.location).to.equal('/');
|
||||
const sessionCookie = parseCookie(samlAuthenticationResponse.header['set-cookie'][0])!;
|
||||
return { Cookie: sessionCookie.cookieString() };
|
||||
},
|
||||
};
|
||||
}
|
|
@ -36,5 +36,14 @@ export function SvlCommonApiServiceProvider({}: FtrProviderContext) {
|
|||
)}'`
|
||||
);
|
||||
},
|
||||
|
||||
assertApiNotFound(body: unknown, status: number) {
|
||||
expect(body).to.eql({
|
||||
statusCode: 404,
|
||||
error: 'Not Found',
|
||||
message: 'Not Found',
|
||||
});
|
||||
expect(status).to.eql(404);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('encrypted saved objects', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('rotate key', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/encrypted_saved_objects/_rotate_key')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -9,9 +9,19 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('serverless common API', function () {
|
||||
loadTestFile(require.resolve('./security_users'));
|
||||
loadTestFile(require.resolve('./encrypted_saved_objects'));
|
||||
loadTestFile(require.resolve('./security/anonymous'));
|
||||
loadTestFile(require.resolve('./security/api_keys'));
|
||||
loadTestFile(require.resolve('./security/authentication'));
|
||||
loadTestFile(require.resolve('./security/authorization'));
|
||||
loadTestFile(require.resolve('./security/misc'));
|
||||
loadTestFile(require.resolve('./security/response_headers'));
|
||||
loadTestFile(require.resolve('./security/role_mappings'));
|
||||
loadTestFile(require.resolve('./security/sessions'));
|
||||
loadTestFile(require.resolve('./security/users'));
|
||||
loadTestFile(require.resolve('./security/user_profiles'));
|
||||
loadTestFile(require.resolve('./security/views'));
|
||||
loadTestFile(require.resolve('./spaces'));
|
||||
loadTestFile(require.resolve('./security_response_headers'));
|
||||
loadTestFile(require.resolve('./rollups'));
|
||||
loadTestFile(require.resolve('./scripted_fields'));
|
||||
loadTestFile(require.resolve('./index_management'));
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/anonymous', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('get access capabilities', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/anonymous_access/capabilities')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get access state', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/anonymous_access/state')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
let roleMapping: { id: string; name: string; api_key: string; encoded: string };
|
||||
|
||||
describe('security/api_keys', function () {
|
||||
describe('route access', () => {
|
||||
describe('internal', () => {
|
||||
before(async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/internal/security/api_key')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({
|
||||
name: 'test',
|
||||
metadata: {},
|
||||
role_descriptors: {},
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
roleMapping = body;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/api_key?isAdmin=true')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
|
||||
if (status === 200) {
|
||||
await supertest
|
||||
.post('/internal/security/api_key/invalidate')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({
|
||||
apiKeys: body?.apiKeys,
|
||||
isAdmin: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('create', async () => {
|
||||
let body: unknown;
|
||||
let status: number;
|
||||
const requestBody = {
|
||||
name: 'create_test',
|
||||
metadata: {},
|
||||
role_descriptors: {},
|
||||
};
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/api_key')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send(requestBody));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/api_key')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send(requestBody));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(expect.objectContaining({ name: 'create_test' }));
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('update', async () => {
|
||||
let body: unknown;
|
||||
let status: number;
|
||||
const requestBody = {
|
||||
id: roleMapping.id,
|
||||
metadata: { test: 'value' },
|
||||
role_descriptors: {},
|
||||
};
|
||||
|
||||
({ body, status } = await supertest
|
||||
.put('/internal/security/api_key')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send(requestBody));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [put] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.put('/internal/security/api_key')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send(requestBody));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(expect.objectContaining({ updated: true }));
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('get all', async () => {
|
||||
let body: unknown;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/api_key?isAdmin=true')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/api_key?isAdmin=true')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
apiKeys: expect.arrayContaining([expect.objectContaining({ id: roleMapping.id })]),
|
||||
})
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('get enabled', async () => {
|
||||
let body: unknown;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/api_key/_enabled')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/api_key/_enabled')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual({ apiKeysEnabled: true });
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('invalidate', async () => {
|
||||
let body: unknown;
|
||||
let status: number;
|
||||
const requestBody = {
|
||||
apiKeys: [
|
||||
{
|
||||
id: roleMapping.id,
|
||||
name: roleMapping.name,
|
||||
},
|
||||
],
|
||||
isAdmin: true,
|
||||
};
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/api_key/invalidate')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send(requestBody));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/api_key/invalidate')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send(requestBody));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual({
|
||||
errors: [],
|
||||
itemsInvalidated: [
|
||||
{
|
||||
id: roleMapping.id,
|
||||
name: roleMapping.name,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/authentication', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
// ToDo: uncomment when we disable login
|
||||
// it('login', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .post('/internal/security/login')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
it('logout (deprecated)', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/v1/logout')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get current user (deprecated)', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/v1/me')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('acknowledge access agreement', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/internal/security/access_agreement/acknowledge')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
describe('OIDC', () => {
|
||||
it('OIDC implicit', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/oidc/implicit')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC implicit (deprecated)', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/v1/oidc/implicit')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC implicit.js', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/oidc/implicit.js')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC callback', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/oidc/callback')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC callback (deprecated)', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/v1/oidc')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC login', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/security/oidc/initiate_login')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC login (deprecated)', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/security/v1/oidc')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('OIDC 3rd party login', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/oidc/initiate_login')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
it('SAML callback (deprecated)', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/security/v1/saml')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal', () => {
|
||||
it('get current user', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/me')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/me')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual({
|
||||
authentication_provider: { name: '__http__', type: 'http' },
|
||||
authentication_realm: { name: 'reserved', type: 'reserved' },
|
||||
authentication_type: 'realm',
|
||||
elastic_cloud_user: false,
|
||||
email: null,
|
||||
enabled: true,
|
||||
full_name: null,
|
||||
lookup_realm: { name: 'reserved', type: 'reserved' },
|
||||
metadata: { _reserved: true },
|
||||
roles: ['superuser'],
|
||||
username: 'elastic',
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
// ToDo: remove when we disable login
|
||||
it('login', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/login')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/login')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('public', () => {
|
||||
it('logout', async () => {
|
||||
const { status } = await supertest.get('/api/security/logout');
|
||||
expect(status).toBe(302);
|
||||
});
|
||||
|
||||
it('SAML callback', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/security/saml/callback')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send({
|
||||
SAMLResponse: '',
|
||||
});
|
||||
|
||||
// Should fail with 401 (not 404) because there is no valid SAML response in the request body
|
||||
expect(body).toEqual({
|
||||
error: 'Unauthorized',
|
||||
message: 'Unauthorized',
|
||||
statusCode: 401,
|
||||
});
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/authorization', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('get all privileges', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/privileges')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get built-in elasticsearch privileges', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/esPrivileges/builtin')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
// ToDo: Uncomment when we disable role APIs
|
||||
// it('create/update role', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .put('/api/security/role/test')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('get role', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .get('/api/security/role/superuser')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('get all roles', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .get('/api/security/role')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('delete role', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .delete('/api/security/role/superuser')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
it('get shared saved object permissions', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/_share_saved_object_permissions')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
// ToDo: remove when we disable role APIs
|
||||
describe('internal', () => {
|
||||
it('create/update role', async () => {
|
||||
const { status } = await supertest
|
||||
.put('/api/security/role/test')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('get role', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/api/security/role/superuser')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('get all roles', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/api/security/role')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('delete role', async () => {
|
||||
const { status } = await supertest
|
||||
.delete('/api/security/role/superuser')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('public', () => {
|
||||
it('reset session page', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/internal/security/reset_session_page.js')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/misc', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('get index fields', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/fields/test')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({ params: 'params' });
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('fix deprecated roles', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/internal/security/deprecations/kibana_user_role/_fix_users')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('fix deprecated role mappings', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/internal/security/deprecations/kibana_user_role/_fix_role_mappings')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get security checkup state', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/security_checkup/state')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal', () => {
|
||||
it('get record auth type', async () => {
|
||||
const { status } = await supertest
|
||||
.post('/internal/security/analytics/_record_auth_type')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -6,13 +6,13 @@
|
|||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security response headers', function () {
|
||||
describe('security/response_headers', function () {
|
||||
const defaultCSP = `script-src 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'; frame-ancestors 'self'`;
|
||||
const defaultCOOP = 'same-origin';
|
||||
const defaultPermissionsPolicy =
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/role_mappings', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('create/update role mapping', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/internal/security/role_mapping/test')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get role mapping', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/role_mapping/test')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get all role mappings', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/role_mapping')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('delete role mapping', async () => {
|
||||
// this test works because the message for a missing endpoint is different from a missing role mapping
|
||||
const { body, status } = await supertest
|
||||
.delete('/internal/security/role_mapping/test')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('role mapping feature check', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/_check_role_mapping_features')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/sessions', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('invalidate', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/security/session/_invalidate')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({ match: 'all' });
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal', () => {
|
||||
it('get session info', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/session')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/security/session')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect 204 because there is no session
|
||||
expect(status).toBe(204);
|
||||
});
|
||||
|
||||
it('extend', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/session')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [post] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.post('/internal/security/session')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect redirect
|
||||
expect(status).toBe(302);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { kibanaTestUser } from '@kbn/test';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const samlTools = getService('samlTools');
|
||||
|
||||
describe('security/user_profiles', function () {
|
||||
describe('route access', () => {
|
||||
describe('internal', () => {
|
||||
it('update', async () => {
|
||||
const { status } = await supertestWithoutAuth
|
||||
.post(`/internal/security/user_profile/_data`)
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.set(await samlTools.login(kibanaTestUser.username))
|
||||
.send({ key: 'value' });
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('get current', async () => {
|
||||
const { status } = await supertestWithoutAuth
|
||||
.get(`/internal/security/user_profile`)
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.set(await samlTools.login(kibanaTestUser.username));
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('bulk get', async () => {
|
||||
const { status } = await supertestWithoutAuth
|
||||
.get(`/internal/security/user_profile`)
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.set(await samlTools.login(kibanaTestUser.username))
|
||||
.send({ uids: ['12345678'] });
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/users', function () {
|
||||
describe('route access', () => {
|
||||
// ToDo: uncomment when we disable user APIs
|
||||
// describe('disabled', () => {
|
||||
// it('get', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .get('/internal/security/users/elastic')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('get all', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .get('/internal/security/users')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('create/update', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .post(`/internal/security/users/some_testuser`)
|
||||
// .set(svlCommonApi.getInternalRequestHeader())
|
||||
// .send({ username: 'some_testuser', password: 'testpassword', roles: [] });
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('delete', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .delete(`/internal/security/users/elastic`)
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('disable', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .post(`/internal/security/users/elastic/_disable`)
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('enable', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .post(`/internal/security/users/elastic/_enable`)
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('set password', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .post(`/internal/security/users/{username}/password`)
|
||||
// .set(svlCommonApi.getInternalRequestHeader())
|
||||
// .send({
|
||||
// password: 'old_pw',
|
||||
// newPassword: 'new_pw',
|
||||
// });
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
// });
|
||||
|
||||
// ToDo: remove when we disable user APIs
|
||||
describe('internal', () => {
|
||||
it('get', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/internal/security/users/elastic')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('get all', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/internal/security/users')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('create/update', async () => {
|
||||
const { status } = await supertest
|
||||
.post(`/internal/security/users/some_testuser`)
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({ username: 'some_testuser', password: 'testpassword', roles: [] });
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('delete', async () => {
|
||||
const { status } = await supertest
|
||||
.delete(`/internal/security/users/elastic`)
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('disable', async () => {
|
||||
const { status } = await supertest
|
||||
.post(`/internal/security/users/elastic/_disable`)
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('enable', async () => {
|
||||
const { status } = await supertest
|
||||
.post(`/internal/security/users/elastic/_enable`)
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
|
||||
it('set password', async () => {
|
||||
const { status } = await supertest
|
||||
.post(`/internal/security/users/{username}/password`)
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({
|
||||
password: 'old_pw',
|
||||
newPassword: 'new_pw',
|
||||
});
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('security/views', function () {
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
// ToDo: uncomment these when we disable login routes
|
||||
// it('login', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .get('/login')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
// it('get login state', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .get('/internal/security/login_state')
|
||||
// .set(svlCommonApi.getInternalRequestHeader());
|
||||
// svlCommonApi.assertApiNotFound(body, status);
|
||||
// });
|
||||
|
||||
it('access agreement', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/security/access_agreement')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get access agreement state', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/access_agreement/state')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
describe('public', () => {
|
||||
it('login', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/login')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).toBe(302);
|
||||
});
|
||||
|
||||
it('get login state', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/internal/security/login_state')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('capture URL', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/internal/security/capture-url')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('space selector', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/spaces/space_selector')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('enter space', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/spaces/enter')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(302);
|
||||
});
|
||||
|
||||
it('account', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/security/account')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('logged out', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/security/logged_out')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('logout', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/logout')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('overwritten session', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/security/overwritten_session')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const svlCommonApi = getService('svlCommonApi');
|
||||
const supertest = getService('supertest');
|
||||
|
||||
// Test should be unskipped when the API is disabled
|
||||
// https://github.com/elastic/kibana/issues/161337
|
||||
describe.skip('security/users', function () {
|
||||
it('rejects request to create user', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post(`/internal/security/users/some_testuser`)
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({ username: 'some_testuser', password: 'testpassword', roles: [] });
|
||||
|
||||
// in a non-serverless environment this would succeed with a 200
|
||||
svlCommonApi.assertResponseStatusCode(400, status, body);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -13,44 +13,200 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
const supertest = getService('supertest');
|
||||
|
||||
describe('spaces', function () {
|
||||
it('rejects request to create a space', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/space')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
disabledFeatures: [],
|
||||
describe('route access', () => {
|
||||
describe('disabled', () => {
|
||||
it('#delete', async () => {
|
||||
const { body, status } = await supertest
|
||||
.delete('/api/spaces/space/default')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
|
||||
// normally we'd get a 400 bad request if we tried to delete the default space
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
// in a non-serverless environment this would succeed with a 200
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message:
|
||||
'Unable to create Space, this exceeds the maximum number of spaces set by the xpack.spaces.maxSpaces setting',
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
});
|
||||
it('#create', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/space')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send({
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
|
||||
it('rejects request to update a space with disabledFeatures', async () => {
|
||||
const { body, status } = await supertest
|
||||
.put('/api/spaces/space/default')
|
||||
.set(svlCommonApi.getInternalRequestHeader())
|
||||
.send({
|
||||
id: 'custom',
|
||||
name: 'Custom',
|
||||
disabledFeatures: ['some-feature'],
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
// in a non-serverless environment this would succeed with a 200
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message:
|
||||
'Unable to update Space, the disabledFeatures array must be empty when xpack.spaces.allowFeatureVisibility setting is disabled',
|
||||
it('#update requires internal header', async () => {
|
||||
const { body, status } = await supertest
|
||||
.put('/api/spaces/space/default')
|
||||
.set(svlCommonApi.getCommonRequestHeader())
|
||||
.send({
|
||||
id: 'default',
|
||||
name: 'UPDATED!',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('#copyToSpace', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/_copy_saved_objects')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('#resolveCopyToSpaceErrors', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/_resolve_copy_saved_objects_errors')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('#updateObjectsSpaces', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/_update_objects_spaces')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('#getShareableReferences', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/_get_shareable_references')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('#disableLegacyUrlAliases', async () => {
|
||||
const { body, status } = await supertest
|
||||
.post('/api/spaces/_disable_legacy_url_aliases')
|
||||
.set(svlCommonApi.getCommonRequestHeader());
|
||||
|
||||
// without a request body we would normally a 400 bad request if the endpoint was registered
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
});
|
||||
|
||||
describe('internal', () => {
|
||||
it('#get requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/api/spaces/space/default')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/api/spaces/space/default')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'default',
|
||||
})
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('#getAll requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/api/spaces/space')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/api/spaces/space')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: 'default',
|
||||
}),
|
||||
])
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
|
||||
it('#getActiveSpace requires internal header', async () => {
|
||||
let body: any;
|
||||
let status: number;
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/spaces/_active_space')
|
||||
.set(svlCommonApi.getCommonRequestHeader()));
|
||||
// expect a rejection because we're not using the internal header
|
||||
expect(body).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: expect.stringContaining(
|
||||
'method [get] exists but is not available with the current configuration'
|
||||
),
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
|
||||
({ body, status } = await supertest
|
||||
.get('/internal/spaces/_active_space')
|
||||
.set(svlCommonApi.getInternalRequestHeader()));
|
||||
// expect success because we're using the internal header
|
||||
expect(body).toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'default',
|
||||
})
|
||||
);
|
||||
expect(status).toBe(200);
|
||||
});
|
||||
});
|
||||
expect(status).toBe(400);
|
||||
});
|
||||
|
||||
// TODO: Re-enable test-suite once users can create and update spaces in the Serverless offering.
|
||||
// it('rejects request to update a space with disabledFeatures', async () => {
|
||||
// const { body, status } = await supertest
|
||||
// .put('/api/spaces/space/default')
|
||||
// .set(svlCommonApi.getInternalRequestHeader())
|
||||
// .send({
|
||||
// id: 'custom',
|
||||
// name: 'Custom',
|
||||
// disabledFeatures: ['some-feature'],
|
||||
// });
|
||||
//
|
||||
// // in a non-serverless environment this would succeed with a 200
|
||||
// expect(body).toEqual({
|
||||
// statusCode: 400,
|
||||
// error: 'Bad Request',
|
||||
// message:
|
||||
// 'Unable to update Space, the disabledFeatures array must be empty when xpack.spaces.allowFeatureVisibility setting is disabled',
|
||||
// });
|
||||
// expect(status).toBe(400);
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { request } from '@kbn/security-solution-plugin/public/management/cypress/tasks/common';
|
||||
import { isLocalhost } from '@kbn/security-solution-plugin/scripts/endpoint/common/is_localhost';
|
||||
import type { ServerlessRoleName } from '../../../../../shared/lib';
|
||||
import { STANDARD_HTTP_HEADERS } from '../../../../../shared/lib/security/default_http_headers';
|
||||
|
||||
|
@ -32,7 +31,7 @@ const sendApiLoginRequest = (
|
|||
url: url.toString(),
|
||||
body: {
|
||||
providerType: 'basic',
|
||||
providerName: isLocalhost(url.hostname) ? 'basic' : 'cloud-basic',
|
||||
providerName: 'basic',
|
||||
currentURL: '/',
|
||||
params: {
|
||||
username,
|
||||
|
|
|
@ -18,12 +18,32 @@ export default async () => {
|
|||
elasticsearch: esTestConfig.getUrlParts(),
|
||||
};
|
||||
|
||||
// "Fake" SAML provider
|
||||
const idpPath = resolve(
|
||||
__dirname,
|
||||
'../../test/security_api_integration/plugins/saml_provider/metadata.xml'
|
||||
);
|
||||
const samlIdPPlugin = resolve(
|
||||
__dirname,
|
||||
'../../test/security_api_integration/plugins/saml_provider'
|
||||
);
|
||||
|
||||
return {
|
||||
servers,
|
||||
|
||||
esTestCluster: {
|
||||
license: 'trial',
|
||||
from: 'snapshot',
|
||||
serverArgs: [
|
||||
'xpack.security.authc.token.enabled=true',
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.order=0',
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.idp.metadata.path=${idpPath}`,
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.idp.entity_id=http://www.elastic.co/saml1',
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.entity_id=http://localhost:${servers.kibana.port}`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.logout=http://localhost:${servers.kibana.port}/logout`,
|
||||
`xpack.security.authc.realms.saml.cloud-saml-kibana.sp.acs=http://localhost:${servers.kibana.port}/api/security/saml/callback`,
|
||||
'xpack.security.authc.realms.saml.cloud-saml-kibana.attributes.principal=urn:oid:0.0.7',
|
||||
],
|
||||
},
|
||||
|
||||
kbnTestServer: {
|
||||
|
@ -34,7 +54,7 @@ export default async () => {
|
|||
sourceArgs: ['--no-base-path', '--env.name=development'],
|
||||
serverArgs: [
|
||||
`--server.restrictInternalApis=true`,
|
||||
`--server.port=${kbnTestConfig.getPort()}`,
|
||||
`--server.port=${servers.kibana.port}`,
|
||||
'--status.allowAnonymous=true',
|
||||
// We shouldn't embed credentials into the URL since Kibana requests to Elasticsearch should
|
||||
// either include `kibanaServerTestUser` credentials, or credentials provided by the test
|
||||
|
@ -60,6 +80,16 @@ export default async () => {
|
|||
appenders: ['deprecation'],
|
||||
},
|
||||
])}`,
|
||||
// This ensures that we register the Security SAML API endpoints.
|
||||
// In the real world the SAML config is injected by control plane.
|
||||
// basic: { 'basic': { order: 0 } },
|
||||
`--plugin-path=${samlIdPPlugin}`,
|
||||
'--xpack.cloud.id=ftr_fake_cloud_id',
|
||||
'--xpack.security.authc.selector.enabled=false',
|
||||
`--xpack.security.authc.providers=${JSON.stringify({
|
||||
basic: { basic: { order: 0 } },
|
||||
saml: { 'cloud-saml-kibana': { order: 1, realm: 'cloud-saml-kibana' } },
|
||||
})}`,
|
||||
'--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"',
|
||||
`--server.publicBaseUrl=${servers.kibana.protocol}://${servers.kibana.hostname}:${servers.kibana.port}`,
|
||||
],
|
||||
|
|
|
@ -47,5 +47,6 @@
|
|||
"@kbn/core-http-common",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/security-api-integration-helpers",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue