mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Config to toggle Custom Roles (#176200)
## Summary
Closes https://github.com/elastic/kibana/issues/174771
While the security team works on Custom Roles for serverless, we want to
hide the content behind a feature flag.
An existing config option that was used to hide the Roles UI during the
initial phases of serverless has been repurposed, and will now toggle
both the Roles UI and the Roles Routes
`xpack.security.confg.ui.roleManagementEnabled` has been changed to
`xpack.security.confg.roleManagementEnabled` and will have to be set to
`true` in a config file while in serverless mode to show the Roles card
on the management screen and enable the UI/routes.
## Reviewers
Ive included a `viewer`:`changeme` user for testing (It will be removed
after approval).
## Testing
### xpack.security.config.roleManagementEnabled
1. In your `kibana.yml`, add
`xpack.security.confg.roleManagementEnabled: true`
2. Start up in serverless mode locally, login in with
`elastic_serverless`:`changeme`
3. Click `Project Settings` > `Management`
4. `Roles` card should display under `Other`
5. Navigate to `Roles`, it displays, but the `Edit Roles` page does not
work yet.
### Test as Viewer
1. In your `kibana.yml`, add either above option as you prefer
2. Start up in serverless mode locally, login in with
`viewer`:`changeme`
3. Click `Project Settings` > `Management`
4. `Roles` card should NOT display under `Other` and the `roles` URL
should not work.
## Screenshots
Roles card
<img width="1281" alt="Screenshot 2024-02-05 at 3 22 12 PM"
src="a1285ada
-7ff7-495f-88a6-9847b3245518">
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
This commit is contained in:
parent
f37d1dd050
commit
35656542eb
32 changed files with 314 additions and 148 deletions
|
@ -64,7 +64,6 @@ xpack.security.showInsecureClusterWarning: false
|
|||
|
||||
# Disable UI of security management plugins
|
||||
xpack.security.ui.userManagementEnabled: false
|
||||
xpack.security.ui.roleManagementEnabled: false
|
||||
xpack.security.ui.roleMappingManagementEnabled: false
|
||||
|
||||
# Enforce restring access to internal APIs see https://github.com/elastic/kibana/issues/151940
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
superuser:elastic_serverless
|
||||
system_indices_superuser:system_indices_superuser
|
||||
|
||||
t1_analyst:t1_analyst
|
||||
t2_analyst:t2_analyst
|
||||
t3_analyst:t3_analyst
|
||||
|
|
|
@ -134,4 +134,12 @@ export const appDefinitions: Record<AppId, AppDefinition> = {
|
|||
}),
|
||||
icon: 'gear',
|
||||
},
|
||||
|
||||
[AppIds.ROLES]: {
|
||||
category: appCategories.OTHER,
|
||||
description: i18n.translate('management.landing.withCardNavigation.rolesDescription', {
|
||||
defaultMessage: 'Allow custom roles to be created for users.',
|
||||
}),
|
||||
icon: 'usersRolesApp',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@ export enum AppIds {
|
|||
RULES = 'triggersActions',
|
||||
MAINTENANCE_WINDOWS = 'maintenanceWindows',
|
||||
SERVERLESS_SETTINGS = 'settings',
|
||||
ROLES = 'roles',
|
||||
}
|
||||
|
||||
// Create new type that is a union of all the appId values
|
||||
|
|
|
@ -311,6 +311,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.security.showInsecureClusterWarning (boolean)',
|
||||
'xpack.security.showNavLinks (boolean)',
|
||||
'xpack.security.ui (any)',
|
||||
'xpack.security.roleManagementEnabled (any)',
|
||||
'xpack.spaces.maxSpaces (number)',
|
||||
'xpack.spaces.allowFeatureVisibility (any)',
|
||||
'xpack.securitySolution.enableExperimental (array)',
|
||||
|
@ -374,6 +375,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.security.showInsecureClusterWarning (boolean)',
|
||||
'xpack.security.showNavLinks (boolean)',
|
||||
'xpack.security.ui (any)',
|
||||
'xpack.security.roleManagementEnabled (any)',
|
||||
|
||||
'telemetry.allowChangingOptInStatus (boolean)',
|
||||
'telemetry.appendServerlessChannelsSuffix (any)', // It's a boolean (any because schema.conditional)
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface ConfigType {
|
|||
sameSiteCookies: 'Strict' | 'Lax' | 'None' | undefined;
|
||||
ui: {
|
||||
userManagementEnabled: boolean;
|
||||
roleManagementEnabled: boolean;
|
||||
roleMappingManagementEnabled: boolean;
|
||||
};
|
||||
roleManagementEnabled: boolean | undefined;
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@ import type {
|
|||
import { createManagementSectionMock } from '@kbn/management-plugin/public/mocks';
|
||||
|
||||
import { apiKeysManagementApp } from './api_keys';
|
||||
import type { ManagementAppConfigType } from './management_service';
|
||||
import { ManagementService } from './management_service';
|
||||
import { roleMappingsManagementApp } from './role_mappings';
|
||||
import { rolesManagementApp } from './roles';
|
||||
import { usersManagementApp } from './users';
|
||||
import type { SecurityLicenseFeatures } from '../../common';
|
||||
import { licenseMock } from '../../common/licensing/index.mock';
|
||||
import type { ConfigType } from '../config';
|
||||
import { securityMock } from '../mocks';
|
||||
|
||||
const mockSection = createManagementSectionMock();
|
||||
|
@ -44,7 +44,7 @@ describe('ManagementService', () => {
|
|||
locator: {} as any,
|
||||
};
|
||||
|
||||
const service = new ManagementService();
|
||||
const service = new ManagementService({} as unknown as ConfigType);
|
||||
service.setup({
|
||||
getStartServices: getStartServices as any,
|
||||
license,
|
||||
|
@ -80,7 +80,7 @@ describe('ManagementService', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('Users, Roles, and Role Mappings are not registered when their UI config settings are set to false', () => {
|
||||
it('Users, Roles, and Role Mappings are not registered when their config settings are set to false', () => {
|
||||
const mockSectionWithConfig = createManagementSectionMock();
|
||||
const { fatalErrors, getStartServices } = coreMock.createSetup();
|
||||
const { authc } = securityMock.createSetup();
|
||||
|
@ -96,20 +96,21 @@ describe('ManagementService', () => {
|
|||
locator: {} as any,
|
||||
};
|
||||
|
||||
const uiConfig: ManagementAppConfigType = {
|
||||
userManagementEnabled: false,
|
||||
const config = {
|
||||
ui: {
|
||||
userManagementEnabled: false,
|
||||
roleMappingManagementEnabled: false,
|
||||
},
|
||||
roleManagementEnabled: false,
|
||||
roleMappingManagementEnabled: false,
|
||||
};
|
||||
} as unknown as ConfigType;
|
||||
|
||||
const service = new ManagementService();
|
||||
const service = new ManagementService(config);
|
||||
service.setup({
|
||||
getStartServices: getStartServices as any,
|
||||
license,
|
||||
fatalErrors,
|
||||
authc,
|
||||
management: managementSetup,
|
||||
uiConfig,
|
||||
});
|
||||
|
||||
// Only API Keys app should be registered
|
||||
|
@ -154,7 +155,15 @@ describe('ManagementService', () => {
|
|||
const license = licenseMock.create();
|
||||
license.features$ = licenseSubject;
|
||||
|
||||
const service = new ManagementService();
|
||||
const config = {
|
||||
ui: {
|
||||
userManagementEnabled: true,
|
||||
roleMappingManagementEnabled: true,
|
||||
},
|
||||
roleManagementEnabled: true,
|
||||
} as unknown as ConfigType;
|
||||
|
||||
const service = new ManagementService(config);
|
||||
|
||||
const managementSetup: ManagementSetup = {
|
||||
sections: {
|
||||
|
@ -166,19 +175,12 @@ describe('ManagementService', () => {
|
|||
locator: {} as any,
|
||||
};
|
||||
|
||||
const uiConfig: ManagementAppConfigType = {
|
||||
userManagementEnabled: true,
|
||||
roleManagementEnabled: true,
|
||||
roleMappingManagementEnabled: true,
|
||||
};
|
||||
|
||||
service.setup({
|
||||
getStartServices: getStartServices as any,
|
||||
license,
|
||||
fatalErrors,
|
||||
authc: securityMock.createSetup().authc,
|
||||
management: managementSetup,
|
||||
uiConfig,
|
||||
});
|
||||
|
||||
const getMockedApp = (id: string) => {
|
||||
|
@ -218,7 +220,6 @@ describe('ManagementService', () => {
|
|||
navLinks: {},
|
||||
catalogue: {},
|
||||
},
|
||||
uiConfig,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -20,12 +20,13 @@ import { roleMappingsManagementApp } from './role_mappings';
|
|||
import { rolesManagementApp } from './roles';
|
||||
import { usersManagementApp } from './users';
|
||||
import type { SecurityLicense } from '../../common';
|
||||
import type { ConfigType } from '../config';
|
||||
import type { PluginStartDependencies } from '../plugin';
|
||||
|
||||
export interface ManagementAppConfigType {
|
||||
userManagementEnabled: boolean;
|
||||
roleManagementEnabled: boolean;
|
||||
roleMappingManagementEnabled: boolean;
|
||||
userManagementEnabled?: boolean;
|
||||
roleManagementEnabled?: boolean;
|
||||
roleMappingManagementEnabled?: boolean;
|
||||
}
|
||||
|
||||
interface SetupParams {
|
||||
|
@ -34,38 +35,48 @@ interface SetupParams {
|
|||
authc: AuthenticationServiceSetup;
|
||||
fatalErrors: FatalErrorsSetup;
|
||||
getStartServices: StartServicesAccessor<PluginStartDependencies>;
|
||||
uiConfig?: ManagementAppConfigType;
|
||||
}
|
||||
|
||||
interface StartParams {
|
||||
capabilities: Capabilities;
|
||||
uiConfig?: ManagementAppConfigType;
|
||||
}
|
||||
|
||||
export class ManagementService {
|
||||
private license!: SecurityLicense;
|
||||
private licenseFeaturesSubscription?: Subscription;
|
||||
private securitySection?: ManagementSection;
|
||||
private readonly userManagementEnabled: boolean;
|
||||
private readonly roleManagementEnabled: boolean;
|
||||
private readonly roleMappingManagementEnabled: boolean;
|
||||
|
||||
setup({ getStartServices, management, authc, license, fatalErrors, uiConfig }: SetupParams) {
|
||||
constructor(config: ConfigType) {
|
||||
this.userManagementEnabled = config.ui?.userManagementEnabled !== false;
|
||||
this.roleManagementEnabled = config.roleManagementEnabled !== false;
|
||||
this.roleMappingManagementEnabled = config.ui?.roleMappingManagementEnabled !== false;
|
||||
}
|
||||
|
||||
setup({ getStartServices, management, authc, license, fatalErrors }: SetupParams) {
|
||||
this.license = license;
|
||||
this.securitySection = management.sections.section.security;
|
||||
|
||||
if (!uiConfig || uiConfig.userManagementEnabled) {
|
||||
if (this.userManagementEnabled) {
|
||||
this.securitySection.registerApp(usersManagementApp.create({ authc, getStartServices }));
|
||||
}
|
||||
if (!uiConfig || uiConfig.roleManagementEnabled) {
|
||||
|
||||
if (this.roleManagementEnabled) {
|
||||
this.securitySection.registerApp(
|
||||
rolesManagementApp.create({ fatalErrors, license, getStartServices })
|
||||
);
|
||||
}
|
||||
|
||||
this.securitySection.registerApp(apiKeysManagementApp.create({ authc, getStartServices }));
|
||||
if (!uiConfig || uiConfig.roleMappingManagementEnabled) {
|
||||
|
||||
if (this.roleMappingManagementEnabled) {
|
||||
this.securitySection.registerApp(roleMappingsManagementApp.create({ getStartServices }));
|
||||
}
|
||||
}
|
||||
|
||||
start({ capabilities, uiConfig }: StartParams) {
|
||||
start({ capabilities }: StartParams) {
|
||||
this.licenseFeaturesSubscription = this.license.features$.subscribe((features) => {
|
||||
const securitySection = this.securitySection!;
|
||||
|
||||
|
@ -73,21 +84,21 @@ export class ManagementService {
|
|||
[securitySection.getApp(apiKeysManagementApp.id)!, features.showLinks],
|
||||
];
|
||||
|
||||
if (!uiConfig || uiConfig.userManagementEnabled) {
|
||||
if (this.userManagementEnabled) {
|
||||
securityManagementAppsStatuses.push([
|
||||
securitySection.getApp(usersManagementApp.id)!,
|
||||
features.showLinks,
|
||||
]);
|
||||
}
|
||||
|
||||
if (!uiConfig || uiConfig.roleManagementEnabled) {
|
||||
if (this.roleManagementEnabled) {
|
||||
securityManagementAppsStatuses.push([
|
||||
securitySection.getApp(rolesManagementApp.id)!,
|
||||
features.showLinks,
|
||||
]);
|
||||
}
|
||||
|
||||
if (!uiConfig || uiConfig.roleMappingManagementEnabled) {
|
||||
if (this.roleMappingManagementEnabled) {
|
||||
securityManagementAppsStatuses.push([
|
||||
securitySection.getApp(roleMappingsManagementApp.id)!,
|
||||
features.showLinks && features.showRoleMappingsManagement,
|
||||
|
|
|
@ -73,19 +73,21 @@ export class SecurityPlugin
|
|||
private readonly authenticationService = new AuthenticationService();
|
||||
private readonly navControlService;
|
||||
private readonly securityLicenseService = new SecurityLicenseService();
|
||||
private readonly managementService = new ManagementService();
|
||||
private readonly managementService: ManagementService;
|
||||
private readonly securityCheckupService: SecurityCheckupService;
|
||||
private readonly anonymousAccessService = new AnonymousAccessService();
|
||||
private readonly analyticsService = new AnalyticsService();
|
||||
private authc!: AuthenticationServiceSetup;
|
||||
private securityApiClients!: SecurityApiClients;
|
||||
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {
|
||||
this.config = this.initializerContext.config.get<ConfigType>();
|
||||
this.securityCheckupService = new SecurityCheckupService(this.config, localStorage);
|
||||
this.navControlService = new SecurityNavControlService(
|
||||
initializerContext.env.packageInfo.buildFlavor
|
||||
);
|
||||
this.managementService = new ManagementService(
|
||||
this.initializerContext.config.get<ConfigType>()
|
||||
);
|
||||
}
|
||||
|
||||
public setup(
|
||||
|
@ -137,7 +139,6 @@ export class SecurityPlugin
|
|||
authc: this.authc,
|
||||
fatalErrors: core.fatalErrors,
|
||||
getStartServices: core.getStartServices,
|
||||
uiConfig: this.config.ui,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -188,7 +189,6 @@ export class SecurityPlugin
|
|||
if (management) {
|
||||
this.managementService.start({
|
||||
capabilities: application.capabilities,
|
||||
uiConfig: this.config.ui,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -226,6 +226,7 @@ describe('config schema', () => {
|
|||
"cookieName": "sid",
|
||||
"loginAssistanceMessage": "",
|
||||
"public": Object {},
|
||||
"roleManagementEnabled": false,
|
||||
"secureCookies": false,
|
||||
"session": Object {
|
||||
"cleanupInterval": "PT1H",
|
||||
|
@ -235,7 +236,6 @@ describe('config schema', () => {
|
|||
"showInsecureClusterWarning": true,
|
||||
"showNavLinks": true,
|
||||
"ui": Object {
|
||||
"roleManagementEnabled": true,
|
||||
"roleMappingManagementEnabled": true,
|
||||
"userManagementEnabled": true,
|
||||
},
|
||||
|
@ -1487,14 +1487,8 @@ describe('config schema', () => {
|
|||
ConfigSchema.validate(
|
||||
{ authc: { http: { jwt: { taggedRoutesOnly: false } } } },
|
||||
{ serverless: true }
|
||||
).ui
|
||||
).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"roleManagementEnabled": true,
|
||||
"roleMappingManagementEnabled": true,
|
||||
"userManagementEnabled": true,
|
||||
}
|
||||
`);
|
||||
).authc.http.jwt.taggedRoutesOnly
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1505,7 +1499,6 @@ describe('config schema', () => {
|
|||
{
|
||||
ui: {
|
||||
userManagementEnabled: false,
|
||||
roleManagementEnabled: false,
|
||||
roleMappingManagementEnabled: false,
|
||||
},
|
||||
},
|
||||
|
@ -1520,7 +1513,6 @@ describe('config schema', () => {
|
|||
{
|
||||
ui: {
|
||||
userManagementEnabled: false,
|
||||
roleManagementEnabled: false,
|
||||
roleMappingManagementEnabled: false,
|
||||
},
|
||||
},
|
||||
|
@ -1528,7 +1520,6 @@ describe('config schema', () => {
|
|||
).ui
|
||||
).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"roleManagementEnabled": false,
|
||||
"roleMappingManagementEnabled": false,
|
||||
"userManagementEnabled": false,
|
||||
}
|
||||
|
@ -1536,6 +1527,32 @@ describe('config schema', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('roleManagementEnabled', () => {
|
||||
it('should not allow xpack.security.roleManagementEnabled to be configured outside of the serverless context', () => {
|
||||
expect(() =>
|
||||
ConfigSchema.validate(
|
||||
{
|
||||
roleManagementEnabled: false,
|
||||
},
|
||||
{ serverless: false }
|
||||
)
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"[roleManagementEnabled]: a value wasn't expected to be present"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should allow xpack.security.roleManagementEnabled to be configured inside of the serverless context', () => {
|
||||
expect(
|
||||
ConfigSchema.validate(
|
||||
{
|
||||
roleManagementEnabled: false,
|
||||
},
|
||||
{ serverless: true }
|
||||
).roleManagementEnabled
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('session', () => {
|
||||
it('should throw error if xpack.security.session.cleanupInterval is less than 10 seconds', () => {
|
||||
expect(() => ConfigSchema.validate({ session: { cleanupInterval: '9s' } })).toThrow(
|
||||
|
|
|
@ -302,11 +302,14 @@ export const ConfigSchema = schema.object({
|
|||
),
|
||||
}),
|
||||
|
||||
roleManagementEnabled: offeringBasedSchema({
|
||||
serverless: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
|
||||
// Setting only allowed in the Serverless offering
|
||||
ui: offeringBasedSchema({
|
||||
serverless: schema.object({
|
||||
userManagementEnabled: schema.boolean({ defaultValue: true }),
|
||||
roleManagementEnabled: schema.boolean({ defaultValue: true }),
|
||||
roleMappingManagementEnabled: schema.boolean({ defaultValue: true }),
|
||||
}),
|
||||
}),
|
||||
|
|
|
@ -88,6 +88,7 @@ export const config: PluginConfigDescriptor<TypeOf<typeof ConfigSchema>> = {
|
|||
sameSiteCookies: true,
|
||||
showNavLinks: true,
|
||||
ui: true,
|
||||
roleManagementEnabled: true,
|
||||
},
|
||||
};
|
||||
export const plugin: PluginInitializer<
|
||||
|
|
|
@ -14,12 +14,17 @@ import type { RouteDefinitionParams } from '..';
|
|||
export function defineAuthorizationRoutes(params: RouteDefinitionParams) {
|
||||
// The reset session endpoint is registered with httpResources and should remain public in serverless
|
||||
resetSessionPageRoutes(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') {
|
||||
// By default, in the serverless environment privileges and permissions are managed internally and only
|
||||
// exposed to users and administrators via control plane UI, however, we will allow these routes to be registered
|
||||
// behind a feature flag for development & design of Custom Roles
|
||||
if (params.buildFlavor !== 'serverless' || params.config.roleManagementEnabled) {
|
||||
definePrivilegesRoutes(params);
|
||||
defineRolesRoutes(params);
|
||||
}
|
||||
|
||||
// Shared object permission routes are not currently available in serverless.
|
||||
if (params.buildFlavor !== 'serverless') {
|
||||
defineShareSavedObjectPermissionRoutes(params);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,86 +14,56 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
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);
|
||||
});
|
||||
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 role', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/api/security/role/superuser')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
it('get built-in elasticsearch privileges', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/internal/security/esPrivileges/builtin')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('get all roles', async () => {
|
||||
const { status } = await supertest
|
||||
.get('/api/security/role')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
});
|
||||
it('create/update role', async () => {
|
||||
const { body, status } = await supertest
|
||||
.put('/api/security/role/test')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertApiNotFound(body, status);
|
||||
});
|
||||
|
||||
it('delete role', async () => {
|
||||
const { status } = await supertest
|
||||
.delete('/api/security/role/superuser')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
expect(status).not.toBe(404);
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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', function () {
|
||||
describe('route access', () => {
|
||||
describe('roles', () => {
|
||||
describe('enabled', () => {
|
||||
it('get role', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/role/superuser')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
|
||||
it('get all roles', async () => {
|
||||
const { body, status } = await supertest
|
||||
.get('/api/security/role')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
svlCommonApi.assertResponseStatusCode(200, status, body);
|
||||
});
|
||||
});
|
||||
|
||||
describe('moved', () => {
|
||||
it('delete role', async () => {
|
||||
const { body, status } = await supertest
|
||||
.delete('/api/security/role/superuser')
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(410, status, body);
|
||||
});
|
||||
|
||||
it('create/update role', async () => {
|
||||
const role = {
|
||||
elasticsearch: {
|
||||
cluster: [],
|
||||
indices: [{ names: ['test'], privileges: ['read'] }],
|
||||
run_as: [],
|
||||
},
|
||||
kibana: [],
|
||||
};
|
||||
|
||||
const { body, status } = await supertest
|
||||
.put('/api/security/role/myRole')
|
||||
.send(role)
|
||||
.set(svlCommonApi.getInternalRequestHeader());
|
||||
|
||||
svlCommonApi.assertResponseStatusCode(410, status, body);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -20,7 +20,7 @@ export default createTestConfig({
|
|||
suiteTags: { exclude: ['skipSvlOblt'] },
|
||||
services,
|
||||
// add feature flags
|
||||
kbnServerArgs: ['--xpack.infra.enabled=true'],
|
||||
kbnServerArgs: ['--xpack.infra.enabled=true', '--xpack.security.roleManagementEnabled=true'],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./index.feature_flags.ts')],
|
||||
|
||||
|
|
|
@ -11,5 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('Serverless observability API - feature flags', function () {
|
||||
loadTestFile(require.resolve('./custom_threshold_rule'));
|
||||
loadTestFile(require.resolve('./infra'));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles_routes_feature_flag.ts'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ export default createTestConfig({
|
|||
},
|
||||
suiteTags: { exclude: ['skipSvlSearch'] },
|
||||
// add feature flags
|
||||
kbnServerArgs: [],
|
||||
kbnServerArgs: ['--xpack.security.roleManagementEnabled=true'],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./index.feature_flags.ts')],
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Serverless search API - feature flags', function () {
|
||||
// add tests that require feature flags, defined in config.feature_flags.ts
|
||||
// loadTestFile(require.resolve(<path_to_test_file>));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles_routes_feature_flag.ts'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ export default createTestConfig({
|
|||
},
|
||||
suiteTags: { exclude: ['skipSvlSec'] },
|
||||
// add feature flags
|
||||
kbnServerArgs: [],
|
||||
kbnServerArgs: ['--xpack.security.roleManagementEnabled=true'],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./index.feature_flags.ts')],
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('Serverless security API - feature flags', function () {
|
||||
// add tests that require feature flags, defined in config.feature_flags.ts
|
||||
// loadTestFile(require.resolve(<path_to_test_file>));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles_routes_feature_flag.ts'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { SvlSecLandingPageProvider } from './svl_sec_landing_page';
|
|||
import { SvlTriggersActionsPageProvider } from './svl_triggers_actions_ui_page';
|
||||
import { SvlRuleDetailsPageProvider } from './svl_rule_details_ui_page';
|
||||
import { SvlSearchConnectorsPageProvider } from './svl_search_connectors_page';
|
||||
import { SvlManagementPageProvider } from './svl_management_page';
|
||||
|
||||
export const pageObjects = {
|
||||
...xpackFunctionalPageObjects,
|
||||
|
@ -34,4 +35,5 @@ export const pageObjects = {
|
|||
svlSecLandingPage: SvlSecLandingPageProvider,
|
||||
svlTriggersActionsUI: SvlTriggersActionsPageProvider,
|
||||
svlRuleDetailsUI: SvlRuleDetailsPageProvider,
|
||||
svlManagementPage: SvlManagementPageProvider,
|
||||
};
|
||||
|
|
|
@ -229,7 +229,7 @@ export function SvlCommonPageProvider({ getService, getPageObjects }: FtrProvide
|
|||
},
|
||||
|
||||
async clickUserAvatar() {
|
||||
testSubjects.click('userMenuAvatar');
|
||||
await testSubjects.click('userMenuAvatar');
|
||||
},
|
||||
|
||||
async assertUserAvatarExists() {
|
||||
|
|
|
@ -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 function SvlManagementPageProvider({ getService }: FtrProviderContext) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
return {
|
||||
async assertRoleManagementCardExists() {
|
||||
await testSubjects.existOrFail('app-card-roles');
|
||||
},
|
||||
|
||||
async assertRoleManagementCardDoesNotExist() {
|
||||
await testSubjects.missingOrFail('app-card-roles');
|
||||
},
|
||||
|
||||
async clickRoleManagementCard() {
|
||||
await testSubjects.click('app-card-roles');
|
||||
},
|
||||
};
|
||||
}
|
|
@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
|
|||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'common']);
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'common', 'svlManagementPage']);
|
||||
const browser = getService('browser');
|
||||
const retry = getService('retry');
|
||||
|
||||
|
@ -36,5 +36,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
return await testSubjects.exists('indexManagementHeaderContent');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Roles management card', () => {
|
||||
it('should not be displayed by default', async () => {
|
||||
await pageObjects.common.navigateToApp('management');
|
||||
|
||||
await pageObjects.svlManagementPage.assertRoleManagementCardDoesNotExist();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const pageObjects = getPageObjects(['svlCommonPage', 'common', 'svlManagementPage']);
|
||||
const browser = getService('browser');
|
||||
const retry = getService('retry');
|
||||
|
||||
describe('Roles management card', function () {
|
||||
this.tags('smoke');
|
||||
before(async () => {
|
||||
// Navigate to the index management page
|
||||
await pageObjects.svlCommonPage.loginAsAdmin();
|
||||
await pageObjects.common.navigateToApp('management');
|
||||
});
|
||||
|
||||
it('renders the page, displays the Roles card, and will navigate to the Roles UI', async () => {
|
||||
await retry.waitFor('page to be visible', async () => {
|
||||
return await testSubjects.exists('cards-navigation-page');
|
||||
});
|
||||
|
||||
let url = await browser.getCurrentUrl();
|
||||
expect(url).to.contain(`/management`);
|
||||
|
||||
await pageObjects.svlManagementPage.assertRoleManagementCardExists();
|
||||
|
||||
await pageObjects.svlManagementPage.clickRoleManagementCard();
|
||||
|
||||
url = await browser.getCurrentUrl();
|
||||
expect(url).to.contain('/management/security/roles');
|
||||
});
|
||||
});
|
||||
};
|
|
@ -21,6 +21,7 @@ export default createTestConfig({
|
|||
kbnServerArgs: [
|
||||
'--xpack.infra.enabled=true',
|
||||
'--xpack.infra.featureFlags.customThresholdAlertsEnabled=true',
|
||||
'--xpack.security.roleManagementEnabled=true',
|
||||
],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./index.feature_flags.ts')],
|
||||
|
|
|
@ -11,5 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('serverless observability UI - feature flags', function () {
|
||||
// add tests that require feature flags, defined in config.feature_flags.ts
|
||||
loadTestFile(require.resolve('./infra'));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles_management_card.ts'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ export default createTestConfig({
|
|||
},
|
||||
suiteTags: { exclude: ['skipSvlSearch'] },
|
||||
// add feature flags
|
||||
kbnServerArgs: [],
|
||||
kbnServerArgs: ['--xpack.security.roleManagementEnabled=true'],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./index.feature_flags.ts')],
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('serverless search UI - feature flags', function () {
|
||||
// add tests that require feature flags, defined in config.feature_flags.ts
|
||||
// loadTestFile(require.resolve(<path_to_test_file>));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles_management_card.ts'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ export default createTestConfig({
|
|||
},
|
||||
suiteTags: { exclude: ['skipSvlSec'] },
|
||||
// add feature flags
|
||||
kbnServerArgs: [],
|
||||
kbnServerArgs: ['--xpack.security.roleManagementEnabled=true'],
|
||||
// load tests in the index file
|
||||
testFiles: [require.resolve('./index.feature_flags.ts')],
|
||||
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export default function () {
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('serverless security UI - feature flags', function () {
|
||||
// add tests that require feature flags, defined in config.feature_flags.ts
|
||||
// loadTestFile(require.resolve(<path_to_test_file>));
|
||||
loadTestFile(require.resolve('../common/platform_security/roles_management_card.ts'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue