mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Create API keys with metadata (#100682)
This commit is contained in:
parent
77f0a2a7ad
commit
27f790c8f2
8 changed files with 106 additions and 33 deletions
Binary file not shown.
Before Width: | Height: | Size: 369 KiB After Width: | Height: | Size: 305 KiB |
|
@ -15,6 +15,7 @@ export interface ApiKey {
|
|||
creation: number;
|
||||
expiration: number;
|
||||
invalidated: boolean;
|
||||
metadata: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ApiKeyToInvalidate {
|
||||
|
|
|
@ -28,6 +28,7 @@ export interface CreateApiKeyRequest {
|
|||
name: string;
|
||||
expiration?: string;
|
||||
role_descriptors?: ApiKeyRoleDescriptors;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface CreateApiKeyResponse {
|
||||
|
|
|
@ -45,7 +45,9 @@ export interface ApiKeyFormValues {
|
|||
expiration: string;
|
||||
customExpiration: boolean;
|
||||
customPrivileges: boolean;
|
||||
includeMetadata: boolean;
|
||||
role_descriptors: string;
|
||||
metadata: string;
|
||||
}
|
||||
|
||||
export interface CreateApiKeyFlyoutProps {
|
||||
|
@ -59,30 +61,9 @@ const defaultDefaultValues: ApiKeyFormValues = {
|
|||
expiration: '',
|
||||
customExpiration: false,
|
||||
customPrivileges: false,
|
||||
role_descriptors: JSON.stringify(
|
||||
{
|
||||
'role-a': {
|
||||
cluster: ['all'],
|
||||
indices: [
|
||||
{
|
||||
names: ['index-a*'],
|
||||
privileges: ['read'],
|
||||
},
|
||||
],
|
||||
},
|
||||
'role-b': {
|
||||
cluster: ['all'],
|
||||
indices: [
|
||||
{
|
||||
names: ['index-b*'],
|
||||
privileges: ['all'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
includeMetadata: false,
|
||||
role_descriptors: '{}',
|
||||
metadata: '{}',
|
||||
};
|
||||
|
||||
export const CreateApiKeyFlyout: FunctionComponent<CreateApiKeyFlyoutProps> = ({
|
||||
|
@ -227,7 +208,6 @@ export const CreateApiKeyFlyout: FunctionComponent<CreateApiKeyFlyoutProps> = ({
|
|||
<EuiSpacer />
|
||||
<EuiFormFieldset>
|
||||
<EuiSwitch
|
||||
id="apiKeyCustom"
|
||||
label={i18n.translate(
|
||||
'xpack.security.accountManagement.createApiKey.customPrivilegesLabel',
|
||||
{
|
||||
|
@ -270,7 +250,6 @@ export const CreateApiKeyFlyout: FunctionComponent<CreateApiKeyFlyoutProps> = ({
|
|||
<EuiSpacer />
|
||||
<EuiFormFieldset>
|
||||
<EuiSwitch
|
||||
name="customExpiration"
|
||||
label={i18n.translate(
|
||||
'xpack.security.accountManagement.createApiKey.customExpirationLabel',
|
||||
{
|
||||
|
@ -312,6 +291,48 @@ export const CreateApiKeyFlyout: FunctionComponent<CreateApiKeyFlyoutProps> = ({
|
|||
)}
|
||||
</EuiFormFieldset>
|
||||
|
||||
<EuiSpacer />
|
||||
<EuiFormFieldset>
|
||||
<EuiSwitch
|
||||
label={i18n.translate(
|
||||
'xpack.security.accountManagement.createApiKey.includeMetadataLabel',
|
||||
{
|
||||
defaultMessage: 'Include metadata',
|
||||
}
|
||||
)}
|
||||
checked={!!form.values.includeMetadata}
|
||||
onChange={(e) => form.setValue('includeMetadata', e.target.checked)}
|
||||
/>
|
||||
{form.values.includeMetadata && (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFormRow
|
||||
helpText={
|
||||
<DocLink
|
||||
app="elasticsearch"
|
||||
doc="security-api-create-api-key.html#security-api-create-api-key-request-body"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.security.accountManagement.createApiKey.metadataHelpText"
|
||||
defaultMessage="Learn how to structure metadata."
|
||||
/>
|
||||
</DocLink>
|
||||
}
|
||||
error={form.errors.metadata}
|
||||
isInvalid={form.touched.metadata && !!form.errors.metadata}
|
||||
>
|
||||
<CodeEditorField
|
||||
value={form.values.metadata!}
|
||||
onChange={(value) => form.setValue('metadata', value)}
|
||||
languageId="xjson"
|
||||
height={200}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
)}
|
||||
</EuiFormFieldset>
|
||||
|
||||
{/* Hidden submit button is required for enter key to trigger form submission */}
|
||||
<input type="submit" hidden />
|
||||
</EuiForm>
|
||||
|
@ -363,6 +384,28 @@ export function validate(values: ApiKeyFormValues) {
|
|||
}
|
||||
}
|
||||
|
||||
if (values.includeMetadata) {
|
||||
if (!values.metadata) {
|
||||
errors.metadata = i18n.translate(
|
||||
'xpack.security.management.apiKeys.createApiKey.metadataRequired',
|
||||
{
|
||||
defaultMessage: 'Enter metadata or disable this option.',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
JSON.parse(values.metadata);
|
||||
} catch (e) {
|
||||
errors.metadata = i18n.translate(
|
||||
'xpack.security.management.apiKeys.createApiKey.invalidJsonError',
|
||||
{
|
||||
defaultMessage: 'Enter valid JSON.',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
@ -374,5 +417,6 @@ export function mapValues(values: ApiKeyFormValues): CreateApiKeyRequest {
|
|||
values.customPrivileges && values.role_descriptors
|
||||
? JSON.parse(values.role_descriptors)
|
||||
: undefined,
|
||||
metadata: values.includeMetadata && values.metadata ? JSON.parse(values.metadata) : undefined,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -30,15 +30,21 @@ export interface CreateAPIKeyParams {
|
|||
name: string;
|
||||
role_descriptors: Record<string, any>;
|
||||
expiration?: string;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
interface GrantAPIKeyParams {
|
||||
api_key: CreateAPIKeyParams;
|
||||
grant_type: 'password' | 'access_token';
|
||||
username?: string;
|
||||
password?: string;
|
||||
access_token?: string;
|
||||
}
|
||||
type GrantAPIKeyParams =
|
||||
| {
|
||||
api_key: CreateAPIKeyParams;
|
||||
grant_type: 'password';
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
| {
|
||||
api_key: CreateAPIKeyParams;
|
||||
grant_type: 'access_token';
|
||||
access_token: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the params for invalidating multiple API keys
|
||||
|
|
|
@ -85,6 +85,9 @@ describe('Create API Key route', () => {
|
|||
role_descriptors: {
|
||||
role_1: {},
|
||||
},
|
||||
metadata: {
|
||||
foo: 'bar',
|
||||
},
|
||||
};
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
|
|
|
@ -29,6 +29,7 @@ export function defineCreateApiKeyRoutes({
|
|||
defaultValue: {},
|
||||
}
|
||||
),
|
||||
metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -46,6 +46,23 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
expect(name).to.eql('test_api_key');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow an API Key to be created with metadata', async () => {
|
||||
await supertest
|
||||
.post('/internal/security/api_key')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send({
|
||||
name: 'test_api_key_with_metadata',
|
||||
metadata: {
|
||||
foo: 'bar',
|
||||
},
|
||||
})
|
||||
.expect(200)
|
||||
.then((response: Record<string, any>) => {
|
||||
const { name } = response.body;
|
||||
expect(name).to.eql('test_api_key_with_metadata');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue