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

Added ability to create API keys #92610

Merged
merged 34 commits into from
Apr 13, 2021

Conversation

thomheymann
Copy link
Contributor

@thomheymann thomheymann commented Feb 24, 2021

Resolves #78222

Summary

Added the ability to create API keys and made UI improvements to the API keys page:

  • Added new “Create API key” flyout.
  • Changed default sort order to “Created” time instead of “Name”.
  • Added API key column so that users can search by key (names aren't unique)
  • Combined “Expiration date” and “Status” columns into one column to reduce visual clutter.
  • Added status indication icons for clearer differentiation.
  • Show relative dates / times (absolute on mouse hover) for better readability.
  • Show user icon to clarify column content.
  • Show “Invalidate” button label, if space allows, since icon itself isn't self explanatory.
  • Removed unnecessary callout when viewing the page as an administrator since that should be the most common use case.

Screenshots

API keys page

Showing 8 first digits of API key alongside metadata.

Screenshot 2021-03-07 at 23 57 31

Create API key flyout

Screenshot 2021-03-07 at 23 58 44

Success state

Showing generated API key ready to be used to authenticate with Elasticsearch. (base64 encoded)

Screenshot 2021-03-08 at 00 01 18

Format selection

We have no unified format for API Keys across the stack with different apps and language clients requiring different formats so added the ability to select the correct one per use case.

Screenshot 2021-03-07 at 23 56 07

Code editor

Updated code editor component to look like an actual input element and improved lazy loading states.

Untitled

Checklist

Delete any items that are not applicable to this PR.

@thomheymann thomheymann added v8.0.0 v7.13.0 release_note:feature Makes this part of the condensed release notes labels Feb 24, 2021
@bytebilly
Copy link
Contributor

@thomheymann when an API key is created, we have both the id and the api_key fields. From your proposal I guess we are returning just the api_key, or maybe the encoded concatenation of the two.

I think that we need to clarify that output, and possibly provide some link to explain how to use it.

What do you think?

@thomheymann
Copy link
Contributor Author

thomheymann commented Mar 3, 2021

@thomheymann when an API key is created, we have both the id and the api_key fields. From your proposal I guess we are returning just the api_key, or maybe the encoded concatenation of the two.

I think that we need to clarify that output, and possibly provide some link to explain how to use it.

What do you think?

@bytebilly What do you have in mind in terms of explanation? There's a few different use cases for API keys so I'm not sure what the best next steps would be for the user or what extra guidance we should offer.

In terms of the output, this is printing the api_key which, I think, is what users would expect to receive back after creation. It's not the ID field since it has no immediate use and we're not showing IDs for any other resource in Kibana so I wouldn't surface that in the UI.

@thomheymann thomheymann marked this pull request as ready for review March 3, 2021 10:37
@thomheymann thomheymann requested review from a team as code owners March 3, 2021 10:37
@legrego
Copy link
Member

legrego commented Mar 3, 2021

What do you have in mind in terms of explanation? There's a few different use cases for API keys so I'm not sure what the best next steps would be for the user or what extra guidance we should offer.

In terms of the output, this is printing the api_key which, I think, is what users would expect to receive back after creation. It's not the ID field since it has no immediate use and we're not showing IDs for any other resource in Kibana so I wouldn't surface that in the UI.

In the traditional basic auth scheme, we send the base64 encoding of username:password. When authenticating via API Keys, the id is akin to the username, and the api_key is akin to the password. So, having the api_key without its corresponding id isn't terribly useful to end users.

These docs explain it a bit better:
https://www.elastic.co/guide/en/kibana/master/api-keys.html#create-api-key


What do you think about exposing both the id and api_key to end users upon creation, and also optionally display the base64-encoded concatenation of the two?

I think we should display the ID in the table too -- it won't be pretty to look at, but it's the only truly unique identifier that we have for each key (names are not unique)

@thomheymann
Copy link
Contributor Author

In the traditional basic auth scheme, we send the base64 encoding of username:password. When authenticating via API Keys, the id is akin to the username, and the api_key is akin to the password. So, having the api_key without its corresponding id isn't terribly useful to end users.

Ah, thanks for clarifying, I thought the API key was enough to authenticate.

Good idea, I'll add that in.

@bytebilly
Copy link
Contributor

In the traditional basic auth scheme, we send the base64 encoding of username:password. When authenticating via API Keys, the id is akin to the username, and the api_key is akin to the password. So, having the api_key without its corresponding id isn't terribly useful to end users.

Ah, thanks for clarifying, I thought the API key was enough to authenticate.

Good idea, I'll add that in.

Thanks, this is exactly what I was talking about. There were multiple discussions on providing the two terms as separate values or as the base64 final string, but I don't think we have a good answer so far. Looping @philkra and @Mpdreamz to get more feedback from the Clients team. Having both may make sense if it doesn't make the UX confusing.

I think we should display the ID in the table too -- it won't be pretty to look at, but it's the only truly unique identifier that we have for each key (names are not unique)

I'm not against that, but I see it could be a separate iteration if we don't have bandwidth to find a good way to present the id in the table.

@thomheymann
Copy link
Contributor Author

@bytebilly I have updated the success state to show the full and correctly formatted API key.

The situation across the stack is a bit of a mess right now and it would be great if we could get Beats and Logstash updated to accept base64 like Elasticsearch and the language clients do (at least the ones I checked). The current situation creates unnecessary confusion and requires relearning for each stack component.

I have added a dropdown so that users can select the app they want to use the API key for but it would be even better to be able to remove this once all our apps and clients accept the same format.

If there's an app I missed that should be listed in the dropdown let me know.

Screenshot 2021-03-07 at 23 56 07

@bytebilly
Copy link
Contributor

Thanks @thomheymann, I like the idea of having multiple output even if making it app specific was not in my initial thoughts. Interesting idea we can consider.

What if the list is:

  • base64
  • id and key (replacing JSON raw response that may be not fully clear)
  • Beats/Logstash (single entry, the config is the same IIRC)

I'm curious what @cristina-eleonora thinks about that as she was looking into it too

@cristina-eleonora
Copy link

cristina-eleonora commented Mar 8, 2021

Thank you for including me in this conversation, @bytebilly.

Since I'm not too versed in the world of Elasticsearch API keys, I have a couple of questions:

  • Why are we showing the generated key in the table? I thought the objective was to only show it once, have users copy it and then hide it
  • When generating an API key, why do we show the username in the flyout?
  • How would this table look when there are cloud users generating the API keys?
  • Is the Beats format identical to the Logstash format?
  • @bytebilly - what will happen to the cloud API keys - will they be generated from here as well?
  • When a key is invalidated, can the operation be reversed? (make it valid again?) If not, I suggest we call the action Remove

Regarding user experience:

  • The name of the API keys would have to come first, as this will more meaningful to the user than a generated string
  • The format of the API key should be set/selected before generating the API key.
  • The format/type of API keys should also be displayed in the table.
  • I wonder if it would make sense to transform the JSON from Restrict access and permissions into input/select fields to make it less intimidating for those users who might not know exactly how to edit that JSON.
  • With RBAC coming our way, it would be good to display the Realm/authentication method of the users who generated the API keys

@bytebilly
Copy link
Contributor

Thank you for the great feedback Cristina! Here are some clarifications.

  • Why are we showing the generated key in the table? I thought the objective was to only show it once, have users copy it and then hide it

In the table we just show the id field, that is the "known" part of the API key and not the secret. It is used to uniquely identify the key and allow management (e.g. revoking it)

  • When generating an API key, why do we show the username in the flyout?

I think the main reason is to make clear who is the user creating the API key, since their permissions affect the final result.

  • How would this table look when there are cloud users generating the API keys?

Currently the Cloud ID (numeric) is shown in the table as the creator, since it is the username property.

  • Is the Beats format identical to the Logstash format?

I'm pretty sure they are.

  • @bytebilly - what will happen to the cloud API keys - will they be generated from here as well?

No, they are not local to the deployment but rather created on the Security Cluster. Users will continue to manage them from the Cloud Console.

  • When a key is invalidated, can the operation be reversed? (make it valid again?) If not, I suggest we call the action Remove

No it cannot be reversed. I personally think that the term "remove" may be a bit weak, since it will not just "remove" the API key (from the list?), but rather make it unusable. I suggested "revoke" initially, but "invalidate" has consistency with our current documentation terminology, but it's interesting to discuss more.

Regarding user experience:

I think there may have been a misunderstanding about the different "formats". The generation process is the same for all of them, it's just some post-create elaboration that changes, in order to provide an input that is more suitable for one use case or another. So the "type" is just a visualization thing, not a structural one.

@thomheymann
Copy link
Contributor Author

  • Why are we showing the generated key in the table? I thought the objective was to only show it once, have users copy it and then hide it

We are showing only the first 8 characters of the API key. We need to show this so that users can search for and invalidate a an API key. API Keys are different to users or roles in that the name isn't unique.

The primary use cases for this page will be creating new API keys or finding an existing one and invalidating it.

When finding an existing API key users will start with the API key, most likely copied from a specific application that they want to decommission, so it needs to be easy for them to find that key based on the actual API key and invalidate it. They wouldn't start with the name since that isn't stored anywhere when connecting an application with Elasticsearch and you could have multiple API Keys with the same name.

  • When generating an API key, why do we show the username in the flyout?

Unlike other resources (e.g. roles or alerts) in stack management, API keys are scoped and can only be created for the currently logged in user. We wouldn't have to show the username here if the form would be displayed in a personal space like account management but since it sits in stack management the user expectation is that they are managing the stack and not their user account. This could pose a security risk if they don't realise that any API key created by them will have the same permissions as their user.

  • How would this table look when there are cloud users generating the API keys?

It would show the user ID (e.g. "3890361203") instead of a username.

  • Is the Beats format identical to the Logstash format?

Yes, they're both identical.

  • When a key is invalidated, can the operation be reversed? (make it valid again?) If not, I suggest we call the action Remove

I don't think it can be reversed. @bytebilly do you know if there's a way to reissue a previously invalidated API key or is that permanent?

Regarding user experience:

  • The name of the API keys would have to come first, as this will more meaningful to the user than a generated string

The name is just descriptive and will be auto-generated in most cases as well (e.g. alerts). I would actually prefer to change this column to "Description".

  • The format of the API key should be set/selected before generating the API key.
  • The format/type of API keys should also be displayed in the table.

An API key doesn't have a format/type. Every API key is the same. The issue is that different Elastic apps currently ask users to format or encode the API key differently in order to configure them. The way we should solve this issue is to agree on a single format and update our existing apps to support that so that we don't even have to show the dropdown menu at all.

  • I wonder if it would make sense to transform the JSON from Restrict access and permissions into input/select fields to make it less intimidating for those users who might not know exactly how to edit that JSON.

Yeh, that would be much nice but unfortunately isn't feasible for this phase. Try and create a new role and have a look at all the options available there. We would have to find a way to cram all that into this form. :(

  • With RBAC coming our way, it would be good to display the Realm/authentication method of the users who generated the

I had to remove that column due to limited space and felt that it was less important than the other columns. Happy to reconsider if we find a way to display this across all the different breakpoints.

@cristina-eleonora
Copy link

Thank you both for your responses 🙇‍♀️

We are showing only the first 8 characters of the API key. We need to show this so that users can search for and invalidate a an API key. API Keys are different to users or roles in that the name isn't unique.

Are the first 8 characters always unique? Just want to make sure that a search will always match one item and not several.

  • When generating an API key, why do we show the username in the flyout?

Unlike other resources (e.g. roles or alerts) in stack management, API keys are scoped and can only be created for the currently logged in user. We wouldn't have to show the username here if the form would be displayed in a personal space like account management but since it sits in stack management the user expectation is that they are managing the stack and not their user account. This could pose a security risk if they don't realise that any API key created by them will have the same permissions as their user.

The general expectation is that the user who is logged in generates the API key. All possible doubts will be removed when the API key user is displayed in the table. Showing the user in the flyout implies that the user could be changed.

  • How would this table look when there are cloud users generating the API keys?

It would show the user ID (e.g. "3890361203") instead of a username.

This is something that we definitely need to fix as:

  • nobody will know who that user is
  • it almost looks like a bug
  • some might think it's an infiltrated user

Right now, when a Cloud user authenticates into Kibana, we are able to match the user and the ID.
We should use the email address.

Screenshot 2021-03-08 at 17 09 03

Screenshot 2021-03-08 at 17 09 23

  • Is the Beats format identical to the Logstash format?

Yes, they're both identical.

Perhaps they can be grouped under the same category then, since their separation implies that the format is different.

  • When a key is invalidated, can the operation be reversed? (make it valid again?) If not, I suggest we call the action Remove

I don't think it can be reversed. @bytebilly do you know if there's a way to reissue a previously invalidated API key or is that permanent?

We should then call the action Revoke or Delete - maybe the latter, to imply that once it's gone, it's gone. The last column can simply become a trash can.

Regarding user experience:

  • The name of the API keys would have to come first, as this will more meaningful to the user than a generated string

The name is just descriptive and will be auto-generated in most cases as well (e.g. alerts). I would actually prefer to change this column to "Description".

If the field becomes the description, then yes, it makes sense to go second. Although it can take a lot of real estate in the table if we don0t put a size limit to it.

  • The format of the API key should be set/selected before generating the API key.
  • The format/type of API keys should also be displayed in the table.

An API key doesn't have a format/type. Every API key is the same. The issue is that different Elastic apps currently ask users to format or encode the API key differently in order to configure them. The way we should solve this issue is to agree on a single format and update our existing apps to support that so that we don't even have to show the dropdown menu at all.

++ it would be the ideal user experience,although I imagine it won't have that single format by 7.13, will it? If not, we should try to make more clear what happens there - it looks a bit confusing at the moment.

@cristina-eleonora
Copy link

Regarding default sorting - I wonder what's most important for a user: the time when it was created or the status of the API keys.

API keys which is expire soon - do they need to be addressed? When would a user create a temporary API key?

@legrego
Copy link
Member

legrego commented Mar 8, 2021

Are the first 8 characters always unique? Just want to make sure that a search will always match one item and not several.

These characters are an encoded portion of the API key's ID, so the odds of a duplicate are very slim. For prior art, GitHub identifies specific commits by the first 7 characters of its hash, and there are significantly more commits within a repository than there will be API Keys within a deployment.

All that said, the current iteration of this screen doesn't show any form of identifier. It just shows the API Key name, which has no uniqueness guarantees.

The general expectation is that the user who is logged in generates the API key.

I think that's fair if the user already understands how Elasticsearch API Keys work. This screen lives within Kibana's general Stack Management app, which itself isn't specific to the current user. I think displaying the username helps to educate/reinforce the distinction, since it behaves differently than other security assets within Stack Management (such as roles and role mappings)

Showing the user in the flyout implies that the user could be changed.

I don't agree with the implication, but I'm not opposed to revisiting this decision, either. We are rendering the username as a readonly piece of text -- not even within a disabled input field. I'm probably too close to this one, but that doesn't make me think it's editable.

  • How would this table look when there are cloud users generating the API keys?

It would show the user ID (e.g. "3890361203") instead of a username.

This is something that we definitely need to fix

I think we all agree, but this is unfortunately the best that we can do today given the information available to us. API Keys only store the username and realm_type. We don't have any more information than that about the creator, so we can't fall back to a more readable value (like email or full_name). Those fields simply aren't present.

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.

First pass through, mostly nits. In addition to the inline comments, we should add a couple functional tests once we're done addressing feedback:

  1. Accessibility tests within x-pack/test/accessibility/apps. None currently exist for API Keys, but we should remedy that since we're adding functionality here.
  2. An end-to-end test within x-pack/test/functional/apps/api_keys to verify that API Keys can be created

Great job on this so far, I really like what you've done!

Copy link
Contributor

@jportner jportner left a comment

Choose a reason for hiding this comment

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

Secondary review --

I love this! Awesome job! I identified a few nits in my comments below.

In addition: this has nothing to do with your changes, but I noticed a couple more things...

  1. When you invalidate an API key, it immediately disappears. This was a bit jarring, and my expectation would be that if it's invalidated (but not deleted) I'd still be able to see it on this page. This may also help users troubleshooting if an API key stops working -- navigate to this page and realize that you're using a key that has been invalidated.

    Perhaps we should consider changing the HTTP API to stop filtering out invalidated keys, and update the UI to display these with the appropriate status? This might be a follow-on task, but I wanted to bring the topic up here.

  2. This fetches all API keys and stores them in memory. I could envision potential performance issues if users have lots of API keys (especially if my suggestion above is implemented). I don't think we have a great alternative right now though.

Comment on lines +239 to +240
<SelectableTokenField
options={[
Copy link
Contributor

Choose a reason for hiding this comment

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

I was going to suggest making this full-width:

Suggested change
<SelectableTokenField
options={[
<SelectableTokenField
fullWidth={true}
options={[

but when I tested it locally, the input does not stretch to fill the entire container:

image

I'm not sure if this is a problem with EUI, or a problem with your TokenField/SelectableTokenField components. I figured I'd leave this here so you can let me know what you think 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I played around with increasing the size of the field as well but if it's too wide it doesn't look like an input field anymore and the copy button gets lost so I kept it at the standard size.

Comment on lines +241 to +254
{
key: 'base64',
value: btoa(concatenated),
icon: 'empty',
label: i18n.translate('xpack.security.management.apiKeys.base64Label', {
defaultMessage: 'Base64',
}),
description: i18n.translate(
'xpack.security.management.apiKeys.base64Description',
{
defaultMessage: 'Format used to authenticate with Elasticsearch.',
}
),
},
Copy link
Contributor

Choose a reason for hiding this comment

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

The current options are a mix of formats and purposes. I think it makes sense to focus on purposes, as that will be most intuitive to users.

Since the base64 option is meant to be used for authenticating to Elasticsearch, what do you think about changing the label and icon to match?

Suggested change
{
key: 'base64',
value: btoa(concatenated),
icon: 'empty',
label: i18n.translate('xpack.security.management.apiKeys.base64Label', {
defaultMessage: 'Base64',
}),
description: i18n.translate(
'xpack.security.management.apiKeys.base64Description',
{
defaultMessage: 'Format used to authenticate with Elasticsearch.',
}
),
},
{
key: 'elasticsearch',
value: btoa(concatenated),
icon: 'logoElasticsearch',
label: i18n.translate('xpack.security.management.apiKeys.elasticsearchLabel', {
defaultMessage: 'Elasticsearch',
}),
description: i18n.translate(
'xpack.security.management.apiKeys.elasticsearchDescription',
{
defaultMessage: 'Format used to authenticate with Elasticsearch.',
}
),
},

Edit: reading the comments on this issue (there are a lot!) it seems there's some desire to standardize on Base64 format and put more emphasis on that. I'm a bit ambivalent about it now but I still lean towards being more clear that this is the format you need to authenticate to Elasticsearch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like the idea but am worried that it could be confusing to have one option called "Elasticsearch" since they're all API keys for Elasticsearch. "Base64" is referenced throughout our docs as a format which is why I used it as a term here.

Once our products are aligned, I'd like to remove the entire dropdown menu and only show the base64 encoded API key.

Comment on lines +255 to +268
{
key: 'json',
value: JSON.stringify(this.state.createdApiKey),
icon: 'empty',
label: i18n.translate('xpack.security.management.apiKeys.jsonLabel', {
defaultMessage: 'JSON',
}),
description: i18n.translate(
'xpack.security.management.apiKeys.jsonDescription',
{
defaultMessage: 'Full API response.',
}
),
},
Copy link
Contributor

@jportner jportner Apr 2, 2021

Choose a reason for hiding this comment

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

If you agree with my comment above about changing the Base64 option (rename it to Elasticsearch), then maybe the JSON option should be moved to the end of the list, as it'll be the only one that is not formatted based on its purpose and it doesn't have an icon. WDYT?

Comment on lines 67 to 80
return (
<EuiEmptyPrompt
iconType="alert"
body={
<p>
<FormattedMessage
id="xpack.security.management.apiKeysEmptyPrompt.errorMessage"
defaultMessage="Could not load API keys."
/>
</p>
}
actions={children}
/>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Before your changes, the grid would display an error's error (errorTitle), message, and statusCode fields to the user.

I have not tested this, but it looks like your changes will no longer display that information. It also looks like it would still display the button to create a new API key, which we probably don't want to show if we haven't been able to load the API keys at all. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The page does not show the create button when initial data fetch failed.

I don't think we should show error codes or messages from the API unless they're intended to be displayed in the UI.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well, the page showed these details before, and other pages show similar details when we encounter unexpected errors. If we don't include this it will be difficult to troubleshoot in support cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

We chatted about this a bit offline, following up here --

To clarify, I tested this in both the current master branch and this PR by adding this bit to throw errors 50% of the time:

diff --git a/x-pack/plugins/security/server/routes/api_keys/get.ts b/x-pack/plugins/security/server/routes/api_keys/get.ts
index 7862401e1b8..0d44fe9acc3 100644
--- a/x-pack/plugins/security/server/routes/api_keys/get.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/get.ts
@@ -29,6 +29,9 @@ export function defineGetApiKeysRoutes({ router }: RouteDefinitionParams) {
     },
     createLicensedRouteHandler(async (context, request, response) => {
       try {
+        if (Math.random() < 0.5) {
+          throw new Error('Test error');
+        }
         const isAdmin = request.query.isAdmin === 'true';
         const apiResponse = await context.core.elasticsearch.client.asCurrentUser.security.getApiKey<{
           api_keys: ApiKey[];

Error in current master:

api-key-error

Error in this PR:

image

So it would be great if we could display the error status code and message in this callout.

Also I inadvertently discovered a bug -- if you encounter an error and click "Reload", even if the request is successful, the UI doesn't change. I tested this change and it fixed the issue:

diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
index 014030a8927..66eed00eb45 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/api_keys_grid_page.tsx
@@ -681,7 +681,7 @@ export class APIKeysGridPage extends Component<Props, State> {
     try {
       const { isAdmin } = this.state;
       const { apiKeys } = await this.props.apiKeysAPIClient.getApiKeys(isAdmin);
-      this.setState({ apiKeys });
+      this.setState({ apiKeys, error: undefined });
     } catch (e) {
       this.setState({ error: e });
     }

})
);

describe('APIKeysGridPage', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

The rewritten tests are great!

There are a couple of code paths that are not exercised and may merit some additional test cases:

  • Invalidate one API key (using the button on the right side)
  • Select + invalidate 2+ API keys (using the button at the top)
  • Switching the format of the API key -- though perhaps it would be better to add separate unit tests for token_field.tsx instead

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, thanks!

<EuiHealth color="primary">
<FormattedMessage
id="xpack.security.management.apiKeys.table.statusActive"
defaultMessage="Active"
Copy link
Contributor

Choose a reason for hiding this comment

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

When I first looked at this page, it was not clear to me that "Active" means the key will never expire.

Perhaps we should reword this? "Active (never expires)" may be sufficient. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see this as an issue to be honest. I think active/inactive status is a fairly common pattern and shouldn't confuse users. My expectation wouldn't be that API Keys expire (or even have the ability to expire) unless specifically mentioned which is why I'm only calling it out if the API key has been specifically configured to automatically expire.

Comment on lines +37 to +41
const apiKey = await getAuthenticationService().apiKeys.create(request, request.body);

if (!apiKey) {
return response.badRequest({ body: { message: `API Keys are not available` } });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It was not clear to me at first that the only way you're going to get a null / "falsey" result is if API keys are not enabled.

I don't think you need to add a redundant check for that, but perhaps an extra comment here clarifying would be a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeh, I'm not sure why this was treated as a null return value rather than an error but the documentation for that belongs in the authentication service, not the route handler, so going to add a comment there instead.

@thomheymann
Copy link
Contributor Author

  1. When you invalidate an API key, it immediately disappears. This was a bit jarring, and my expectation would be that if it's invalidated (but not deleted) I'd still be able to see it on this page. This may also help users troubleshooting if an API key stops working -- navigate to this page and realize that you're using a key that has been invalidated.
    Perhaps we should consider changing the HTTP API to stop filtering out invalidated keys, and update the UI to display these with the appropriate status? This might be a follow-on task, but I wanted to bring the topic up here.

Yeh, I agree, the decision to show expired API Keys but hide invalidated ones seems strange. Happy to make that change but think it should be a future improvement as we'd need to provide a way to hide expired API Keys or filter by status somehow.

  1. This fetches all API keys and stores them in memory. I could envision potential performance issues if users have lots of API keys (especially if my suggestion above is implemented). I don't think we have a great alternative right now though.

I've raised this point too but the API does not allow pagination or server side search / filtering so our options are limited.

@thomheymann
Copy link
Contributor Author

@elasticmachine merge upstream

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.

Thanks for addressing my feedback, this looks great!

The only thing we have left is to update the docs located at docs/user/security/api-keys/index.asciidoc:

  • This includes a screenshot which needs updating to reflect the new design.
  • It includes a section instructing users to visit the Kibana Console ("Dev tools") in order to create API Keys. We should instead direct them to the new interface.

Copy link
Contributor

@jportner jportner left a comment

Choose a reason for hiding this comment

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

I looked at your most recent changes, looks great!

I have two last minor concerns (error message rendering and page behavior when an error is encountered), I added some details in a comment here: #92610 (comment)

@thomheymann
Copy link
Contributor Author

thomheymann commented Apr 8, 2021

@jportner I've added a detailed error description accordion after discussing best option with design team.

chrome-capture (9)

@legrego As discussed, I have changed all references of "invalidate" to "delete" since invalidated API Keys are deleted automatically after 7 days anyways and there's no way to bring back invalidated keys so there's not much value in showing them to the user.

chrome-capture (10)

Copy link
Contributor

@jportner jportner left a comment

Choose a reason for hiding this comment

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

@jportner I've added a detailed error description accordion after discussing best option with design team.

Looks great, thanks!

legrego
legrego previously approved these changes Apr 12, 2021
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.

LGTM on green CI

@legrego legrego dismissed their stale review April 12, 2021 12:33

test failures

@@ -5,182 +5,292 @@
* 2.0.
*/

import { EuiCallOut } from '@elastic/eui';
import type { ReactWrapper } from 'enzyme';
import {
Copy link
Member

Choose a reason for hiding this comment

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

We've noticed that the @testing-library-based tests have been timing out quite a bit, and appears to be the result of the latest test failures here. Do you know what could be causing these tests to run for so long? 15 seconds should be plenty of time for these tests to complete.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is the timeout per test or for the entire test suite?
All of them together do take 18-20 seconds on my local machine but individually are much faster:

  • loads and displays API keys (163 ms)
  • displays callout when API keys are disabled (28 ms)
  • displays error when user does not have required permissions (12 ms)
  • displays error when fetching API keys fails (58 ms)
  • creates API key when submitting form, redirects back and displays base64 (3191 ms)
  • creates API key with optional expiration, redirects back and displays base64 (3773 ms)
  • deletes api key using cta button (159 ms)
  • deletes multiple api keys using bulk select (744 ms)

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure exactly what the timeout is but it just caused CI to fail again:
#97085

@thomheymann
Copy link
Contributor Author

@elasticmachine merge upstream

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
security 474 475 +1

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
kibanaReact 311.4KB 312.0KB +650.0B
security 681.6KB 713.4KB +31.7KB
total +32.4KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
kibanaReact 115.1KB 117.5KB +2.4KB
security 122.7KB 124.4KB +1.8KB
total +4.1KB

History

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

@thomheymann thomheymann merged commit 69f013e into elastic:master Apr 13, 2021
thomheymann added a commit to thomheymann/kibana that referenced this pull request Apr 13, 2021
* Added ability to create API keys

* Remove hard coded colours

* Added unit tests

* Fix linting errors

* Display full base64 encoded API key

* Fix linting errors

* Fix more linting error and unit tests

* Added suggestions from code review

* fix unit tests

* move code editor field into separate component

* fixed tests

* fixed test

* Fixed functional tests

* replaced theme hook with eui import

* Revert to manual theme detection

* added storybook

* Additional unit and functional tests

* Added suggestions from code review

* Remove unused translations

* Updated docs and added detailed error description

* Removed unused messages

* Updated unit test

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Larry Gregory <larry.gregory@elastic.co>
# Conflicts:
#	x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx
#	x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx
thomheymann added a commit that referenced this pull request Apr 13, 2021
* Added ability to create API keys (#92610)

* Added ability to create API keys

* Remove hard coded colours

* Added unit tests

* Fix linting errors

* Display full base64 encoded API key

* Fix linting errors

* Fix more linting error and unit tests

* Added suggestions from code review

* fix unit tests

* move code editor field into separate component

* fixed tests

* fixed test

* Fixed functional tests

* replaced theme hook with eui import

* Revert to manual theme detection

* added storybook

* Additional unit and functional tests

* Added suggestions from code review

* Remove unused translations

* Updated docs and added detailed error description

* Removed unused messages

* Updated unit test

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Larry Gregory <larry.gregory@elastic.co>
# Conflicts:
#	x-pack/plugins/security/public/management/api_keys/api_keys_management_app.test.tsx
#	x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx

* Updated translations
@graphaelli graphaelli mentioned this pull request Apr 22, 2021
2 tasks
@legrego legrego added the Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! label Apr 26, 2021
@elasticmachine
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:feature Makes this part of the condensed release notes Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! v7.13.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create API Key
9 participants