import { RulesClient , ConstructorOptions } from '../rules_client' ; import { savedObjectsClientMock , loggingSystemMock } from '../../../../../../src/core/server/mocks' ; import { taskManagerMock } from '../../../../task_manager/server/mocks' ; import { ruleTypeRegistryMock } from '../../rule_type_registry.mock' ; import { alertingAuthorizationMock } from '../../authorization/alerting_authorization.mock' ; import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks' ; import { actionsAuthorizationMock } from '../../../../actions/server/mocks' ; import { AlertingAuthorization } from '../../authorization/alerting_authorization' ; import { ActionsAuthorization } from '../../../../actions/server' ; import { httpServerMock } from '../../../../../../src/core/server/mocks' ; import { auditServiceMock } from '../../../../security/server/audit/index.mock' ; import { getBeforeSetup } from './lib' ; const taskManager = taskManagerMock . createStart ( ) ; const ruleTypeRegistry = ruleTypeRegistryMock . create ( ) ; const unsecuredSavedObjectsClient = savedObjectsClientMock . create ( ) ; const encryptedSavedObjects = encryptedSavedObjectsMock . createClient ( ) ; const authorization = alertingAuthorizationMock . create ( ) ; const actionsAuthorization = actionsAuthorizationMock . create ( ) ; const auditLogger = auditServiceMock . create ( ) . asScoped ( httpServerMock . createKibanaRequest ( ) ) ; const kibanaVersion = 'v7.10.0' ; const rulesClientParams = { taskManager , ruleTypeRegistry , unsecuredSavedObjectsClient , authorization : authorization as unknown as AlertingAuthorization , actionsAuthorization : actionsAuthorization as unknown as ActionsAuthorization , spaceId : 'default' , namespace : 'default' , getUserName : jest . fn ( ) , createAPIKey : jest . fn ( ) , logger : loggingSystemMock . create ( ) . get ( ) , encryptedSavedObjectsClient : encryptedSavedObjects , getActionsClient : jest . fn ( ) , getEventLogClient : jest . fn ( ) , kibanaVersion , auditLogger , } ; beforeEach ( ( ) => { getBeforeSetup ( rulesClientParams , taskManager , ruleTypeRegistry ) ; ( auditLogger . log as jest . Mock ) . mockClear ( ) ; } ) ; describe ( 'delete()' , ( ) => { let rulesClient ; const existingAlert = { id : '1' , type : 'alert' , attributes : { alertTypeId : 'myType' , consumer : 'myApp' , schedule : { interval : '10s' } , params : { bar : true , } , scheduledTaskId : 'task-123' , actions : [ { group : 'default' , actionTypeId : '.no-op' , actionRef : 'action_0' , params : { foo : true , } , } , ] , } , references : [ { name : 'action_0' , type : 'action' , id : '1' , } , ] , } ; const existingDecryptedAlert = { ... existingAlert , attributes : { ... existingAlert . attributes , apiKey : Buffer . from ( '123:abc' ) . toString ( 'base64' ) , } , } ; beforeEach ( ( ) => { rulesClient = new RulesClient ( rulesClientParams ) ; unsecuredSavedObjectsClient . get . mockResolvedValue ( existingAlert ) ; unsecuredSavedObjectsClient . delete . mockResolvedValue ( { success : true , } ) ; encryptedSavedObjects . getDecryptedAsInternalUser . mockResolvedValue ( existingDecryptedAlert ) ; } ) ; test ( 'successfully removes an alert' , async ( ) => { unsecuredSavedObjectsClient . create . mockResolvedValueOnce ( { id : '1' , type : 'api_key_pending_invalidation' , attributes : { apiKeyId : '123' , createdAt : '2019-02-12T21:01:22.479Z' , } , references : [ ] , } ) ; const result = await rulesClient . delete ( { id : '1' } ) ; expect ( result ) . toEqual ( { success : true } ) ; expect ( unsecuredSavedObjectsClient . delete ) . toHaveBeenCalledWith ( 'alert' , '1' ) ; expect ( taskManager . removeIfExists ) . toHaveBeenCalledWith ( 'task-123' ) ; expect ( unsecuredSavedObjectsClient . create . mock . calls [ 0 ] [ 0 ] ) . toBe ( 'api_key_pending_invalidation' ) ; expect ( encryptedSavedObjects . getDecryptedAsInternalUser ) . toHaveBeenCalledWith ( 'alert' , '1' , { namespace : 'default' , } ) ; expect ( unsecuredSavedObjectsClient . get ) . not . toHaveBeenCalled ( ) ; } ) ; test ( 'falls back to SOC.get when getDecryptedAsInternalUser throws an error' , async ( ) => { encryptedSavedObjects . getDecryptedAsInternalUser . mockRejectedValue ( new Error ( 'Fail' ) ) ; unsecuredSavedObjectsClient . create . mockResolvedValueOnce ( { id : '1' , type : 'api_key_pending_invalidation' , attributes : { apiKeyId : '123' , createdAt : '2019-02-12T21:01:22.479Z' , } , references : [ ] , } ) ; const result = await rulesClient . delete ( { id : '1' } ) ; expect ( result ) . toEqual ( { success : true } ) ; expect ( unsecuredSavedObjectsClient . delete ) . toHaveBeenCalledWith ( 'alert' , '1' ) ; expect ( taskManager . removeIfExists ) . toHaveBeenCalledWith ( 'task-123' ) ; expect ( unsecuredSavedObjectsClient . create ) . not . toHaveBeenCalled ( ) ; expect ( unsecuredSavedObjectsClient . get ) . toHaveBeenCalledWith ( 'alert' , '1' ) ; expect ( rulesClientParams . logger . error ) . toHaveBeenCalledWith ( 'delete(): Failed to load API key to invalidate on alert 1: Fail' ) ; } ) ; test ( `doesn't remove a task when scheduledTaskId is null` , async ( ) => { encryptedSavedObjects . getDecryptedAsInternalUser . mockResolvedValue ( { ... existingDecryptedAlert , attributes : { ... existingDecryptedAlert . attributes , scheduledTaskId : null , } , } ) ; await rulesClient . delete ( { id : '1' } ) ; expect ( taskManager . removeIfExists ) . not . toHaveBeenCalled ( ) ; } ) ; test ( `doesn't invalidate API key when apiKey is null` , async ( ) => { unsecuredSavedObjectsClient . create . mockResolvedValueOnce ( { id : '1' , type : 'api_key_pending_invalidation' , attributes : { apiKeyId : '123' , createdAt : '2019-02-12T21:01:22.479Z' , } , references : [ ] , } ) ; encryptedSavedObjects . getDecryptedAsInternalUser . mockResolvedValue ( { ... existingAlert , attributes : { ... existingAlert . attributes , apiKey : null , } , } ) ; await rulesClient . delete ( { id : '1' } ) ; expect ( unsecuredSavedObjectsClient . create ) . not . toHaveBeenCalled ( ) ; } ) ; test ( 'swallows error when invalidate API key throws' , async ( ) => { unsecuredSavedObjectsClient . create . mockRejectedValueOnce ( new Error ( 'Fail' ) ) ; await rulesClient . delete ( { id : '1' } ) ; expect ( unsecuredSavedObjectsClient . create . mock . calls [ 0 ] [ 0 ] ) . toBe ( 'api_key_pending_invalidation' ) ; expect ( rulesClientParams . logger . error ) . toHaveBeenCalledWith ( 'Failed to mark for API key [id="MTIzOmFiYw=="] for invalidation: Fail' ) ; } ) ; test ( 'swallows error when getDecryptedAsInternalUser throws an error' , async ( ) => { unsecuredSavedObjectsClient . create . mockResolvedValueOnce ( { id : '1' , type : 'api_key_pending_invalidation' , attributes : { apiKeyId : '123' , createdAt : '2019-02-12T21:01:22.479Z' , } , references : [ ] , } ) ; encryptedSavedObjects . getDecryptedAsInternalUser . mockRejectedValue ( new Error ( 'Fail' ) ) ; await rulesClient . delete ( { id : '1' } ) ; expect ( unsecuredSavedObjectsClient . create ) . not . toHaveBeenCalled ( ) ; expect ( rulesClientParams . logger . error ) . toHaveBeenCalledWith ( 'delete(): Failed to load API key to invalidate on alert 1: Fail' ) ; } ) ; test ( 'throws error when unsecuredSavedObjectsClient.get throws an error' , async ( ) => { encryptedSavedObjects . getDecryptedAsInternalUser . mockRejectedValue ( new Error ( 'Fail' ) ) ; unsecuredSavedObjectsClient . get . mockRejectedValue ( new Error ( 'SOC Fail' ) ) ; await expect ( rulesClient . delete ( { id : '1' } ) ) . rejects . toThrowErrorMatchingInlineSnapshot ( `"SOC Fail"` ) ; } ) ; test ( 'throws error when taskManager.removeIfExists throws an error' , async ( ) => { taskManager . removeIfExists . mockRejectedValue ( new Error ( 'TM Fail' ) ) ; await expect ( rulesClient . delete ( { id : '1' } ) ) . rejects . toThrowErrorMatchingInlineSnapshot ( `"TM Fail"` ) ; } ) ; describe ( 'authorization' , ( ) => { test ( 'ensures user is authorised to delete this type of alert under the consumer' , async ( ) => { await rulesClient . delete ( { id : '1' } ) ; expect ( authorization . ensureAuthorized ) . toHaveBeenCalledWith ( { entity : 'rule' , consumer : 'myApp' , operation : 'delete' , ruleTypeId : 'myType' , } ) ; } ) ; test ( 'throws when user is not authorised to delete this type of alert' , async ( ) => { authorization . ensureAuthorized . mockRejectedValue ( new Error ( `Unauthorized to delete a "myType" alert for "myApp"` ) ) ; await expect ( rulesClient . delete ( { id : '1' } ) ) . rejects . toMatchInlineSnapshot ( `[Error: Unauthorized to delete a "myType" alert for "myApp"]` ) ; expect ( authorization . ensureAuthorized ) . toHaveBeenCalledWith ( { entity : 'rule' , consumer : 'myApp' , operation : 'delete' , ruleTypeId : 'myType' , } ) ; } ) ; } ) ; describe ( 'auditLogger' , ( ) => { test ( 'logs audit event when deleting a rule' , async ( ) => { await rulesClient . delete ( { id : '1' } ) ; expect ( auditLogger . log ) . toHaveBeenCalledWith ( expect . objectContaining ( { event : expect . objectContaining ( { action : 'rule_delete' , outcome : 'unknown' , } ) , kibana : { saved_object : { id : '1' , type : 'alert' } } , } ) ) ; } ) ; test ( 'logs audit event when not authorised to delete a rule' , async ( ) => { authorization . ensureAuthorized . mockRejectedValue ( new Error ( 'Unauthorized' ) ) ; await expect ( rulesClient . delete ( { id : '1' } ) ) . rejects . toThrow ( ) ; expect ( auditLogger . log ) . toHaveBeenCalledWith ( expect . objectContaining ( { event : expect . objectContaining ( { action : 'rule_delete' , outcome : 'failure' , } ) , kibana : { saved_object : { id : '1' , type : 'alert' , } , } , error : { code : 'Error' , message : 'Unauthorized' , } , } ) ) ; } ) ; } ) ; } ) ;