Skip to content

Commit

Permalink
Introduce Access Agreement UI. (elastic#63563)
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Keairns <contactryank@gmail.com>
  • Loading branch information
azasypkin and ryankeairns authored Apr 28, 2020
1 parent 4a04adf commit 36b4864
Show file tree
Hide file tree
Showing 43 changed files with 1,297 additions and 100 deletions.
5 changes: 5 additions & 0 deletions x-pack/plugins/security/common/licensing/license_features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export interface SecurityLicenseFeatures {
*/
readonly showRoleMappingsManagement: boolean;

/**
* Indicates whether we allow users to access agreement UI and acknowledge it.
*/
readonly allowAccessAgreement: boolean;

/**
* Indicates whether we allow users to define document level security in roles.
*/
Expand Down
14 changes: 11 additions & 3 deletions x-pack/plugins/security/common/licensing/license_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('license features', function() {
allowLogin: false,
showLinks: false,
showRoleMappingsManagement: false,
allowAccessAgreement: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
layout: 'error-es-unavailable',
Expand All @@ -37,6 +38,7 @@ describe('license features', function() {
allowLogin: false,
showLinks: false,
showRoleMappingsManagement: false,
allowAccessAgreement: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
layout: 'error-xpack-unavailable',
Expand All @@ -60,6 +62,7 @@ describe('license features', function() {
expect(subscriptionHandler.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"allowAccessAgreement": false,
"allowLogin": false,
"allowRbac": false,
"allowRoleDocumentLevelSecurity": false,
Expand All @@ -78,6 +81,7 @@ describe('license features', function() {
expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(`
Array [
Object {
"allowAccessAgreement": true,
"allowLogin": true,
"allowRbac": true,
"allowRoleDocumentLevelSecurity": true,
Expand All @@ -94,7 +98,7 @@ describe('license features', function() {
}
});

it('should show login page and other security elements, allow RBAC but forbid role mappings, DLS, and sub-feature privileges if license is basic.', () => {
it('should show login page and other security elements, allow RBAC but forbid paid features if license is basic.', () => {
const mockRawLicense = licensingMock.createLicense({
features: { security: { isEnabled: true, isAvailable: true } },
});
Expand All @@ -109,6 +113,7 @@ describe('license features', function() {
allowLogin: true,
showLinks: true,
showRoleMappingsManagement: false,
allowAccessAgreement: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: true,
Expand All @@ -131,14 +136,15 @@ describe('license features', function() {
allowLogin: false,
showLinks: false,
showRoleMappingsManagement: false,
allowAccessAgreement: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
allowSubFeaturePrivileges: false,
});
});

it('should allow role mappings and sub-feature privileges, but not DLS/FLS if license = gold', () => {
it('should allow role mappings, access agreement and sub-feature privileges, but not DLS/FLS if license = gold', () => {
const mockRawLicense = licensingMock.createLicense({
license: { mode: 'gold', type: 'gold' },
features: { security: { isEnabled: true, isAvailable: true } },
Expand All @@ -152,14 +158,15 @@ describe('license features', function() {
allowLogin: true,
showLinks: true,
showRoleMappingsManagement: true,
allowAccessAgreement: true,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: true,
allowSubFeaturePrivileges: true,
});
});

it('should allow to login, allow RBAC, role mappings, sub-feature privileges, and DLS if license >= platinum', () => {
it('should allow to login, allow RBAC, role mappings, access agreement, sub-feature privileges, and DLS if license >= platinum', () => {
const mockRawLicense = licensingMock.createLicense({
license: { mode: 'platinum', type: 'platinum' },
features: { security: { isEnabled: true, isAvailable: true } },
Expand All @@ -173,6 +180,7 @@ describe('license features', function() {
allowLogin: true,
showLinks: true,
showRoleMappingsManagement: true,
allowAccessAgreement: true,
allowRoleDocumentLevelSecurity: true,
allowRoleFieldLevelSecurity: true,
allowRbac: true,
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/security/common/licensing/license_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class SecurityLicenseService {
allowLogin: false,
showLinks: false,
showRoleMappingsManagement: false,
allowAccessAgreement: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
Expand All @@ -88,6 +89,7 @@ export class SecurityLicenseService {
allowLogin: false,
showLinks: false,
showRoleMappingsManagement: false,
allowAccessAgreement: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
Expand All @@ -102,6 +104,7 @@ export class SecurityLicenseService {
allowLogin: true,
showLinks: true,
showRoleMappingsManagement: isLicenseGoldOrBetter,
allowAccessAgreement: isLicenseGoldOrBetter,
allowSubFeaturePrivileges: isLicenseGoldOrBetter,
// Only platinum and trial licenses are compliant with field- and document-level security.
allowRoleDocumentLevelSecurity: isLicensePlatinumOrBetter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@
* you may not use this file except in compliance with the Elastic License.
*/

/**
* Type and name tuple to identify provider used to authenticate user.
*/
export interface AuthenticationProvider {
type: string;
name: string;
}

export interface SessionInfo {
now: number;
idleTimeoutExpiration: number | null;
lifespanExpiration: number | null;
provider: string;
provider: AuthenticationProvider;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.secAccessAgreementPage .secAuthenticationStatePage__content {
max-width: 600px;
}

.secAccessAgreementPage__textWrapper {
overflow-y: hidden;
}

.secAccessAgreementPage__text {
@include euiYScrollWithShadows;
max-height: 400px;
padding: $euiSize $euiSizeL 0;
}

.secAccessAgreementPage__footer {
padding: $euiSize $euiSizeL $euiSizeL;
}

.secAccessAgreementPage__footerInner {
text-align: left;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import './access_agreement_page';
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

jest.mock('./access_agreement_page');

import { AppMount, ScopedHistory } from 'src/core/public';
import { accessAgreementApp } from './access_agreement_app';

import { coreMock, scopedHistoryMock } from '../../../../../../src/core/public/mocks';

describe('accessAgreementApp', () => {
it('properly registers application', () => {
const coreSetupMock = coreMock.createSetup();

accessAgreementApp.create({
application: coreSetupMock.application,
getStartServices: coreSetupMock.getStartServices,
});

expect(coreSetupMock.application.register).toHaveBeenCalledTimes(1);

const [[appRegistration]] = coreSetupMock.application.register.mock.calls;
expect(appRegistration).toEqual({
id: 'security_access_agreement',
chromeless: true,
appRoute: '/security/access_agreement',
title: 'Access Agreement',
mount: expect.any(Function),
});
});

it('properly renders application', async () => {
const coreSetupMock = coreMock.createSetup();
const coreStartMock = coreMock.createStart();
coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]);
const containerMock = document.createElement('div');

accessAgreementApp.create({
application: coreSetupMock.application,
getStartServices: coreSetupMock.getStartServices,
});

const [[{ mount }]] = coreSetupMock.application.register.mock.calls;
await (mount as AppMount)({
element: containerMock,
appBasePath: '',
onAppLeave: jest.fn(),
history: (scopedHistoryMock.create() as unknown) as ScopedHistory,
});

const mockRenderApp = jest.requireMock('./access_agreement_page').renderAccessAgreementPage;
expect(mockRenderApp).toHaveBeenCalledTimes(1);
expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, {
http: coreStartMock.http,
notifications: coreStartMock.notifications,
fatalErrors: coreStartMock.fatalErrors,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { i18n } from '@kbn/i18n';
import { StartServicesAccessor, ApplicationSetup, AppMountParameters } from 'src/core/public';

interface CreateDeps {
application: ApplicationSetup;
getStartServices: StartServicesAccessor;
}

export const accessAgreementApp = Object.freeze({
id: 'security_access_agreement',
create({ application, getStartServices }: CreateDeps) {
application.register({
id: this.id,
title: i18n.translate('xpack.security.accessAgreementAppTitle', {
defaultMessage: 'Access Agreement',
}),
chromeless: true,
appRoute: '/security/access_agreement',
async mount({ element }: AppMountParameters) {
const [[coreStart], { renderAccessAgreementPage }] = await Promise.all([
getStartServices(),
import('./access_agreement_page'),
]);
return renderAccessAgreementPage(coreStart.i18n, element, {
http: coreStart.http,
notifications: coreStart.notifications,
fatalErrors: coreStart.fatalErrors,
});
},
});
},
});
Loading

0 comments on commit 36b4864

Please sign in to comment.