mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security in Core] Exposes apiKeys
from core.security.authc
(#186910)
## Summary Expose `apiKeys` as a service under `core.security.authc.apiKeys`. Closes https://github.com/elastic/kibana/issues/184764 ### Details PR introduces a new API Keys Service which is accessible under the `authc` namespace in core.security. The service exposes the public API that was already available on the server-side in the security plugin. The service is initialized and registered with core using the `delegate_api` - allowing access to the service within the core plugin without the need for the `security` plugin. Note: I had to move quite a few types/functions around to prevent cyclical dependencies. ### Plugins and the APIs that use the current `apiKeys` function from the security plugin <details> <summary> Expand for table with details </summary> | Plugin | File | API used | Can be migrated | |--------|--------|--------|--------| | alerting | x-pack/plugins/alerting/plugin/server.ts | areApiKeysEnabled() | ✅ | | | x-pack/plugins/alerting/server/rules_client_factory.ts | grantAsInternalUser() | ❌ | | | x-pack/plugins/alerting/server/task.ts | invalidatedAsInternalUser() | ❌ | | enterprise_search | x-pack/plugins/enterprise_search/server/routes/enterprise_search/api_keys | create() | ✅ | | | x-pack/plugins/enterprise_search/server/lib/indices/create_api_key.ts | create() | ✅ | | fleet | x-pack/plugins/fleet/server/routes/setup/handlers.ts | areApiKeysEnabled() | ✅ | | | x-pack/plugins/fleet/server/services/api_keys/security | invalidateAsInternalUser() | ❌ | | | x-pack/plugins/fleet/server/services/api_keys/transform_api_keys.ts | grantAsInternalUser() | ❌ | | | x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts | areApiKeysEnabled() | ✅ | | | x-pack/plugins/fleet/server/services/setup/fleet_server_policies_enrollment_keys.ts | areAPIKeysEnabled() | ✅ | | | x-pack/plugins/observability_solution/apm/server/routes/agent_keys/get_agent_keys_privileges.ts | areAPIKeysEnabled() | ✅ | | observability_solution | x-pack/plugins/observability_solution/entity_manager/server/lib/auth/api_key/api_key.ts | areAPIKeysEnabled | ✅ | | | | validate | ✅ | | | | grantAsInternalUser | ❌ | | | x-pack/plugins/observability_solution/entity_manager/server/routes/enablement/disable.ts | invalidateAsInternalUser | ❌ | | | x-pack/plugins/observability_solution/entity_manager/server/routes/enablement/enable.ts | invalidateAsInternalUser | ❌ | | | x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts | create | ✅ | | | x-pack/plugins/observability_solution/synthetics/server/routes/synthetics_service/enablement.ts | invalidateAsInternalUser | ❌ | | | x-pack/plugins/observability_solution/synthetics/server/synthetics_service/get_api_key.ts | validate | ✅ | | | | areAPIKeysEnabled | ✅ | | | | grantAsInternalUser | ❌ | | | | create | ✅ | | serverless_search | x-pack/plugins/serverless_search/server/routes/api_key_routes.ts | create | ✅ | | | x-pack/plugins/transform/server/routes/api/reauthorize_transforms/route_handler_factory.ts | grantAsInternalUser | ❌ | | | x-pack/plugins/upgrade_assistant/server/lib/reindexing/credential_store.ts | grantAsInternalUser | ❌ | | | | invalidateAsInternalUser | ❌ | | | | areAPIKeysEnabled() | ✅ | </details> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
f484acad11
commit
ff9a48edbe
31 changed files with 644 additions and 269 deletions
|
@ -26,6 +26,16 @@ export class CoreSecurityRouteHandlerContext implements SecurityRequestHandlerCo
|
|||
if (this.#authc == null) {
|
||||
this.#authc = {
|
||||
getCurrentUser: () => this.securityStart.authc.getCurrentUser(this.request),
|
||||
apiKeys: {
|
||||
areAPIKeysEnabled: () => this.securityStart.authc.apiKeys.areAPIKeysEnabled(),
|
||||
create: (createParams) =>
|
||||
this.securityStart.authc.apiKeys.create(this.request, createParams),
|
||||
update: (updateParams) =>
|
||||
this.securityStart.authc.apiKeys.update(this.request, updateParams),
|
||||
validate: (apiKeyParams) => this.securityStart.authc.apiKeys.validate(apiKeyParams),
|
||||
invalidate: (apiKeyParams) =>
|
||||
this.securityStart.authc.apiKeys.invalidate(this.request, apiKeyParams),
|
||||
},
|
||||
};
|
||||
}
|
||||
return this.#authc;
|
||||
|
|
|
@ -15,6 +15,16 @@ describe('convertSecurityApi', () => {
|
|||
const source: CoreSecurityDelegateContract = {
|
||||
authc: {
|
||||
getCurrentUser: jest.fn(),
|
||||
apiKeys: {
|
||||
areAPIKeysEnabled: jest.fn(),
|
||||
areCrossClusterAPIKeysEnabled: jest.fn(),
|
||||
validate: jest.fn(),
|
||||
invalidate: jest.fn(),
|
||||
invalidateAsInternalUser: jest.fn(),
|
||||
grantAsInternalUser: jest.fn(),
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
},
|
||||
audit: {
|
||||
asScoped: jest.fn().mockReturnValue(createAuditLoggerMock.create()),
|
||||
|
@ -23,6 +33,7 @@ describe('convertSecurityApi', () => {
|
|||
};
|
||||
const output = convertSecurityApi(source);
|
||||
expect(output.authc.getCurrentUser).toBe(source.authc.getCurrentUser);
|
||||
expect(output.authc.apiKeys).toBe(source.authc.apiKeys);
|
||||
expect(output.audit.asScoped).toBe(source.audit.asScoped);
|
||||
expect(output.audit.withoutRequest).toBe(source.audit.withoutRequest);
|
||||
});
|
||||
|
|
|
@ -23,6 +23,15 @@ describe('getDefaultSecurityImplementation', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('authc.apiKeys', () => {
|
||||
it('returns stub object', async () => {
|
||||
const { apiKeys } = implementation.authc;
|
||||
const areAPIKeysEnabled = await apiKeys.areAPIKeysEnabled();
|
||||
|
||||
expect(areAPIKeysEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('audit.asScoped', () => {
|
||||
it('returns null', async () => {
|
||||
const logger = implementation.audit.asScoped({} as any);
|
||||
|
|
|
@ -8,10 +8,23 @@
|
|||
|
||||
import type { CoreSecurityDelegateContract } from '@kbn/core-security-server';
|
||||
|
||||
const API_KEYS_DISABLED_ERROR = new Error('API keys are disabled');
|
||||
const REJECT_WHEN_API_KEYS_DISABLED = () => Promise.reject(API_KEYS_DISABLED_ERROR);
|
||||
|
||||
export const getDefaultSecurityImplementation = (): CoreSecurityDelegateContract => {
|
||||
return {
|
||||
authc: {
|
||||
getCurrentUser: () => null,
|
||||
apiKeys: {
|
||||
areAPIKeysEnabled: () => Promise.resolve(false),
|
||||
areCrossClusterAPIKeysEnabled: () => Promise.resolve(false),
|
||||
create: REJECT_WHEN_API_KEYS_DISABLED,
|
||||
update: REJECT_WHEN_API_KEYS_DISABLED,
|
||||
grantAsInternalUser: REJECT_WHEN_API_KEYS_DISABLED,
|
||||
validate: REJECT_WHEN_API_KEYS_DISABLED,
|
||||
invalidate: REJECT_WHEN_API_KEYS_DISABLED,
|
||||
invalidateAsInternalUser: REJECT_WHEN_API_KEYS_DISABLED,
|
||||
},
|
||||
},
|
||||
audit: {
|
||||
asScoped: () => {
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
export { securityServiceMock } from './src/security_service.mock';
|
||||
export type { InternalSecurityStartMock, SecurityStartMock } from './src/security_service.mock';
|
||||
export { auditLoggerMock } from './src/audit.mock';
|
||||
export { apiKeysMock } from './src/api_keys.mock';
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
/*
|
||||
* 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.
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
|
||||
import type { APIKeys } from './api_keys';
|
||||
import type { APIKeysService } from '@kbn/core-security-server';
|
||||
|
||||
export const apiKeysMock = {
|
||||
create: (): jest.Mocked<PublicMethodsOf<APIKeys>> => ({
|
||||
create: (): jest.MockedObjectDeep<APIKeysService> => ({
|
||||
areAPIKeysEnabled: jest.fn(),
|
||||
areCrossClusterAPIKeysEnabled: jest.fn(),
|
||||
create: jest.fn(),
|
|
@ -15,6 +15,7 @@ import type {
|
|||
InternalSecurityServiceSetup,
|
||||
InternalSecurityServiceStart,
|
||||
} from '@kbn/core-security-server-internal';
|
||||
import { apiKeysMock } from './api_keys.mock';
|
||||
import { auditServiceMock, type MockedAuditService } from './audit.mock';
|
||||
import { mockAuthenticatedUser, MockAuthenticatedUserProps } from '@kbn/core-security-common/mocks';
|
||||
|
||||
|
@ -35,6 +36,7 @@ const createStartMock = (): SecurityStartMock => {
|
|||
const mock = {
|
||||
authc: {
|
||||
getCurrentUser: jest.fn(),
|
||||
apiKeys: apiKeysMock.create(),
|
||||
},
|
||||
audit: auditServiceMock.create(),
|
||||
};
|
||||
|
@ -61,6 +63,7 @@ const createInternalStartMock = (): InternalSecurityStartMock => {
|
|||
const mock = {
|
||||
authc: {
|
||||
getCurrentUser: jest.fn(),
|
||||
apiKeys: apiKeysMock.create(),
|
||||
},
|
||||
audit: auditServiceMock.create(),
|
||||
};
|
||||
|
@ -82,6 +85,13 @@ const createRequestHandlerContextMock = () => {
|
|||
const mock: jest.MockedObjectDeep<SecurityRequestHandlerContext> = {
|
||||
authc: {
|
||||
getCurrentUser: jest.fn(),
|
||||
apiKeys: {
|
||||
areAPIKeysEnabled: jest.fn(),
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
validate: jest.fn(),
|
||||
invalidate: jest.fn(),
|
||||
},
|
||||
},
|
||||
audit: {
|
||||
logger: {
|
||||
|
|
|
@ -26,4 +26,26 @@ export type {
|
|||
AuditRequest,
|
||||
} from './src/audit_logging/audit_events';
|
||||
export type { AuditLogger } from './src/audit_logging/audit_logger';
|
||||
|
||||
export type {
|
||||
APIKeysServiceWithContext,
|
||||
APIKeysService,
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
InvalidateAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
CreateRestAPIKeyParams,
|
||||
CreateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
CreateCrossClusterAPIKeyParams,
|
||||
GrantAPIKeyResult,
|
||||
UpdateAPIKeyParams,
|
||||
UpdateAPIKeyResult,
|
||||
UpdateCrossClusterAPIKeyParams,
|
||||
UpdateRestAPIKeyParams,
|
||||
UpdateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
} from './src/authentication/api_keys';
|
||||
|
||||
export type { KibanaPrivilegesType, ElasticsearchPrivilegesType } from './src/roles';
|
||||
export { isCreateRestAPIKeyParams } from './src/authentication/api_keys';
|
||||
export type { CoreFipsService } from './src/fips';
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import type { AuthenticatedUser } from '@kbn/core-security-common';
|
||||
import type { APIKeysService } from './authentication/api_keys';
|
||||
|
||||
/**
|
||||
* Core's authentication service
|
||||
|
@ -22,4 +23,5 @@ export interface CoreAuthenticationService {
|
|||
* @param request The request to retrieve the authenticated user for.
|
||||
*/
|
||||
getCurrentUser(request: KibanaRequest): AuthenticatedUser | null;
|
||||
apiKeys: APIKeysService;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
|
||||
import { ElasticsearchPrivilegesType, KibanaPrivilegesType } from '../../roles';
|
||||
|
||||
/**
|
||||
* Interface for managing API keys in Elasticsearch, including creation,
|
||||
* validation, and invalidation of API keys,
|
||||
* as well as checking the status of API key features.
|
||||
*/
|
||||
export interface APIKeys {
|
||||
/**
|
||||
* Determines if API Keys are enabled in Elasticsearch.
|
||||
*/
|
||||
areAPIKeysEnabled(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Determines if Cross-Cluster API Keys are enabled in Elasticsearch.
|
||||
*/
|
||||
areCrossClusterAPIKeysEnabled(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Tries to create an API key for the current user.
|
||||
*
|
||||
* Returns newly created API key or `null` if API keys are disabled.
|
||||
*
|
||||
* User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.
|
||||
*
|
||||
* @param request Request instance.
|
||||
* @param createParams The params to create an API key
|
||||
*/
|
||||
create(
|
||||
request: KibanaRequest,
|
||||
createParams: CreateAPIKeyParams
|
||||
): Promise<CreateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Attempts update an API key with the provided 'role_descriptors' and 'metadata'
|
||||
*
|
||||
* Returns `updated`, `true` if the update was successful, `false` if there was nothing to update
|
||||
*
|
||||
* User needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.
|
||||
*
|
||||
* @param request Request instance.
|
||||
* @param updateParams The params to edit an API key
|
||||
*/
|
||||
update(
|
||||
request: KibanaRequest,
|
||||
updateParams: UpdateAPIKeyParams
|
||||
): Promise<UpdateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to grant an API key for the current user.
|
||||
* @param request Request instance.
|
||||
* @param createParams Create operation parameters.
|
||||
*/
|
||||
grantAsInternalUser(
|
||||
request: KibanaRequest,
|
||||
createParams: CreateRestAPIKeyParams | CreateRestAPIKeyWithKibanaPrivilegesParams
|
||||
): Promise<GrantAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to validate an API key.
|
||||
* @param apiKeyPrams ValidateAPIKeyParams.
|
||||
*/
|
||||
validate(apiKeyPrams: ValidateAPIKeyParams): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Tries to invalidate an API keys.
|
||||
* @param request Request instance.
|
||||
* @param params The params to invalidate an API keys.
|
||||
*/
|
||||
invalidate(
|
||||
request: KibanaRequest,
|
||||
params: InvalidateAPIKeysParams
|
||||
): Promise<InvalidateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to invalidate the API keys by using the internal user.
|
||||
* @param params The params to invalidate the API keys.
|
||||
*/
|
||||
invalidateAsInternalUser(params: InvalidateAPIKeysParams): Promise<InvalidateAPIKeyResult | null>;
|
||||
}
|
||||
|
||||
export type CreateAPIKeyParams =
|
||||
| CreateRestAPIKeyParams
|
||||
| CreateRestAPIKeyWithKibanaPrivilegesParams
|
||||
| CreateCrossClusterAPIKeyParams;
|
||||
|
||||
/**
|
||||
* Response of Kibana Create API key endpoint.
|
||||
*/
|
||||
export type CreateAPIKeyResult = estypes.SecurityCreateApiKeyResponse;
|
||||
|
||||
export interface CreateRestAPIKeyParams {
|
||||
type?: 'rest';
|
||||
expiration?: string;
|
||||
name: string;
|
||||
role_descriptors: Record<string, { [key: string]: any }>;
|
||||
metadata?: { [key: string]: any };
|
||||
}
|
||||
|
||||
export interface CreateRestAPIKeyWithKibanaPrivilegesParams {
|
||||
type?: 'rest';
|
||||
expiration?: string;
|
||||
name: string;
|
||||
metadata?: { [key: string]: any };
|
||||
kibana_role_descriptors: Record<
|
||||
string,
|
||||
{
|
||||
elasticsearch: ElasticsearchPrivilegesType & { [key: string]: unknown };
|
||||
kibana: KibanaPrivilegesType;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
export interface CreateCrossClusterAPIKeyParams {
|
||||
type: 'cross_cluster';
|
||||
expiration?: string;
|
||||
name: string;
|
||||
metadata?: { [key: string]: any };
|
||||
access: {
|
||||
search?: Array<{
|
||||
names: string[];
|
||||
query?: unknown;
|
||||
field_security?: unknown;
|
||||
allow_restricted_indices?: boolean;
|
||||
}>;
|
||||
replication?: Array<{
|
||||
names: string[];
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GrantAPIKeyResult {
|
||||
/**
|
||||
* Unique id for this API key
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Name for this API key
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Generated API key
|
||||
*/
|
||||
api_key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the parameters for validating API Key credentials.
|
||||
*/
|
||||
export interface ValidateAPIKeyParams {
|
||||
/**
|
||||
* Unique id for this API key
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Generated API Key (secret)
|
||||
*/
|
||||
api_key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the params for invalidating multiple API keys
|
||||
*/
|
||||
export interface InvalidateAPIKeysParams {
|
||||
/**
|
||||
* List of unique API key IDs
|
||||
*/
|
||||
ids: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value when invalidating an API key in Elasticsearch.
|
||||
*/
|
||||
export interface InvalidateAPIKeyResult {
|
||||
/**
|
||||
* The IDs of the API keys that were invalidated as part of the request.
|
||||
*/
|
||||
invalidated_api_keys: string[];
|
||||
/**
|
||||
* The IDs of the API keys that were already invalidated.
|
||||
*/
|
||||
previously_invalidated_api_keys: string[];
|
||||
/**
|
||||
* The number of errors that were encountered when invalidating the API keys.
|
||||
*/
|
||||
error_count: number;
|
||||
/**
|
||||
* Details about these errors. This field is not present in the response when error_count is 0.
|
||||
*/
|
||||
error_details?: Array<{
|
||||
type?: string;
|
||||
reason?: string;
|
||||
caused_by?: {
|
||||
type?: string;
|
||||
reason?: string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response of Kibana Update API key endpoint.
|
||||
*/
|
||||
export type UpdateAPIKeyResult = estypes.SecurityUpdateApiKeyResponse;
|
||||
|
||||
/**
|
||||
* Request body of Kibana Update API key endpoint.
|
||||
*/
|
||||
export type UpdateAPIKeyParams =
|
||||
| UpdateRestAPIKeyParams
|
||||
| UpdateCrossClusterAPIKeyParams
|
||||
| UpdateRestAPIKeyWithKibanaPrivilegesParams;
|
||||
|
||||
export interface UpdateRestAPIKeyParams {
|
||||
id: string;
|
||||
type?: 'rest';
|
||||
expiration?: string;
|
||||
role_descriptors: Record<string, { [key: string]: unknown }>;
|
||||
metadata?: { [key: string]: any };
|
||||
}
|
||||
|
||||
export interface UpdateCrossClusterAPIKeyParams {
|
||||
id: string;
|
||||
type: 'cross_cluster';
|
||||
expiration?: string;
|
||||
metadata?: { [key: string]: any };
|
||||
access: {
|
||||
search?: Array<{
|
||||
names: string[];
|
||||
query?: unknown;
|
||||
field_security?: unknown;
|
||||
allow_restricted_indices?: boolean;
|
||||
}>;
|
||||
replication?: Array<{
|
||||
names: string[];
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UpdateRestAPIKeyWithKibanaPrivilegesParams {
|
||||
id: string;
|
||||
type?: 'rest';
|
||||
expiration?: string;
|
||||
metadata?: { [key: string]: any };
|
||||
kibana_role_descriptors: Record<
|
||||
string,
|
||||
{
|
||||
elasticsearch: ElasticsearchPrivilegesType & { [key: string]: unknown };
|
||||
kibana: KibanaPrivilegesType;
|
||||
}
|
||||
>;
|
||||
}
|
||||
|
||||
export function isCreateRestAPIKeyParams(params: any): params is CreateRestAPIKeyParams {
|
||||
return 'role_descriptors' in params;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
UpdateAPIKeyParams,
|
||||
UpdateAPIKeyResult,
|
||||
ValidateAPIKeyParams,
|
||||
InvalidateAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
} from './api_keys';
|
||||
|
||||
/**
|
||||
* Public API Keys service exposed through core context to manage
|
||||
* API keys in Elasticsearch, including creation,
|
||||
* validation, and invalidation of API keys,
|
||||
* as well as checking the status of API key features.
|
||||
*/
|
||||
export interface APIKeysServiceWithContext {
|
||||
/**
|
||||
* Determines if API Keys are enabled in Elasticsearch.
|
||||
*/
|
||||
areAPIKeysEnabled(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Tries to create an API key for the current user.
|
||||
*
|
||||
* Returns newly created API key or `null` if API keys are disabled.
|
||||
*
|
||||
* User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.
|
||||
*
|
||||
* @param createParams The params to create an API key
|
||||
*/
|
||||
create(createParams: CreateAPIKeyParams): Promise<CreateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Attempts update an API key with the provided 'role_descriptors' and 'metadata'
|
||||
*
|
||||
* Returns `updated`, `true` if the update was successful, `false` if there was nothing to update
|
||||
*
|
||||
* User needs `manage_api_key` privilege to update REST API keys and `manage_security` for cross-cluster API keys.
|
||||
*
|
||||
* @param updateParams The params to edit an API key
|
||||
*/
|
||||
update(updateParams: UpdateAPIKeyParams): Promise<UpdateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to validate an API key.
|
||||
* @param apiKeyPrams ValidateAPIKeyParams.
|
||||
*/
|
||||
validate(apiKeyPrams: ValidateAPIKeyParams): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Tries to invalidate an API keys.
|
||||
* @param params The params to invalidate an API keys.
|
||||
*/
|
||||
invalidate(params: InvalidateAPIKeysParams): Promise<InvalidateAPIKeyResult | null>;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type {
|
||||
APIKeys as APIKeysService,
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
InvalidateAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
CreateRestAPIKeyParams,
|
||||
CreateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
CreateCrossClusterAPIKeyParams,
|
||||
GrantAPIKeyResult,
|
||||
UpdateAPIKeyParams,
|
||||
UpdateAPIKeyResult,
|
||||
UpdateCrossClusterAPIKeyParams,
|
||||
UpdateRestAPIKeyParams,
|
||||
UpdateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
} from './api_keys';
|
||||
export type { APIKeysServiceWithContext } from './api_keys_context';
|
||||
export { isCreateRestAPIKeyParams } from './api_keys';
|
|
@ -7,7 +7,9 @@
|
|||
*/
|
||||
|
||||
import type { AuthenticatedUser } from '@kbn/core-security-common';
|
||||
|
||||
import { AuditLogger } from './audit_logging/audit_logger';
|
||||
import type { APIKeysServiceWithContext } from './authentication/api_keys';
|
||||
|
||||
export interface SecurityRequestHandlerContext {
|
||||
authc: AuthcRequestHandlerContext;
|
||||
|
@ -16,6 +18,7 @@ export interface SecurityRequestHandlerContext {
|
|||
|
||||
export interface AuthcRequestHandlerContext {
|
||||
getCurrentUser(): AuthenticatedUser | null;
|
||||
apiKeys: APIKeysServiceWithContext;
|
||||
}
|
||||
|
||||
export interface AuditRequestHandlerContext {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './schema';
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Type representing Elasticsearch specific portion of the role definition.
|
||||
*/
|
||||
export interface ElasticsearchPrivilegesType {
|
||||
cluster?: string[];
|
||||
remote_cluster?: Array<{
|
||||
privileges: string[];
|
||||
clusters: string[];
|
||||
}>;
|
||||
indices?: Array<{
|
||||
names: string[];
|
||||
field_security?: Record<'grant' | 'except', string[]>;
|
||||
privileges: string[];
|
||||
query?: string;
|
||||
allow_restricted_indices?: boolean;
|
||||
}>;
|
||||
remote_indices?: Array<{
|
||||
clusters: string[];
|
||||
names: string[];
|
||||
field_security?: Record<'grant' | 'except', string[]>;
|
||||
privileges: string[];
|
||||
query?: string;
|
||||
allow_restricted_indices?: boolean;
|
||||
}>;
|
||||
run_as?: string[];
|
||||
}
|
||||
/**
|
||||
* Type representing Kibana specific portion of the role definition.
|
||||
*/
|
||||
export type KibanaPrivilegesType = Array<{
|
||||
spaces: string[];
|
||||
base?: string[];
|
||||
feature?: Record<string, string[]>;
|
||||
}>;
|
|
@ -14,15 +14,6 @@ export type {
|
|||
AuditLogger,
|
||||
} from './src/audit';
|
||||
export type {
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
CreateRestAPIKeyParams,
|
||||
GrantAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
CreateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
CreateCrossClusterAPIKeyParams,
|
||||
InvalidateAPIKeyResult,
|
||||
APIKeys,
|
||||
AuthenticationServiceStart,
|
||||
UpdateAPIKeyParams,
|
||||
|
@ -39,7 +30,6 @@ export type {
|
|||
CheckPrivilegesWithRequest,
|
||||
CheckSavedObjectsPrivilegesWithRequest,
|
||||
CheckPrivilegesDynamicallyWithRequest,
|
||||
KibanaPrivilegesType,
|
||||
SavedObjectActions,
|
||||
UIActions,
|
||||
CheckPrivilegesPayload,
|
||||
|
@ -51,7 +41,6 @@ export type {
|
|||
CheckPrivilegesOptions,
|
||||
CheckUserProfilesPrivilegesPayload,
|
||||
CheckUserProfilesPrivilegesResponse,
|
||||
ElasticsearchPrivilegesType,
|
||||
CasesActions,
|
||||
CheckPrivileges,
|
||||
AlertingActions,
|
||||
|
@ -72,11 +61,30 @@ export type {
|
|||
} from './src/user_profile';
|
||||
|
||||
export {
|
||||
restApiKeySchema,
|
||||
getRestApiKeyWithKibanaPrivilegesSchema,
|
||||
getUpdateRestApiKeyWithKibanaPrivilegesSchema,
|
||||
crossClusterApiKeySchema,
|
||||
updateRestApiKeySchema,
|
||||
updateCrossClusterApiKeySchema,
|
||||
} from './src/authentication';
|
||||
export { GLOBAL_RESOURCE, elasticsearchRoleSchema, getKibanaRoleSchema } from './src/authorization';
|
||||
|
||||
export type {
|
||||
ElasticsearchPrivilegesType,
|
||||
KibanaPrivilegesType,
|
||||
APIKeysService,
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
InvalidateAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
CreateRestAPIKeyParams,
|
||||
CreateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
CreateCrossClusterAPIKeyParams,
|
||||
GrantAPIKeyResult,
|
||||
} from '@kbn/core-security-server';
|
||||
export { isCreateRestAPIKeyParams } from '@kbn/core-security-server';
|
||||
|
||||
export {
|
||||
restApiKeySchema,
|
||||
crossClusterApiKeySchema,
|
||||
getRestApiKeyWithKibanaPrivilegesSchema,
|
||||
} from './src/authentication';
|
||||
export { getKibanaRoleSchema, elasticsearchRoleSchema, GLOBAL_RESOURCE } from './src/authorization';
|
||||
|
|
|
@ -5,153 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
import type { KibanaRequest } from '@kbn/core/server';
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { getKibanaRoleSchema, elasticsearchRoleSchema } from '../../authorization';
|
||||
|
||||
export interface APIKeys {
|
||||
/**
|
||||
* Determines if API Keys are enabled in Elasticsearch.
|
||||
*/
|
||||
areAPIKeysEnabled(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Determines if Cross-Cluster API Keys are enabled in Elasticsearch.
|
||||
*/
|
||||
areCrossClusterAPIKeysEnabled(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Tries to create an API key for the current user.
|
||||
*
|
||||
* Returns newly created API key or `null` if API keys are disabled.
|
||||
*
|
||||
* User needs `manage_api_key` privilege to create REST API keys and `manage_security` for Cross-Cluster API keys.
|
||||
*
|
||||
* @param request Request instance.
|
||||
* @param createParams The params to create an API key
|
||||
*/
|
||||
create(
|
||||
request: KibanaRequest,
|
||||
createParams: CreateAPIKeyParams
|
||||
): Promise<CreateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to grant an API key for the current user.
|
||||
* @param request Request instance.
|
||||
* @param createParams Create operation parameters.
|
||||
*/
|
||||
grantAsInternalUser(
|
||||
request: KibanaRequest,
|
||||
createParams: CreateRestAPIKeyParams | CreateRestAPIKeyWithKibanaPrivilegesParams
|
||||
): Promise<GrantAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to validate an API key.
|
||||
* @param apiKeyPrams ValidateAPIKeyParams.
|
||||
*/
|
||||
validate(apiKeyPrams: ValidateAPIKeyParams): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Tries to invalidate an API keys.
|
||||
* @param request Request instance.
|
||||
* @param params The params to invalidate an API keys.
|
||||
*/
|
||||
invalidate(
|
||||
request: KibanaRequest,
|
||||
params: InvalidateAPIKeysParams
|
||||
): Promise<InvalidateAPIKeyResult | null>;
|
||||
|
||||
/**
|
||||
* Tries to invalidate the API keys by using the internal user.
|
||||
* @param params The params to invalidate the API keys.
|
||||
*/
|
||||
invalidateAsInternalUser(params: InvalidateAPIKeysParams): Promise<InvalidateAPIKeyResult | null>;
|
||||
}
|
||||
|
||||
export type CreateAPIKeyParams =
|
||||
| CreateRestAPIKeyParams
|
||||
| CreateRestAPIKeyWithKibanaPrivilegesParams
|
||||
| CreateCrossClusterAPIKeyParams;
|
||||
|
||||
/**
|
||||
* Response of Kibana Create API key endpoint.
|
||||
*/
|
||||
export type CreateAPIKeyResult = estypes.SecurityCreateApiKeyResponse;
|
||||
|
||||
export type CreateRestAPIKeyParams = TypeOf<typeof restApiKeySchema>;
|
||||
export type CreateRestAPIKeyWithKibanaPrivilegesParams = TypeOf<
|
||||
ReturnType<typeof getRestApiKeyWithKibanaPrivilegesSchema>
|
||||
>;
|
||||
export type CreateCrossClusterAPIKeyParams = TypeOf<typeof crossClusterApiKeySchema>;
|
||||
|
||||
export interface GrantAPIKeyResult {
|
||||
/**
|
||||
* Unique id for this API key
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Name for this API key
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Generated API key
|
||||
*/
|
||||
api_key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the parameters for validating API Key credentials.
|
||||
*/
|
||||
export interface ValidateAPIKeyParams {
|
||||
/**
|
||||
* Unique id for this API key
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Generated API Key (secret)
|
||||
*/
|
||||
api_key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the params for invalidating multiple API keys
|
||||
*/
|
||||
export interface InvalidateAPIKeysParams {
|
||||
ids: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value when invalidating an API key in Elasticsearch.
|
||||
*/
|
||||
export interface InvalidateAPIKeyResult {
|
||||
/**
|
||||
* The IDs of the API keys that were invalidated as part of the request.
|
||||
*/
|
||||
invalidated_api_keys: string[];
|
||||
/**
|
||||
* The IDs of the API keys that were already invalidated.
|
||||
*/
|
||||
previously_invalidated_api_keys: string[];
|
||||
/**
|
||||
* The number of errors that were encountered when invalidating the API keys.
|
||||
*/
|
||||
error_count: number;
|
||||
/**
|
||||
* Details about these errors. This field is not present in the response when error_count is 0.
|
||||
*/
|
||||
error_details?: Array<{
|
||||
type?: string;
|
||||
reason?: string;
|
||||
caused_by?: {
|
||||
type?: string;
|
||||
reason?: string;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
export const restApiKeySchema = schema.object({
|
||||
type: schema.maybe(schema.literal('rest')),
|
||||
name: schema.string(),
|
||||
|
@ -165,8 +21,11 @@ export const restApiKeySchema = schema.object({
|
|||
export const getRestApiKeyWithKibanaPrivilegesSchema = (
|
||||
getBasePrivilegeNames: Parameters<typeof getKibanaRoleSchema>[0]
|
||||
) =>
|
||||
restApiKeySchema.extends({
|
||||
role_descriptors: null,
|
||||
schema.object({
|
||||
type: schema.maybe(schema.literal('rest')),
|
||||
name: schema.string(),
|
||||
expiration: schema.maybe(schema.string()),
|
||||
metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
kibana_role_descriptors: schema.recordOf(
|
||||
schema.string(),
|
||||
schema.object({
|
||||
|
@ -176,9 +35,11 @@ export const getRestApiKeyWithKibanaPrivilegesSchema = (
|
|||
),
|
||||
});
|
||||
|
||||
export const crossClusterApiKeySchema = restApiKeySchema.extends({
|
||||
export const crossClusterApiKeySchema = schema.object({
|
||||
type: schema.literal('cross_cluster'),
|
||||
role_descriptors: null,
|
||||
name: schema.string(),
|
||||
expiration: schema.maybe(schema.string()),
|
||||
metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
access: schema.object(
|
||||
{
|
||||
search: schema.maybe(
|
||||
|
@ -203,41 +64,52 @@ export const crossClusterApiKeySchema = restApiKeySchema.extends({
|
|||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* Response of Kibana Update API key endpoint.
|
||||
*/
|
||||
export type UpdateAPIKeyResult = estypes.SecurityUpdateApiKeyResponse;
|
||||
|
||||
/**
|
||||
* Request body of Kibana Update API key endpoint.
|
||||
*/
|
||||
export type UpdateAPIKeyParams =
|
||||
| UpdateRestAPIKeyParams
|
||||
| UpdateCrossClusterAPIKeyParams
|
||||
| UpdateRestAPIKeyWithKibanaPrivilegesParams;
|
||||
|
||||
export const updateRestApiKeySchema = restApiKeySchema.extends({
|
||||
name: null,
|
||||
export const updateRestApiKeySchema = schema.object({
|
||||
id: schema.string(),
|
||||
type: schema.maybe(schema.literal('rest')),
|
||||
expiration: schema.maybe(schema.string()),
|
||||
role_descriptors: schema.recordOf(schema.string(), schema.object({}, { unknowns: 'allow' }), {
|
||||
defaultValue: {},
|
||||
}),
|
||||
metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
});
|
||||
|
||||
export const updateCrossClusterApiKeySchema = crossClusterApiKeySchema.extends({
|
||||
name: null,
|
||||
export const updateCrossClusterApiKeySchema = schema.object({
|
||||
id: schema.string(),
|
||||
type: schema.literal('cross_cluster'),
|
||||
expiration: schema.maybe(schema.string()),
|
||||
metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
access: schema.object(
|
||||
{
|
||||
search: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
names: schema.arrayOf(schema.string()),
|
||||
query: schema.maybe(schema.any()),
|
||||
field_security: schema.maybe(schema.any()),
|
||||
allow_restricted_indices: schema.maybe(schema.boolean()),
|
||||
})
|
||||
)
|
||||
),
|
||||
replication: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.object({
|
||||
names: schema.arrayOf(schema.string()),
|
||||
})
|
||||
)
|
||||
),
|
||||
},
|
||||
{ unknowns: 'allow' }
|
||||
),
|
||||
});
|
||||
|
||||
export type UpdateRestAPIKeyParams = TypeOf<typeof updateRestApiKeySchema>;
|
||||
export type UpdateCrossClusterAPIKeyParams = TypeOf<typeof updateCrossClusterApiKeySchema>;
|
||||
export type UpdateRestAPIKeyWithKibanaPrivilegesParams = TypeOf<
|
||||
ReturnType<typeof getUpdateRestApiKeyWithKibanaPrivilegesSchema>
|
||||
>;
|
||||
|
||||
export const getUpdateRestApiKeyWithKibanaPrivilegesSchema = (
|
||||
getBasePrivilegeNames: Parameters<typeof getKibanaRoleSchema>[0]
|
||||
) =>
|
||||
restApiKeySchema.extends({
|
||||
role_descriptors: null,
|
||||
name: null,
|
||||
schema.object({
|
||||
type: schema.maybe(schema.literal('rest')),
|
||||
expiration: schema.maybe(schema.string()),
|
||||
metadata: schema.maybe(schema.object({}, { unknowns: 'allow' })),
|
||||
id: schema.string(),
|
||||
kibana_role_descriptors: schema.recordOf(
|
||||
schema.string(),
|
||||
|
|
|
@ -5,23 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export type {
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
InvalidateAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
CreateRestAPIKeyParams,
|
||||
CreateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
CreateCrossClusterAPIKeyParams,
|
||||
GrantAPIKeyResult,
|
||||
APIKeys,
|
||||
UpdateAPIKeyParams,
|
||||
UpdateAPIKeyResult,
|
||||
UpdateCrossClusterAPIKeyParams,
|
||||
UpdateRestAPIKeyParams,
|
||||
UpdateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
} from './api_keys';
|
||||
export {
|
||||
crossClusterApiKeySchema,
|
||||
getRestApiKeyWithKibanaPrivilegesSchema,
|
||||
|
|
|
@ -6,14 +6,13 @@
|
|||
*/
|
||||
|
||||
import type { KibanaRequest } from '@kbn/core/server';
|
||||
import type { AuthenticatedUser } from '@kbn/core-security-common';
|
||||
|
||||
import type { APIKeys } from './api_keys';
|
||||
import type { AuthenticatedUser } from '@kbn/security-plugin-types-common';
|
||||
import type { APIKeysService } from '@kbn/core-security-server';
|
||||
|
||||
/**
|
||||
* Authentication services available on the security plugin's start contract.
|
||||
*/
|
||||
export interface AuthenticationServiceStart {
|
||||
apiKeys: APIKeys;
|
||||
apiKeys: APIKeysService;
|
||||
getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null;
|
||||
}
|
||||
|
|
|
@ -5,29 +5,22 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export type { AuthenticationServiceStart } from './authentication_service';
|
||||
|
||||
export type {
|
||||
CreateAPIKeyParams,
|
||||
CreateAPIKeyResult,
|
||||
CreateRestAPIKeyParams,
|
||||
CreateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
CreateCrossClusterAPIKeyParams,
|
||||
InvalidateAPIKeyResult,
|
||||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
APIKeys,
|
||||
GrantAPIKeyResult,
|
||||
APIKeysService as APIKeys,
|
||||
UpdateAPIKeyParams,
|
||||
UpdateAPIKeyResult,
|
||||
UpdateCrossClusterAPIKeyParams,
|
||||
UpdateRestAPIKeyParams,
|
||||
UpdateRestAPIKeyWithKibanaPrivilegesParams,
|
||||
} from './api_keys';
|
||||
export type { AuthenticationServiceStart } from './authentication_service';
|
||||
} from '@kbn/core-security-server';
|
||||
|
||||
export {
|
||||
restApiKeySchema,
|
||||
crossClusterApiKeySchema,
|
||||
getRestApiKeyWithKibanaPrivilegesSchema,
|
||||
getUpdateRestApiKeyWithKibanaPrivilegesSchema,
|
||||
crossClusterApiKeySchema,
|
||||
restApiKeySchema,
|
||||
updateRestApiKeySchema,
|
||||
updateCrossClusterApiKeySchema,
|
||||
} from './api_keys';
|
||||
|
|
|
@ -42,7 +42,6 @@ export type {
|
|||
PrivilegeDeprecationsRolesByFeatureIdResponse,
|
||||
} from './deprecations';
|
||||
export type { AuthorizationMode } from './mode';
|
||||
export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './role_schema';
|
||||
|
||||
export { GLOBAL_RESOURCE } from './constants';
|
||||
export { elasticsearchRoleSchema, getKibanaRoleSchema } from './role_schema';
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/config-schema",
|
||||
"@kbn/core",
|
||||
"@kbn/security-plugin-types-common",
|
||||
"@kbn/core-user-profile-server",
|
||||
"@kbn/core-security-server",
|
||||
"@kbn/core-security-common"
|
||||
"@kbn/config-schema",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ describe('API Keys', () => {
|
|||
>;
|
||||
let mockLicense: jest.Mocked<SecurityLicense>;
|
||||
let logger: Logger;
|
||||
const roleDescriptors: { [key: string]: any } = { foo: true };
|
||||
|
||||
beforeEach(() => {
|
||||
mockValidateKibanaPrivileges.mockReset().mockReturnValue({ validationErrors: [] });
|
||||
|
@ -239,9 +240,10 @@ describe('API Keys', () => {
|
|||
});
|
||||
const result = await apiKeys.create(httpServerMock.createKibanaRequest(), {
|
||||
name: 'key-name',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
api_key: 'abc123',
|
||||
expiration: '1d',
|
||||
|
@ -253,7 +255,7 @@ describe('API Keys', () => {
|
|||
expect(mockScopedClusterClient.asCurrentUser.security.createApiKey).toHaveBeenCalledWith({
|
||||
body: {
|
||||
name: 'key-name',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
},
|
||||
});
|
||||
|
@ -343,7 +345,7 @@ describe('API Keys', () => {
|
|||
|
||||
const result = await apiKeys.update(httpServerMock.createKibanaRequest(), {
|
||||
id: 'test_id',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
metadata: {},
|
||||
});
|
||||
|
||||
|
@ -370,7 +372,7 @@ describe('API Keys', () => {
|
|||
|
||||
const result = await apiKeys.update(httpServerMock.createKibanaRequest(), {
|
||||
id: 'test_id',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
metadata: {},
|
||||
});
|
||||
|
||||
|
@ -473,7 +475,7 @@ describe('API Keys', () => {
|
|||
}),
|
||||
{
|
||||
name: 'test_api_key',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
}
|
||||
);
|
||||
|
@ -512,7 +514,7 @@ describe('API Keys', () => {
|
|||
}),
|
||||
{
|
||||
name: 'test_api_key',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
}
|
||||
);
|
||||
|
@ -527,7 +529,7 @@ describe('API Keys', () => {
|
|||
body: {
|
||||
api_key: {
|
||||
name: 'test_api_key',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
},
|
||||
grant_type: 'access_token',
|
||||
|
@ -553,7 +555,7 @@ describe('API Keys', () => {
|
|||
}),
|
||||
{
|
||||
name: 'test_api_key',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
}
|
||||
);
|
||||
|
@ -592,7 +594,7 @@ describe('API Keys', () => {
|
|||
}),
|
||||
{
|
||||
name: 'test_api_key',
|
||||
role_descriptors: { foo: true },
|
||||
role_descriptors: roleDescriptors,
|
||||
expiration: '1d',
|
||||
}
|
||||
)
|
||||
|
|
|
@ -20,6 +20,7 @@ import type {
|
|||
InvalidateAPIKeysParams,
|
||||
ValidateAPIKeyParams,
|
||||
} from '@kbn/security-plugin-types-server';
|
||||
import { isCreateRestAPIKeyParams } from '@kbn/security-plugin-types-server';
|
||||
|
||||
import { getFakeKibanaRequest } from './fake_kibana_request';
|
||||
import type { SecurityLicense } from '../../../common';
|
||||
|
@ -96,7 +97,6 @@ export class APIKeys implements APIKeysType {
|
|||
this.logger.debug(
|
||||
`Testing if API Keys are enabled by attempting to invalidate a non-existant key: ${id}`
|
||||
);
|
||||
|
||||
try {
|
||||
await this.clusterClient.asInternalUser.security.invalidateApiKey({
|
||||
body: {
|
||||
|
@ -125,7 +125,6 @@ export class APIKeys implements APIKeysType {
|
|||
this.logger.debug(
|
||||
`Testing if cross-cluster API Keys are enabled by attempting to update a non-existant key: ${id}`
|
||||
);
|
||||
|
||||
try {
|
||||
await this.clusterClient.asInternalUser.transport.request({
|
||||
method: 'PUT',
|
||||
|
@ -155,13 +154,13 @@ export class APIKeys implements APIKeysType {
|
|||
if (!this.license.isEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { type, expiration, name, metadata } = createParams;
|
||||
const scopedClusterClient = this.clusterClient.asScoped(request);
|
||||
|
||||
this.logger.debug('Trying to create an API key');
|
||||
|
||||
let result: CreateAPIKeyResult;
|
||||
|
||||
try {
|
||||
if (type === 'cross_cluster') {
|
||||
result = await scopedClusterClient.asCurrentUser.transport.request<CreateAPIKeyResult>({
|
||||
|
@ -175,13 +174,13 @@ export class APIKeys implements APIKeysType {
|
|||
name,
|
||||
expiration,
|
||||
metadata,
|
||||
role_descriptors:
|
||||
'role_descriptors' in createParams
|
||||
? createParams.role_descriptors
|
||||
: this.parseRoleDescriptorsWithKibanaPrivileges(
|
||||
createParams.kibana_role_descriptors,
|
||||
false
|
||||
),
|
||||
role_descriptors: isCreateRestAPIKeyParams(createParams)
|
||||
? createParams.role_descriptors
|
||||
: this.parseRoleDescriptorsWithKibanaPrivileges(
|
||||
createParams.kibana_role_descriptors,
|
||||
this.kibanaFeatures,
|
||||
false
|
||||
),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -234,6 +233,7 @@ export class APIKeys implements APIKeysType {
|
|||
? updateParams.role_descriptors
|
||||
: this.parseRoleDescriptorsWithKibanaPrivileges(
|
||||
updateParams.kibana_role_descriptors,
|
||||
this.kibanaFeatures,
|
||||
true
|
||||
),
|
||||
});
|
||||
|
@ -279,12 +279,12 @@ export class APIKeys implements APIKeysType {
|
|||
);
|
||||
|
||||
const { expiration, metadata, name } = createParams;
|
||||
|
||||
const roleDescriptors =
|
||||
'role_descriptors' in createParams
|
||||
? createParams.role_descriptors
|
||||
: this.parseRoleDescriptorsWithKibanaPrivileges(
|
||||
createParams.kibana_role_descriptors,
|
||||
this.kibanaFeatures,
|
||||
false
|
||||
);
|
||||
|
||||
|
@ -293,7 +293,6 @@ export class APIKeys implements APIKeysType {
|
|||
authorizationHeader,
|
||||
clientAuthorizationHeader
|
||||
);
|
||||
|
||||
// User needs `manage_api_key` or `grant_api_key` privilege to use this API
|
||||
let result: GrantAPIKeyResult;
|
||||
try {
|
||||
|
@ -318,7 +317,6 @@ export class APIKeys implements APIKeysType {
|
|||
}
|
||||
|
||||
this.logger.debug(`Trying to invalidate ${params.ids.length} an API key as current user`);
|
||||
|
||||
let result: InvalidateAPIKeyResult;
|
||||
try {
|
||||
// User needs `manage_api_key` privilege to use this API
|
||||
|
@ -354,6 +352,7 @@ export class APIKeys implements APIKeysType {
|
|||
this.logger.debug(`Trying to invalidate ${params.ids.length} API keys`);
|
||||
|
||||
let result: InvalidateAPIKeyResult;
|
||||
|
||||
try {
|
||||
// Internal user needs `cluster:admin/xpack/security/api_key/invalidate` privilege to use this API
|
||||
result = await this.clusterClient.asInternalUser.security.invalidateApiKey({
|
||||
|
@ -384,7 +383,6 @@ export class APIKeys implements APIKeysType {
|
|||
const fakeRequest = getFakeKibanaRequest(apiKeyPrams);
|
||||
|
||||
this.logger.debug(`Trying to validate an API key`);
|
||||
|
||||
try {
|
||||
await this.clusterClient.asScoped(fakeRequest).asCurrentUser.security.authenticate();
|
||||
this.logger.debug(`API key was validated successfully`);
|
||||
|
@ -445,6 +443,7 @@ export class APIKeys implements APIKeysType {
|
|||
|
||||
private parseRoleDescriptorsWithKibanaPrivileges(
|
||||
kibanaRoleDescriptors: CreateRestAPIKeyWithKibanaPrivilegesParams['kibana_role_descriptors'],
|
||||
features: KibanaFeature[],
|
||||
isEdit: boolean
|
||||
) {
|
||||
const roleDescriptors = Object.create(null);
|
||||
|
@ -452,10 +451,7 @@ export class APIKeys implements APIKeysType {
|
|||
const allValidationErrors: string[] = [];
|
||||
if (kibanaRoleDescriptors) {
|
||||
Object.entries(kibanaRoleDescriptors).forEach(([roleKey, roleDescriptor]) => {
|
||||
const { validationErrors } = validateKibanaPrivileges(
|
||||
this.kibanaFeatures,
|
||||
roleDescriptor.kibana
|
||||
);
|
||||
const { validationErrors } = validateKibanaPrivileges(features, roleDescriptor.kibana);
|
||||
allValidationErrors.push(...validationErrors);
|
||||
|
||||
const applications = transformPrivilegesToElasticsearchPrivileges(
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { apiKeysMock } from '@kbn/core-security-server-mocks';
|
||||
import type { DeeplyMockedKeys } from '@kbn/utility-types-jest';
|
||||
|
||||
import { apiKeysMock } from './api_keys/api_keys.mock';
|
||||
import type { InternalAuthenticationServiceStart } from './authentication_service';
|
||||
|
||||
export const authenticationServiceMock = {
|
||||
|
|
|
@ -348,7 +348,6 @@ export class AuthenticationService {
|
|||
applicationName,
|
||||
kibanaFeatures,
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieves server protocol name/host name/port and merges it with `xpack.security.public` config
|
||||
* to construct a server base URL (deprecated, used by the SAML provider only).
|
||||
|
|
|
@ -65,6 +65,21 @@ describe('buildSecurityApi', () => {
|
|||
expect(auditService.asScoped(request).log).toHaveBeenCalledWith({ message: 'an event' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('authc.apiKeys', () => {
|
||||
it('properly delegates to the service', async () => {
|
||||
await authc.apiKeys.areAPIKeysEnabled();
|
||||
expect(authc.apiKeys.areAPIKeysEnabled).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns the result from the service', async () => {
|
||||
authc.apiKeys.areAPIKeysEnabled.mockReturnValue(Promise.resolve(false));
|
||||
|
||||
const areAPIKeysEnabled = await authc.apiKeys.areAPIKeysEnabled();
|
||||
|
||||
expect(areAPIKeysEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildUserProfileApi', () => {
|
||||
|
|
|
@ -24,6 +24,17 @@ export const buildSecurityApi = ({
|
|||
getCurrentUser: (request) => {
|
||||
return getAuthc().getCurrentUser(request);
|
||||
},
|
||||
apiKeys: {
|
||||
areAPIKeysEnabled: () => getAuthc().apiKeys.areAPIKeysEnabled(),
|
||||
areCrossClusterAPIKeysEnabled: () => getAuthc().apiKeys.areAPIKeysEnabled(),
|
||||
grantAsInternalUser: (request, createParams) =>
|
||||
getAuthc().apiKeys.grantAsInternalUser(request, createParams),
|
||||
create: (request, createParams) => getAuthc().apiKeys.create(request, createParams),
|
||||
update: (request, updateParams) => getAuthc().apiKeys.update(request, updateParams),
|
||||
validate: (apiKeyParams) => getAuthc().apiKeys.validate(apiKeyParams),
|
||||
invalidate: (request, params) => getAuthc().apiKeys.invalidate(request, params),
|
||||
invalidateAsInternalUser: (params) => getAuthc().apiKeys.invalidateAsInternalUser(params),
|
||||
},
|
||||
},
|
||||
audit: {
|
||||
asScoped(request) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type { TransportResult } from '@elastic/elasticsearch';
|
||||
|
||||
import { securityServiceMock } from '@kbn/core-security-server-mocks';
|
||||
import { apiKeysMock, securityServiceMock } from '@kbn/core-security-server-mocks';
|
||||
|
||||
import { auditServiceMock } from './audit/mocks';
|
||||
import { authenticationServiceMock } from './authentication/authentication_service.mock';
|
||||
|
@ -19,7 +19,10 @@ function createSetupMock() {
|
|||
const mockAuthz = authorizationMock.create();
|
||||
return {
|
||||
audit: auditServiceMock.create(),
|
||||
authc: { getCurrentUser: jest.fn() },
|
||||
authc: {
|
||||
getCurrentUser: jest.fn(),
|
||||
apiKeys: apiKeysMock.create(),
|
||||
},
|
||||
authz: {
|
||||
actions: mockAuthz.actions,
|
||||
checkPrivilegesWithRequest: mockAuthz.checkPrivilegesWithRequest,
|
||||
|
|
|
@ -77,7 +77,9 @@ export interface SecurityPluginSetup extends SecurityPluginSetupWithoutDeprecate
|
|||
/**
|
||||
* @deprecated Use `authc` methods from the `SecurityServiceStart` contract instead.
|
||||
*/
|
||||
authc: { getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null };
|
||||
authc: {
|
||||
getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null;
|
||||
};
|
||||
/**
|
||||
* @deprecated Use `authz` methods from the `SecurityServiceStart` contract instead.
|
||||
*/
|
||||
|
@ -110,6 +112,7 @@ export class SecurityPlugin
|
|||
private readonly logger: Logger;
|
||||
private authorizationSetup?: AuthorizationServiceSetupInternal;
|
||||
private auditSetup?: AuditServiceSetup;
|
||||
|
||||
private configSubscription?: Subscription;
|
||||
|
||||
private config?: ConfigType;
|
||||
|
@ -189,6 +192,7 @@ export class SecurityPlugin
|
|||
this.initializerContext.logger.get('authentication')
|
||||
);
|
||||
this.auditService = new AuditService(this.initializerContext.logger.get('audit'));
|
||||
|
||||
this.elasticsearchService = new ElasticsearchService(
|
||||
this.initializerContext.logger.get('elasticsearch')
|
||||
);
|
||||
|
@ -340,7 +344,9 @@ export class SecurityPlugin
|
|||
|
||||
return Object.freeze<SecurityPluginSetup>({
|
||||
audit: this.auditSetup,
|
||||
authc: { getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request) },
|
||||
authc: {
|
||||
getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request),
|
||||
},
|
||||
authz: {
|
||||
actions: this.authorizationSetup.actions,
|
||||
checkPrivilegesWithRequest: this.authorizationSetup.checkPrivilegesWithRequest,
|
||||
|
@ -421,8 +427,8 @@ export class SecurityPlugin
|
|||
|
||||
return Object.freeze<SecurityPluginStart>({
|
||||
authc: {
|
||||
apiKeys: this.authenticationStart.apiKeys,
|
||||
getCurrentUser: this.authenticationStart.getCurrentUser,
|
||||
apiKeys: this.authenticationStart.apiKeys,
|
||||
},
|
||||
authz: {
|
||||
actions: this.authorizationSetup!.actions,
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
"@kbn/core-user-profile-browser",
|
||||
"@kbn/security-api-key-management",
|
||||
"@kbn/security-form-components",
|
||||
"@kbn/core-security-server-mocks",
|
||||
"@kbn/core-security-server-mocks"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue