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

[Security in Core] Exposes apiKeys from core.security.authc #186910

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
083b1b4
[Security-in-Core]initial work for exposing api keys service in core
SiddharthMantri Jun 25, 2024
25f0509
move public security types to core-security-common
SiddharthMantri Jun 25, 2024
3c0f0c8
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 25, 2024
eb130eb
remove unused types
SiddharthMantri Jun 25, 2024
99ba029
update jest test for api keys
SiddharthMantri Jun 26, 2024
73dc2f0
remove cyclical ts deps, update tsdocs partial
SiddharthMantri Jun 26, 2024
0b13f8d
update security plugin unit test
SiddharthMantri Jun 26, 2024
d021ddd
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 26, 2024
1987f8c
fix cyclical dependency on kibana/core
SiddharthMantri Jun 26, 2024
1042962
fix formatting
SiddharthMantri Jun 26, 2024
7892ff2
update delegate api test
SiddharthMantri Jun 26, 2024
471f939
simplify api key type exports
SiddharthMantri Jun 26, 2024
5ac332d
update comments for public ts docs
SiddharthMantri Jun 26, 2024
0dfc26e
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 26, 2024
10a38b6
fix types
SiddharthMantri Jun 26, 2024
b83a4c4
Merge branch 'main' into security-in-core-api-keys
kibanamachine Jun 26, 2024
fc8af4d
remove unneeded comma
SiddharthMantri Jun 26, 2024
f8d8fae
remove unneeded comma
SiddharthMantri Jun 26, 2024
114bfc1
consolidate exported types
SiddharthMantri Jun 26, 2024
219d8f6
fix eslint type issue
SiddharthMantri Jun 26, 2024
381316a
properly await apiKeysEnabled
SiddharthMantri Jun 26, 2024
fa37f7d
move changes to core-security-server, expose update in public schema
SiddharthMantri Jun 27, 2024
2b6438e
update imports
SiddharthMantri Jun 27, 2024
a8c7795
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 27, 2024
9a21a9a
update mocks
SiddharthMantri Jun 27, 2024
11844e5
fix types
SiddharthMantri Jun 27, 2024
43c8a07
revert formatting changes for tsconfig
SiddharthMantri Jun 27, 2024
850a22a
update mocks for update fn
SiddharthMantri Jun 27, 2024
1932f53
remove unused kbn reference
SiddharthMantri Jun 27, 2024
ec18b5b
remove empty comment
SiddharthMantri Jun 27, 2024
7aa2dd3
fix import name
SiddharthMantri Jun 27, 2024
5c12825
update mocks
SiddharthMantri Jun 27, 2024
d04f88b
update default implementation to be stub
SiddharthMantri Jun 27, 2024
fb9a78f
create explicit decoupled public types, move schema back to plugin
SiddharthMantri Jun 27, 2024
54952ea
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 27, 2024
50c100f
remove typeguards
SiddharthMantri Jun 27, 2024
6b1235e
fix types, update context to expose only contract we need
SiddharthMantri Jun 28, 2024
087db06
add reject when api disabled
SiddharthMantri Jun 28, 2024
06d897d
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 28, 2024
b079606
Update api_keys_context.ts
SiddharthMantri Jun 28, 2024
51b5177
Update api_keys.ts
SiddharthMantri Jun 28, 2024
7b2c26f
revert changes to api keys class constructor
SiddharthMantri Jun 28, 2024
caddc90
update type guard to use correct key
SiddharthMantri Jun 28, 2024
2f1915e
Merge branch 'main' into security-in-core-api-keys
SiddharthMantri Jun 28, 2024
7435d87
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Jun 28, 2024
8fb77d3
Merge branch 'main' into security-in-core-api-keys
kibanamachine Jun 29, 2024
df6e945
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jun 29, 2024
1faeaa4
use arrow functions instead of bind
SiddharthMantri Jul 2, 2024
6c85ea3
Merge branch 'main' into security-in-core-api-keys
SiddharthMantri Jul 4, 2024
b65a666
Merge branch 'main' into security-in-core-api-keys
elasticmachine Jul 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/core/security/core-security-common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,24 @@ export type {
AuthenticatedUser,
AuthenticationProvider,
} from './src/authentication';

export type {
APIKeysService,
CreateAPIKeyParams,
CreateAPIKeyResult,
InvalidateAPIKeyResult,
InvalidateAPIKeysParams,
ValidateAPIKeyParams,
CreateRestAPIKeyParams,
CreateRestAPIKeyWithKibanaPrivilegesParams,
CreateCrossClusterAPIKeyParams,
GrantAPIKeyResult,
} from './src/api_keys';
export {
restApiKeySchema,
crossClusterApiKeySchema,
getRestApiKeyWithKibanaPrivilegesSchema,
} from './src/api_keys';

