diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/forbidden_api_error.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/forbidden_api_error.ts new file mode 100644 index 0000000000000..c404920bc55e9 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/forbidden_api_error.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ + +export class ForbiddenApiError extends Error { + status: number; + + constructor(message: string = 'Forbidden') { + super(message); + this.name = 'ForbiddenApiError'; + this.status = 403; + } +} diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/observability_ai_assistant_api_client.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/observability_ai_assistant_api_client.ts index 3ac941501ae7c..d8ca8661b90b4 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/observability_ai_assistant_api_client.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/common/observability_ai_assistant_api_client.ts @@ -40,7 +40,11 @@ export function getObservabilityAIAssistantApiClient({ } } -type ObservabilityAIAssistantApiClientKey = 'slsAdmin' | 'slsEditor' | 'slsUser'; +type ObservabilityAIAssistantApiClientKey = + | 'slsAdmin' + | 'slsEditor' + | 'slsUser' + | 'slsUnauthorized'; export type ObservabilityAIAssistantApiClient = Record< ObservabilityAIAssistantApiClientKey, @@ -195,18 +199,27 @@ export async function getObservabilityAIAssistantApiClientService({ const svlSharedConfig = getService('config'); const roleScopedSupertest = getService('roleScopedSupertest'); + // admin user const supertestAdminWithCookieCredentials: SupertestWithRoleScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', { useCookieHeader: true, withInternalHeaders: true, }); + // editor user const supertestEditorWithCookieCredentials: SupertestWithRoleScope = await roleScopedSupertest.getSupertestWithRoleScope('editor', { useCookieHeader: true, withInternalHeaders: true, }); + // unauthorized user + const supertestUnauthorizedWithCookieCredentials: SupertestWithRoleScope = + await roleScopedSupertest.getSupertestWithRoleScope('viewer', { + useCookieHeader: true, + withInternalHeaders: false, // No internal headers for unauthorized users + }); + return { // defaults to elastic_admin user when used without auth slsUser: await getObservabilityAIAssistantApiClient({ @@ -222,5 +235,9 @@ export async function getObservabilityAIAssistantApiClientService({ svlSharedConfig, supertestUserWithCookieCredentials: supertestEditorWithCookieCredentials, }), + slsUnauthorized: await getObservabilityAIAssistantApiClient({ + svlSharedConfig, + supertestUserWithCookieCredentials: supertestUnauthorizedWithCookieCredentials, + }), }; } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts index 2a25a309e8174..0ad911f792a7d 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/chat/chat.spec.ts @@ -16,6 +16,7 @@ import { SupertestWithRoleScope } from '@kbn/test-suites-xpack/api_integration/d import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createProxyActionConnector, deleteActionConnector } from '../../common/action_connectors'; import type { InternalRequestHeader, RoleCredentials } from '../../../../../../shared/services'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; export default function ApiTest({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); @@ -23,6 +24,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const svlCommonApi = getService('svlCommonApi'); const log = getService('log'); const roleScopedSupertest = getService('roleScopedSupertest'); + const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantAPIClient'); let supertestEditorWithCookieCredentials: SupertestWithRoleScope; @@ -170,57 +172,26 @@ export default function ApiTest({ getService }: FtrProviderContext) { ]); }); - it.skip('returns a useful error if the request fails', async () => { - const interceptor = proxy.intercept('conversation', () => true); - - const passThrough = new PassThrough(); - - supertestWithoutAuth - .post(CHAT_API_URL) - .set(roleAuthc.apiKeyHeader) - .set(internalReqHeader) - .set('kbn-xsrf', 'foo') - .send({ - name: 'my_api_call', - messages, - connectorId, - functions: [], - scopes: ['all'], - }) - .expect(200) - .pipe(passThrough); - - let data: string = ''; - - passThrough.on('data', (chunk) => { - data += chunk.toString('utf-8'); + describe('security roles and access privileges', () => { + it('should deny access for users without the ai_assistant privilege', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: `POST ${CHAT_API_URL}`, + params: { + body: { + name: 'my_api_call', + messages, + connectorId, + functions: [], + scopes: ['all'], + }, + }, + }); + throw new ForbiddenApiError('Expected slsUnauthorized() to throw a 403 Forbidden error'); + } catch (e) { + expect(e.status).to.be(403); + } }); - - const simulator = await interceptor.waitForIntercept(); - - await simulator.status(400); - - await simulator.rawWrite( - JSON.stringify({ - error: { - code: 'context_length_exceeded', - message: - "This model's maximum context length is 8192 tokens. However, your messages resulted in 11036 tokens. Please reduce the length of the messages.", - param: 'messages', - type: 'invalid_request_error', - }, - }) - ); - - await simulator.rawEnd(); - - await new Promise((resolve) => passThrough.on('end', () => resolve())); - - const response = JSON.parse(data.trim()); - - expect(response.error.message).to.be( - `Token limit reached. Token limit is 8192, but the current conversation has 11036 tokens.` - ); }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts index 5081d6b12879b..94024c78baf50 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/complete/complete.spec.ts @@ -34,6 +34,7 @@ import { } from '../conversations/helpers'; import { createProxyActionConnector, deleteActionConnector } from '../../common/action_connectors'; import type { InternalRequestHeader, RoleCredentials } from '../../../../../../shared/services'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; export default function ApiTest({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); @@ -547,5 +548,27 @@ export default function ApiTest({ getService }: FtrProviderContext) { // todo it.skip('executes a function', async () => {}); + + describe('security roles and access privileges', () => { + it('should deny access for users without the ai_assistant privilege', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'POST /internal/observability_ai_assistant/chat/complete', + params: { + body: { + messages, + connectorId, + persist: false, + screenContexts: [], + scopes: ['all'], + }, + }, + }); + throw new ForbiddenApiError('Expected slsUnauthorized() to throw a 403 Forbidden error'); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/connectors/connectors.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/connectors/connectors.spec.ts index 2096abe74e2e8..b133b2d56aa1e 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/connectors/connectors.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/connectors/connectors.spec.ts @@ -13,6 +13,9 @@ import type { RoleCredentials, SupertestWithoutAuthProviderType, } from '../../../../../../shared/services'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; + +const CONNECTOR_API_URL = '/internal/observability_ai_assistant/connectors'; export default function ApiTest({ getService }: FtrProviderContext) { const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantAPIClient'); @@ -47,14 +50,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('Returns a 2xx for enterprise license', async () => { await observabilityAIAssistantAPIClient .slsEditor({ - endpoint: 'GET /internal/observability_ai_assistant/connectors', + endpoint: `GET ${CONNECTOR_API_URL}`, }) .expect(200); }); it('returns an empty list of connectors', async () => { const res = await observabilityAIAssistantAPIClient.slsEditor({ - endpoint: 'GET /internal/observability_ai_assistant/connectors', + endpoint: `GET ${CONNECTOR_API_URL}`, }); expect(res.body.length).to.be(0); @@ -70,7 +73,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); const res = await observabilityAIAssistantAPIClient.slsEditor({ - endpoint: 'GET /internal/observability_ai_assistant/connectors', + endpoint: `GET ${CONNECTOR_API_URL}`, }); expect(res.body.length).to.be(1); @@ -83,6 +86,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { roleAuthc, }); }); + + describe('security roles and access privileges', () => { + it('should deny access for users without the ai_assistant privilege', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: `GET ${CONNECTOR_API_URL}`, + }); + throw new ForbiddenApiError('Expected slsUnauthorized() to throw a 403 Forbidden error'); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/conversations/conversations.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/conversations/conversations.spec.ts index 7033e0660f5c6..e4aa90993bd3b 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/conversations/conversations.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/conversations/conversations.spec.ts @@ -14,6 +14,7 @@ import { } from '@kbn/observability-ai-assistant-plugin/common/types'; import type { FtrProviderContext } from '../../common/ftr_provider_context'; import type { SupertestReturnType } from '../../common/observability_ai_assistant_api_client'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; export default function ApiTest({ getService }: FtrProviderContext) { const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantAPIClient'); @@ -253,5 +254,128 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); }); + + describe('security roles and access privileges', () => { + describe('should deny access for users without the ai_assistant privilege', () => { + let createResponse: Awaited< + SupertestReturnType<'POST /internal/observability_ai_assistant/conversation'> + >; + before(async () => { + createResponse = await observabilityAIAssistantAPIClient + .slsEditor({ + endpoint: 'POST /internal/observability_ai_assistant/conversation', + params: { + body: { + conversation: conversationCreate, + }, + }, + }) + .expect(200); + }); + + after(async () => { + await observabilityAIAssistantAPIClient + .slsEditor({ + endpoint: 'DELETE /internal/observability_ai_assistant/conversation/{conversationId}', + params: { + path: { + conversationId: createResponse.body.conversation.id, + }, + }, + }) + .expect(200); + }); + + it('POST /internal/observability_ai_assistant/conversation', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'POST /internal/observability_ai_assistant/conversation', + params: { + body: { + conversation: conversationCreate, + }, + }, + }); + // throw new ForbiddenApiError( + // 'Expected slsUnauthorized() to throw a 403 Forbidden error' + // ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('POST /internal/observability_ai_assistant/conversations', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'POST /internal/observability_ai_assistant/conversations', + }); + // throw new ForbiddenApiError( + // 'Expected slsUnauthorized() to throw a 403 Forbidden error' + // ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('PUT /internal/observability_ai_assistant/conversation/{conversationId}', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'PUT /internal/observability_ai_assistant/conversation/{conversationId}', + params: { + path: { + conversationId: createResponse.body.conversation.id, + }, + body: { + conversation: merge(omit(conversationUpdate, 'conversation.id'), { + conversation: { id: createResponse.body.conversation.id }, + }), + }, + }, + }); + // throw new ForbiddenApiError( + // 'Expected slsUnauthorized() to throw a 403 Forbidden error' + // ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('GET /internal/observability_ai_assistant/conversation/{conversationId}', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'GET /internal/observability_ai_assistant/conversation/{conversationId}', + params: { + path: { + conversationId: createResponse.body.conversation.id, + }, + }, + }); + // throw new ForbiddenApiError( + // 'Expected slsUnauthorized() to throw a 403 Forbidden error' + // ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('DELETE /internal/observability_ai_assistant/conversation/{conversationId}', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'DELETE /internal/observability_ai_assistant/conversation/{conversationId}', + params: { + path: { + conversationId: createResponse.body.conversation.id, + }, + }, + }); + throw new ForbiddenApiError( + 'Expected slsUnauthorized() to throw a 403 Forbidden error' + ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base.spec.ts index f156ba7e583b5..8f29716ae3eda 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base.spec.ts @@ -15,6 +15,7 @@ import { } from '@kbn/test-suites-xpack/observability_ai_assistant_api_integration/tests/knowledge_base/helpers'; import { type KnowledgeBaseEntry } from '@kbn/observability-ai-assistant-plugin/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; export default function ApiTest({ getService }: FtrProviderContext) { const ml = getService('ml'); @@ -212,6 +213,62 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(entries[0].title).to.eql('My title b'); }); }); + + describe('security roles and access privileges', () => { + describe('should deny access for users without the ai_assistant privilege', () => { + it('POST /internal/observability_ai_assistant/kb/entries/save', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'POST /internal/observability_ai_assistant/kb/entries/save', + params: { + body: { + id: 'my-doc-id-1', + title: 'My title', + text: 'My content', + }, + }, + }); + throw new ForbiddenApiError( + 'Expected unauthorizedUser() to throw a 403 Forbidden error' + ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('GET /internal/observability_ai_assistant/kb/entries', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'GET /internal/observability_ai_assistant/kb/entries', + params: { + query: { query: '', sortBy: 'title', sortDirection: 'asc' }, + }, + }); + throw new ForbiddenApiError( + 'Expected slsUnauthorized() to throw a 403 Forbidden error' + ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('DELETE /internal/observability_ai_assistant/kb/entries/{entryId}', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'DELETE /internal/observability_ai_assistant/kb/entries/{entryId}', + params: { + path: { entryId: 'my-doc-id-1' }, + }, + }); + throw new ForbiddenApiError( + 'Expected slsUnauthorized() to throw a 403 Forbidden error' + ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts index 87ceec18f1985..1899dbbb5daf1 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts @@ -14,6 +14,9 @@ import { } from '@kbn/test-suites-xpack/observability_ai_assistant_api_integration/tests/knowledge_base/helpers'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; + +export const KNOWLEDGE_BASE_SETUP_API_URL = '/internal/observability_ai_assistant/kb/setup'; export default function ApiTest({ getService }: FtrProviderContext) { const ml = getService('ml'); @@ -33,7 +36,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { await createKnowledgeBaseModel(ml); const res = await observabilityAIAssistantAPIClient .slsAdmin({ - endpoint: 'POST /internal/observability_ai_assistant/kb/setup', + endpoint: `POST ${KNOWLEDGE_BASE_SETUP_API_URL}`, params: { query: { model_id: TINY_ELSER.id, @@ -52,7 +55,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns bad request if model cannot be installed', async () => { const res = await observabilityAIAssistantAPIClient .slsAdmin({ - endpoint: 'POST /internal/observability_ai_assistant/kb/setup', + endpoint: `POST ${KNOWLEDGE_BASE_SETUP_API_URL}`, params: { query: { model_id: TINY_ELSER.id, @@ -66,5 +69,23 @@ export default function ApiTest({ getService }: FtrProviderContext) { 'No known trained model with model_id [pt_tiny_elser]' ); }); + + describe('security roles and access privileges', () => { + it('should deny access for users without the ai_assistant privilege', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: `POST ${KNOWLEDGE_BASE_SETUP_API_URL}`, + params: { + query: { + model_id: TINY_ELSER.id, + }, + }, + }); + throw new ForbiddenApiError('Expected slsUnauthorized() to throw a 403 Forbidden error'); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_status.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_status.spec.ts index 207badc1b855a..3058f90823546 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_status.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_status.spec.ts @@ -14,6 +14,10 @@ import { } from '@kbn/test-suites-xpack/observability_ai_assistant_api_integration/tests/knowledge_base/helpers'; import { AI_ASSISTANT_KB_INFERENCE_ID } from '@kbn/observability-ai-assistant-plugin/server/service/inference_endpoint'; import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; +import { KNOWLEDGE_BASE_SETUP_API_URL } from './knowledge_base_setup.spec'; + +const KNOWLEDGE_BASE_STATUS_API_URL = '/internal/observability_ai_assistant/kb/status'; export default function ApiTest({ getService }: FtrProviderContext) { const ml = getService('ml'); @@ -27,7 +31,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { await createKnowledgeBaseModel(ml); await observabilityAIAssistantAPIClient .slsAdmin({ - endpoint: 'POST /internal/observability_ai_assistant/kb/setup', + endpoint: `POST ${KNOWLEDGE_BASE_SETUP_API_URL}`, params: { query: { model_id: TINY_ELSER.id, @@ -45,7 +49,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns correct status after knowledge base is setup', async () => { const res = await observabilityAIAssistantAPIClient .slsEditor({ - endpoint: 'GET /internal/observability_ai_assistant/kb/status', + endpoint: `GET ${KNOWLEDGE_BASE_STATUS_API_URL}`, }) .expect(200); @@ -59,12 +63,25 @@ export default function ApiTest({ getService }: FtrProviderContext) { const res = await observabilityAIAssistantAPIClient .slsEditor({ - endpoint: 'GET /internal/observability_ai_assistant/kb/status', + endpoint: `GET ${KNOWLEDGE_BASE_STATUS_API_URL}`, }) .expect(200); expect(res.body.enabled).to.be(true); expect(res.body.ready).to.be(false); }); + + describe('security roles and access privileges', () => { + it('should deny access for users without the ai_assistant privilege', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: `GET ${KNOWLEDGE_BASE_STATUS_API_URL}`, + }); + throw new ForbiddenApiError('Expected unauthorizedUser() to throw a 403 Forbidden error'); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts index 0557d43830bc0..00f53e356a345 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_user_instructions.spec.ts @@ -24,6 +24,7 @@ import { import { createProxyActionConnector, deleteActionConnector } from '../../common/action_connectors'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import type { InternalRequestHeader, RoleCredentials } from '../../../../../../shared/services'; +import { ForbiddenApiError } from '../../common/forbidden_api_error'; export default function ApiTest({ getService }: FtrProviderContext) { const observabilityAIAssistantAPIClient = getService('observabilityAIAssistantAPIClient'); @@ -329,5 +330,42 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(conversation.messages.length).to.be(5); }); }); + + describe('security roles and access privileges', () => { + describe('should deny access for users without the ai_assistant privilege', () => { + it('PUT /internal/observability_ai_assistant/kb/user_instructions', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'PUT /internal/observability_ai_assistant/kb/user_instructions', + params: { + body: { + id: 'test-instruction', + text: 'Test user instruction', + public: true, + }, + }, + }); + throw new ForbiddenApiError( + 'Expected slsUnauthorized() to throw a 403 Forbidden error' + ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + + it('GET /internal/observability_ai_assistant/kb/user_instructions', async () => { + try { + await observabilityAIAssistantAPIClient.slsUnauthorized({ + endpoint: 'GET /internal/observability_ai_assistant/kb/user_instructions', + }); + throw new ForbiddenApiError( + 'Expected slsUnauthorized() to throw a 403 Forbidden error' + ); + } catch (e) { + expect(e.status).to.be(403); + } + }); + }); + }); }); }