Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Access Agreement UI. #63563

Merged
merged 22 commits into from
Apr 28, 2020

Conversation

azasypkin
Copy link
Member

@azasypkin azasypkin commented Apr 15, 2020

This PR introduces the Access Agreement UI that works like that:

  • Every provider has an additional accessAgreement configuration option now. Option accepts markdown string as a value.
  • Every authenticated request will be redirected to /security/access_agreement only if:
    • Request is authenticated by the provider that has accessAgreement.message configured
    • User hasn't yet acknowledged access notice in the current session (based on the flag we store in the session)
    • Request can be redirected (not API call)
    • And it's not a request to the Access Agreement UI itself
  • When request is redirected to the Access Agreement UI original URL is captured and user will be redirected to it once access agreement is acknowledged

UI/UX:

Peek 2020-04-24 10-43

Config:

xpack.security.authc.providers:
  basic.basic1:
    order: 0
    accessAgreement.message: "#### You are accessing a system with U.S. government information \n\n
                   By logging in, you acknowledge that information system usage may be monitored, recorded, and subject to audit. Unauthorized use of the information system is prohibited and subject to criminal and civil penalties. Use of the information system indicates consent to monitoring and recording."

Blocked by: Design Team and stakeholders feedback is pending
Fixes: #18176


"Release Note: every provider can now be configured with the access agreement message (markdown syntax) that will be presented to the users after login. Users won't be able to use Kibana until they acknowledge this agreement."

@azasypkin azasypkin added blocked release_note:roadmap Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! v7.8.0 labels Apr 15, 2020
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-security (Team:Security)

@azasypkin azasypkin force-pushed the issue-18176-pre-access-message branch 2 times, most recently from 9320a37 to 5f43585 Compare April 20, 2020 07:17
@azasypkin azasypkin removed the blocked label Apr 20, 2020
@@ -8,11 +8,12 @@ import { EuiIcon, EuiSpacer, EuiTitle } from '@elastic/eui';
import React from 'react';