export { elasticsearchRoleSchema, getKibanaRoleSchema, GLOBAL_RESOURCE } from './src/roles';
export type { KibanaPrivilegesType, ElasticsearchPrivilegesType } from './src/roles';
SiddharthMantri marked this conversation as resolved.
Show resolved Hide resolved
214 changes: 214 additions & 0 deletions packages/core/security/core-security-common/src/api_keys/api_keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* 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 { schema, TypeOf } from '@kbn/config-schema';
import { getKibanaRoleSchema, elasticsearchRoleSchema } 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>;

/**
* 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 {
/**
* 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;
};
}>;
}

export const restApiKeySchema = schema.object({
type: schema.maybe(schema.literal('rest')),
name: schema.string(),
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 getRestApiKeyWithKibanaPrivilegesSchema = (
getBasePrivilegeNames: Parameters<typeof getKibanaRoleSchema>[0]
) =>
restApiKeySchema.extends({
role_descriptors: null,
kibana_role_descriptors: schema.recordOf(
schema.string(),
schema.object({
elasticsearch: elasticsearchRoleSchema.extends({}, { unknowns: 'allow' }),
kibana: getKibanaRoleSchema(getBasePrivilegeNames),
})
),
});

export const crossClusterApiKeySchema = restApiKeySchema.extends({
type: schema.literal('cross_cluster'),
role_descriptors: null,
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' }
),
});
25 changes: 25 additions & 0 deletions packages/core/security/core-security-common/src/api_keys/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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,
} from './api_keys';
export {
restApiKeySchema,
crossClusterApiKeySchema,
getRestApiKeyWithKibanaPrivilegesSchema,
} from './api_keys';
10 changes: 10 additions & 0 deletions packages/core/security/core-security-common/src/roles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 { getKibanaRoleSchema, elasticsearchRoleSchema, GLOBAL_RESOURCE } from './schema';
export type { ElasticsearchPrivilegesType, KibanaPrivilegesType } from './schema';
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/*
SiddharthMantri marked this conversation as resolved.
Show resolved Hide resolved
* 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 _ from 'lodash';

import type { TypeOf } from '@kbn/config-schema';
import { schema } from '@kbn/config-schema';

import { GLOBAL_RESOURCE } from './constants';

export const GLOBAL_RESOURCE = '*';
/**
* Elasticsearch specific portion of the role definition.
* See more details at https://www.elastic.co/guide/en/elasticsearch/reference/master/security-api.html#security-role-apis.
Expand Down
17 changes: 4 additions & 13 deletions packages/core/security/core-security-common/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,9 @@
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
"types": ["jest", "node", "react"]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": []
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["target/**/*"],
"kbn_references": ["@kbn/config-schema"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class CoreSecurityRouteHandlerContext implements SecurityRequestHandlerCo
if (this.#authc == null) {
this.#authc = {
getCurrentUser: () => this.securityStart.authc.getCurrentUser(this.request),
apiKeys: this.securityStart.authc.apiKeys,
};
}
return this.#authc;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ 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(),
},
},
audit: {
asScoped: jest.fn().mockReturnValue(createAuditLoggerMock.create()),
Expand All @@ -23,6 +32,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);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ describe('getDefaultSecurityImplementation', () => {
});
});

describe('authc.apiKeys', () => {
it('returns null', async () => {
const { apiKeys } = implementation.authc;
expect(apiKeys).toBeNull();
});
});

describe('audit.asScoped', () => {
it('returns null', async () => {
const logger = implementation.audit.asScoped({} as any);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const getDefaultSecurityImplementation = (): CoreSecurityDelegateContract
return {
authc: {
getCurrentUser: () => null,
apiKeys: null,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does default implementation have to be null? 🤔 When will apiKeys service be null?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

But to answer the question: we have integration test suites where we boot core in oss mode (so we don't load the xpack plugins). This is only for those test/dev scenario that we had to add this stubbed "default implementation", for this very reason of avoiding having nullable/?-able APIs,

},
audit: {
asScoped: () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 const apiKeysServiceMock = {
areAPIKeysEnabled: jest.fn(),
areCrossClusterAPIKeysEnabled: jest.fn(),
validate: jest.fn(),
invalidate: jest.fn(),
invalidateAsInternalUser: jest.fn(),
grantAsInternalUser: jest.fn(),
create: jest.fn(),
};
Loading