interface Props {
className?: string;
Copy link
Member Author

@azasypkin azasypkin Apr 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: motivation behind this change is that I wanted to make body of this component wider for access notice as we have much more text there (600px instead of 480px)

@@ -15,7 +15,6 @@ import {
} from './plugin';

export { SecurityPluginSetup, SecurityPluginStart };
export { SessionInfo } from './types';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: it doesn't seem like we want it to be a part of our public API, feels like our own internal thing.

/**
* The set of flags used to describe various aspects of the user session.
*/
flags?: {
Copy link
Member Author

@azasypkin azasypkin Apr 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: if we wouldn't have plans to switch to server side sessions, I'd use a bitmask field here instead to save space 🙂 For now will just make it optional.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question I'm not opposed to this structure, but do you have other flags in mind that we'll add in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually a good point, I had something else in mind at the beginning, but now I can't think of anything. Maybe flags is too premature indeed, I'll switch to the top level property for now, thanks!

(this.options.config.authc.providers as Record<string, any>)[session.provider.type]?.[
session.provider.name
]?.accessNotice &&
request.url.pathname !== ACCESS_NOTICE_ROUTE
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: technically pathname can have a trailing slash, but I'm ignoring this here to simplify the check because of the following reasons:

  • When we redirect to this page we don't add trailing slash
  • If user goes directly to this page and adds a trailing slash, they will end up at /security/access_notice?next=%2Fsecurity%2Faccess_notice%2F assuming they haven't acknowledged access notice yet, that means after acknowledgement they will be redirect to /security/access_notice (since Kibana server will eventually issue a redirected to the same URL but without trailing slash), after second acknowledgement or just by navigating to another page everything will go back to normal.

I'm not worried about the second point since it's a user mistake after all and it doesn't have that bad consequences.

@@ -225,25 +234,19 @@ export function createConfig(
const sortedProviders: Array<{
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: since we're adding more common config properties the amount of info we're duplicating in sortedProviders is increasing, so I decided to take a step back and keep authc.providers as the only source of truth.

@@ -71,14 +71,10 @@ describe('Security Plugin', () => {
"authc": Object {
"createAPIKey": [Function],
"getCurrentUser": [Function],
"getSessionInfo": [Function],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: technically it's a breaking change (if we already started to treat changed plugin contracts as such), but nobody in Kibana was using it and I'm not sure we want to expose this to 3rd party plugins yet. Starting from 8.0 I'd not do that 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, now is a great time to clean this up!

const session = await authc.getSessionInfo(request);
const accessNotice =
(session &&
(config.authc.providers as Record<string, any>)[session.provider.type]?.[
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: extracting config from config.authc.providers is a bit ugly since we don't know what providers are configured and there is no "type link" between session provider field and this config (and may not be since session can rely on old config). If we see this pattern more and more, we'll create a helper method instead.

@azasypkin azasypkin force-pushed the issue-18176-pre-access-message branch from 5f43585 to 5c395ef Compare April 20, 2020 09:23
@azasypkin azasypkin marked this pull request as ready for review April 20, 2020 11:29
@azasypkin azasypkin requested review from a team as code owners April 20, 2020 11:29
@azasypkin azasypkin requested a review from legrego April 20, 2020 11:30
@ryankeairns
Copy link
Contributor

@azasypkin A few design tweaks coming... I'll have a design PR for you to review.

Copy link
Contributor

@ryankeairns ryankeairns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azasypkin here is a design PR with a few changes that more closely aligns to the modal content styling we are after. If it looks good, simply merge: azasypkin#1

@azasypkin
Copy link
Member Author

@azasypkin here is a design PR with a few changes that more closely aligns to the modal content styling we are after. If it looks good, simply merge: azasypkin#1

Looks great, thanks a lot! Merged and updated screenshot in description.

@ryankeairns
Copy link
Contributor

@azasypkin I'll need to make some changes based upon some feedback I received from @cchaos . I'll get that going straight away.

@ryankeairns
Copy link
Contributor

2nd design PR 👉 azasypkin#2

… generalize flags just yet, use object format for `accessAgreement` config.
… generalize flags just yet, use object format for `accessAgreement` config, use loading screen.
@azasypkin azasypkin changed the title Introduce Access Notice UI. Introduce Access Agreement UI. Apr 23, 2020
@cchaos
Copy link
Contributor

cchaos commented Apr 23, 2020

should this screen be using the Elastic cluster logo instead of the Kibana logo? I know we recently made this change to the login and space selector screens

Yes indeed, the Kibana logo should now be the cluster logo.

@azasypkin
Copy link
Member Author

should this screen be using the Elastic cluster logo instead of the Kibana logo? I know we recently made this change to the login and space selector screens
Yes indeed, the Kibana logo should now be the cluster logo.

Roger that, changed to logoElastic.

@azasypkin azasypkin requested a review from legrego April 24, 2020 11:55
@azasypkin
Copy link
Member Author

@legrego PR should be ready for another pass! But since we're still waiting for some details on this feature that will require a couple of changes here and there (in a separate commit though), feel free to postpone review.

}

if (!this.options.license.getFeatures().allowAccessAgreement) {
throw new Error('Current license does not allow access agreement acknowledgement.');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: this doesn't sound like an audit event, but rather as a developer mistake, hence just throwing error here.

// 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 allow it.`);
return response.notFound();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I was considering two options here, either to return 403 or 404. I picked 404 and the rational is that license doesn't provide this feature and that basically means that feature is missing (not found).. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a 403 would make more sense here (with reason in the response body), so that we're consistent with the base licensed route handler functionality:

return responseToolkit.forbidden({ body: { message: licenseCheck.message! } });

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, makes sense too, let's go with 403. By reason you mean our own custom message, right? Currently SecurityLicense doesn't keep the optional message that Licensing plugin would return in case of failed check...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed over Slack, we'll go with custom Current license doesn't support access agreement. message.

Copy link
Member

@legrego legrego left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks @azasypkin! Just a couple nits/comments, then I think we're good to go!

// 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 allow it.`);
return response.notFound();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a 403 would make more sense here (with reason in the response body), so that we're consistent with the base licensed route handler functionality:

return responseToolkit.forbidden({ body: { message: licenseCheck.message! } });

>(
handler: RequestHandler<P, Q, B, M, R>
) => {
const licensedRouteHandler: RequestHandler<P, Q, B, M, R> = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for future us: we should consider updating this to accept a minimum license level once we have more than one route that requires an elevated license

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest I have mixed feelings about this since feature that depends on license level can rely on multiple endpoints and keeping all of them in sync may be tedious. Unless we refactor SecurityLicense to include something similar to feature <-> min license level mapping that will be the only source of truth.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s a great point, a single source of truth is much more beneficial than the potential duplication of license checks

@@ -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 role mappings, access agreement, DLS, and sub-feature privileges if license is basic.', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is getting a bit out of hand 😅What do you think about something like this?

Suggested change
it('should show login page and other security elements, allow RBAC but forbid role mappings, access agreement, 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.', () => {

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙂 yeah

expect(routeConfig.validate).toBe(false);
});

it('returns 404 if current license doesnt allow access agreement acknowledgement.', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('returns 404 if current license doesnt allow access agreement acknowledgement.', async () => {
it(`returns 404 if current license doesn't allow access agreement acknowledgement.`, async () => {

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 allow it.`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
logger.warn(`Attempted to acknowledge access agreement when license doesn allow it.`);
logger.warn(`Attempted to acknowledge access agreement when license doesn't allow it.`);

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

@azasypkin azasypkin merged commit 36b4864 into elastic:master Apr 28, 2020
@azasypkin azasypkin deleted the issue-18176-pre-access-message branch April 28, 2020 16:00
azasypkin added a commit to azasypkin/kibana that referenced this pull request Apr 28, 2020
Co-authored-by: Ryan Keairns <contactryank@gmail.com>
@azasypkin
Copy link
Member Author

7.x/7.8.0: a3790c8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backported release_note:enhancement Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! v7.8.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants