All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.airbus_cyber_security.graylog.wizard.alert.rest.AlertRuleResource Maven / Gradle / Ivy

There is a newer version: 6.1.1
Show newest version
/*
 * Copyright (C) 2018 Airbus CyberSecurity (SAS)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * .
 */

// TODO should rename package rest into resources
package com.airbus_cyber_security.graylog.wizard.alert.rest;

import com.airbus_cyber_security.graylog.events.notifications.types.LoggingNotificationConfig;
import com.airbus_cyber_security.graylog.wizard.alert.business.*;
import com.airbus_cyber_security.graylog.wizard.alert.business.AlertRuleService;
import com.airbus_cyber_security.graylog.wizard.alert.model.*;
import com.airbus_cyber_security.graylog.wizard.alert.rest.models.AlertRuleStream;
import com.airbus_cyber_security.graylog.wizard.alert.model.FieldRule;
import com.airbus_cyber_security.graylog.wizard.alert.rest.models.requests.AlertRuleRequest;
import com.airbus_cyber_security.graylog.wizard.alert.model.AlertType;
import com.airbus_cyber_security.graylog.wizard.alert.rest.models.responses.GetDataAlertRule;
import com.airbus_cyber_security.graylog.wizard.audit.AlertWizardAuditEventTypes;
import com.airbus_cyber_security.graylog.wizard.config.rest.AlertWizardConfig;
import com.airbus_cyber_security.graylog.wizard.config.rest.AlertWizardConfigurationService;
import com.airbus_cyber_security.graylog.wizard.config.rest.ImportPolicyType;
import com.airbus_cyber_security.graylog.wizard.list.utilities.AlertListUtilsService;
import com.airbus_cyber_security.graylog.wizard.permissions.AlertRuleRestPermissions;
import com.codahale.metrics.annotation.Timed;
import com.mongodb.MongoException;
import io.swagger.annotations.*;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.graylog.events.notifications.NotificationDto;
import org.graylog.events.processor.EventDefinitionDto;
import org.graylog.events.processor.EventProcessorConfig;
import org.graylog.events.rest.EventNotificationsResource;
import org.graylog.plugins.pipelineprocessor.db.*;
import org.graylog.security.UserContext;
import org.graylog2.audit.jersey.AuditEvent;
import org.graylog2.database.NotFoundException;
import org.graylog2.events.ClusterEventBus;
import org.graylog2.plugin.database.ValidationException;
import org.graylog2.plugin.rest.PluginRestResource;
import org.graylog2.plugin.streams.Stream;
import org.graylog2.shared.rest.resources.RestResource;
import org.graylog2.streams.StreamService;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.UnsupportedEncodingException;
import java.util.*;

@Api(value = "Wizard/Alerts", description = "Management of Wizard alerts rules.")
@Path("/alerts")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class AlertRuleResource extends RestResource implements PluginRestResource {
    private static final Logger LOG = LoggerFactory.getLogger(AlertRuleResource.class);

    private static final String ENCODING = "UTF-8";
    private static final String TITLE = "title";

    private final StreamService streamService;
    private final ClusterEventBus clusterEventBus;
    // TODO try to remove this field => move it down in business
    private final AlertWizardConfigurationService configurationService;

    // TODO try to remove this field => Use AlertRuleUtilsService
    private final EventNotificationsResource eventNotificationsResource;
    private final EventDefinitionService eventDefinitionService;

    private final AlertRuleService alertRuleService;
    private final Conversions conversions;
    private final StreamPipelineService streamPipelineService;
    private final AlertListUtilsService alertListUtilsService;
    private final NotificationService notificationService;
    private final FieldRulesUtilities fieldRulesUtilities;

    @Inject
    public AlertRuleResource(AlertRuleService alertRuleService,
                             StreamService streamService,
                             StreamPipelineService streamPipelineService,
                             ClusterEventBus clusterEventBus,
                             AlertWizardConfigurationService configurationService,
                             AlertListUtilsService alertListUtilsService,
                             EventNotificationsResource eventNotificationsResource,
                             Conversions conversions,
                             EventDefinitionService eventDefinitionService,
                             NotificationService notificationService,
                             FieldRulesUtilities fieldRulesUtilities) {
        // TODO should probably move these fields down into the business namespace
        this.alertRuleService = alertRuleService;
        this.streamService = streamService;
        this.clusterEventBus = clusterEventBus;
        this.configurationService = configurationService;
        this.eventNotificationsResource = eventNotificationsResource;
        this.eventDefinitionService = eventDefinitionService;

        this.alertListUtilsService = alertListUtilsService;
        this.conversions = conversions;
        this.streamPipelineService = streamPipelineService;
        this.notificationService = notificationService;
        this.fieldRulesUtilities = fieldRulesUtilities;
    }

    private Stream loadStream(String streamIdentifier) {
        try {
            return this.streamService.load(streamIdentifier);
        } catch (NotFoundException e) {
            // this may happen if the underlying stream was deleted
            // see test test_get_all_rules_should_not_fail_when_a_stream_is_deleted_issue105 and related issue
            // TODO in this case, maybe the rule should rather be converted into a corrupted rule than this aspect being handled by the interface?
            return null;
        }
    }

    private AlertRuleStream constructAlertRuleStream(TriggeringConditions conditions) {
        String streamIdentifier = conditions.filteringStreamIdentifier();
        Stream stream = this.loadStream(streamIdentifier);
        return this.conversions.constructAlertRuleStream(stream, conditions);
    }

    private boolean isDisabled(TriggeringConditions conditions) {
        String streamIdentifier = conditions.filteringStreamIdentifier();
        Stream stream = this.loadStream(streamIdentifier);
        if (stream == null) {
            return false;
        }
        return stream.getDisabled();
    }

    private GetDataAlertRule constructDataAlertRule(AlertRule alert) {
        AlertPattern alertPattern = alert.pattern();
        DateTime lastModified = alert.getLastModified();
        Optional event = Optional.empty();
        Map parametersCondition = null;
        boolean isDisabled = false;
        AlertRuleStream alertRuleStream = null;
        AlertRuleStream alertRuleStream2 = null;
        String eventIdentifier2 = null;
        if (alertPattern instanceof CorrelationAlertPattern pattern) {
            event = this.eventDefinitionService.getEventDefinition(pattern.eventIdentifier());
            parametersCondition = getConditionParameters(event);
            TriggeringConditions conditions1 = pattern.conditions1();
            alertRuleStream = this.constructAlertRuleStream(conditions1);
            TriggeringConditions conditions2 = pattern.conditions2();
            alertRuleStream2 = this.constructAlertRuleStream(conditions2);
            isDisabled = this.isDisabled(conditions1);
        } else if (alertPattern instanceof DisjunctionAlertPattern pattern) {
            event = this.eventDefinitionService.getEventDefinition(pattern.eventIdentifier1());
            parametersCondition = getConditionParameters(event);
            TriggeringConditions conditions = pattern.conditions1();
            alertRuleStream = this.constructAlertRuleStream(conditions);
            TriggeringConditions conditions2 = pattern.conditions2();
            alertRuleStream2 = this.constructAlertRuleStream(conditions2);
            isDisabled = this.isDisabled(conditions);
            eventIdentifier2 = pattern.eventIdentifier2();
        } else if (alertPattern instanceof AggregationAlertPattern pattern) {
            event = this.eventDefinitionService.getEventDefinition(pattern.eventIdentifier());
            parametersCondition = getConditionParameters(event);
            TriggeringConditions conditions = pattern.conditions();
            alertRuleStream = this.constructAlertRuleStream(conditions);
            isDisabled = this.isDisabled(conditions);
        }
        Optional notification = this.notificationService.get(alert.getNotificationID());
        String severity = null;
        String notificationIdentifier = null;
        if (notification.isPresent()) {
            NotificationDto notificationDto = notification.get();
            LoggingNotificationConfig loggingNotificationConfig = (LoggingNotificationConfig) notificationDto.config();
            severity = loggingNotificationConfig.severity().getType();
            notificationIdentifier = notificationDto.id();
        }

        String eventIdentifier = null;
        String description = null;
        if (event.isPresent()) {
            EventDefinitionDto eventDefinitionDto = event.get();
            eventIdentifier = eventDefinitionDto.id();
            description = eventDefinitionDto.description();
        }

        return GetDataAlertRule.create(alert.getTitle(),
                severity,
                eventIdentifier,
                eventIdentifier2,
                notificationIdentifier,
                alert.getCreatedAt(),
                alert.getCreatorUserId(),
                lastModified,
                isDisabled,
                description,
                alert.getAlertType(),
                parametersCondition,
                alertRuleStream,
                alertRuleStream2);
    }

    private Map getConditionParameters(Optional event) {
        if (!event.isPresent()) {
            return null;
        }
        return this.conversions.getConditionParameters(event.get().config());
    }

    @GET
    @Timed
    @ApiOperation(value = "Lists all existing alerts")
    @RequiresAuthentication
    @RequiresPermissions(AlertRuleRestPermissions.WIZARD_ALERTS_RULES_READ)
    public List list() {
        List alerts = this.alertRuleService.all();

        List alertsData = new ArrayList<>();
        for (AlertRule alert: alerts) {
            alertsData.add(this.constructDataAlertRule(alert));
        }

        return alertsData;
    }

    @GET
    @Path("/{title}")
    @Timed
    @ApiOperation(value = "Get a alert")
    @RequiresAuthentication
    @RequiresPermissions(AlertRuleRestPermissions.WIZARD_ALERTS_RULES_READ)
    @ApiResponses(value = {
            @ApiResponse(code = 404, message = "Alert not found."),
    })
    public GetDataAlertRule get(@ApiParam(name = TITLE, required = true) @PathParam(TITLE) String title)
            throws UnsupportedEncodingException, NotFoundException {
        String alertTitle = java.net.URLDecoder.decode(title, ENCODING);
        AlertRule alert = this.alertRuleService.load(alertTitle);
        if (alert == null) {
            throw new NotFoundException("Alert <" + alertTitle + "> not found!");
        }
        return this.constructDataAlertRule(alert);
    }

    private String checkImportPolicyAndGetTitle(String title, UserContext userContext) {
        String alertTitle = title;
        if (this.alertRuleService.isPresent(alertTitle)) {
            // TODO should be get or default here: it will return null when starting with a fresh instance of graylog
            // Idem in AlertListRessource. Add a test that creates two alerts with same title
            AlertWizardConfig configuration = this.configurationService.getConfiguration();
            ImportPolicyType importPolicy = configuration.accessImportPolicy();
            if (importPolicy != null && importPolicy.equals(ImportPolicyType.RENAME)) {
                String newAlertTitle;
                int i = 1;
                do {
                    newAlertTitle = alertTitle + "(" + i + ")";
                    i++;
                } while (this.alertRuleService.isPresent(newAlertTitle));
                alertTitle = newAlertTitle;
            } else if (importPolicy != null && importPolicy.equals(ImportPolicyType.REPLACE)) {
                try {
                    this.delete(alertTitle, userContext);
                } catch (MongoException | UnsupportedEncodingException e) {
                    LOG.error("Failed to replace alert rule");
                    throw new BadRequestException("Failed to replace alert rule.");
                }
            } else {
                LOG.info("Failed to create alert rule: Alert rule title already exist");
                throw new BadRequestException("Failed to create alert rule: Alert rule title already exist.");
            }
        }
        return alertTitle;
    }

    private TriggeringConditions createTriggeringConditionsFromStream(AlertRuleStream streamConfiguration, String title,
                                                                      Stream filteringStream, String userName) throws ValidationException {
        List fieldRulesWithList = this.streamPipelineService.extractPipelineFieldRules(streamConfiguration.getFieldRules());

        TriggeringConditions.Builder builder = TriggeringConditions.builder()
                .filteringStreamIdentifier(filteringStream.getId());
        if (fieldRulesWithList.isEmpty()) {
            return builder.outputStreamIdentifier(filteringStream.getId()).build();
        }

        for (FieldRule fieldRule: fieldRulesWithList) {
            this.alertListUtilsService.incrementUsage(fieldRule.getValue());
        }

        Stream.MatchingType matchingType = streamConfiguration.getMatchingType();
        if (matchingType.equals(Stream.MatchingType.AND) && this.fieldRulesUtilities.hasStreamRules(streamConfiguration.getFieldRules())) {
            PipelineDao graylogPipeline = this.streamPipelineService.createPipeline(title, matchingType, filteringStream.getId());
            Stream outputStream = this.streamPipelineService.createStream(matchingType, title + " output", userName);
            RuleDao pipelineRule = this.streamPipelineService.createPipelineRule(title, fieldRulesWithList, matchingType, outputStream);
            Pipeline pipeline = Pipeline.builder()
                    .identifier(graylogPipeline.id()).ruleIdentifier(pipelineRule.id()).fieldRules(fieldRulesWithList)
                    .build();
            return builder.outputStreamIdentifier(outputStream.getId()).pipeline(pipeline).build();
        } else {
            PipelineDao graylogPipeline = this.streamPipelineService.createPipeline(title, matchingType);
            RuleDao pipelineRule = this.streamPipelineService.createPipelineRule(title, fieldRulesWithList, matchingType, filteringStream);
            Pipeline pipeline = Pipeline.builder()
                    .identifier(graylogPipeline.id()).ruleIdentifier(pipelineRule.id()).fieldRules(fieldRulesWithList)
                    .build();
            return builder.outputStreamIdentifier(filteringStream.getId()).pipeline(pipeline).build();
        }
    }

    private TriggeringConditions createTriggeringConditions(AlertRuleStream streamConfiguration, String title, String userName) throws ValidationException {
        Stream stream = this.streamPipelineService.createStream(streamConfiguration.getMatchingType(), title, userName);
        this.streamPipelineService.createStreamRule(streamConfiguration.getFieldRules(), stream.getId());
        return createTriggeringConditionsFromStream(streamConfiguration, title, stream, userName);
    }

    private TriggeringConditions updateTriggeringConditions(TriggeringConditions previousConditions, String alertTitle,
                                                            AlertRuleStream streamConfiguration, String userName) throws ValidationException {
        // update filtering stream
        String streamIdentifier = previousConditions.filteringStreamIdentifier();
        Stream stream = this.loadStream(streamIdentifier);
        this.streamPipelineService.updateStream(stream, streamConfiguration, alertTitle);

        if (!previousConditions.outputStreamIdentifier().equals(streamIdentifier)) {
            this.streamPipelineService.deleteStreamFromIdentifier(previousConditions.outputStreamIdentifier());
        }
        deletePipelineIfAny(previousConditions.pipeline());

        return createTriggeringConditionsFromStream(streamConfiguration, alertTitle, stream, userName);
    }

    @POST
    // TODO is this annotation @Timed necessary? What is it for? Remove?
    @Timed
    @ApiOperation(value = "Create an alert")
    @RequiresAuthentication
    @RequiresPermissions(AlertRuleRestPermissions.WIZARD_ALERTS_RULES_CREATE)
    @ApiResponses(value = {@ApiResponse(code = 400, message = "The supplied request is not valid.")})
    @AuditEvent(type = AlertWizardAuditEventTypes.WIZARD_ALERTS_RULES_CREATE)
    public Response create(@ApiParam(name = "JSON body", required = true) @Valid @NotNull AlertRuleRequest request, @Context UserContext userContext)
            throws ValidationException, BadRequestException {

        this.conversions.checkIsValidRequest(request);

        String userName = getCurrentUser().getName();
        String title = request.getTitle();
        String alertTitle = checkImportPolicyAndGetTitle(title, userContext);
        AlertType alertType = request.getConditionType();

        String notificationIdentifier = this.notificationService.createNotification(alertTitle, request.getSeverity(), userContext);
        AlertPattern pattern = createAlertPattern(notificationIdentifier, request, alertTitle, userContext, userName);

        AlertRule alertRule = AlertRule.create(
                alertTitle,
                alertType,
                pattern,
                notificationIdentifier,
                DateTime.now(),
                userName,
                DateTime.now());
        alertRule = this.alertRuleService.create(alertRule);

        GetDataAlertRule result = this.constructDataAlertRule(alertRule);
        return Response.ok().entity(result).build();
    }

    private AlertPattern createAlertPattern(String notificationIdentifier, AlertRuleRequest request, String alertTitle,
                                            UserContext userContext, String userName) throws ValidationException {
        AlertType alertType = request.getConditionType();

        TriggeringConditions conditions = createTriggeringConditions(request.getStream(), alertTitle, userName);

        switch (alertType) {
            case THEN:
            case AND:
                return createCorrelationAlertPattern(notificationIdentifier, request, alertTitle, userContext, userName, conditions);
            case OR:
                return createDisjunctionAlertPattern(notificationIdentifier, request, alertTitle, userContext, userName, conditions);
            default:
                String description = request.getDescription();
                Map conditionParameters = request.conditionParameters();
                String streamIdentifier = conditions.outputStreamIdentifier();
                EventProcessorConfig configuration1 = this.conversions.createEventConfiguration(alertType, conditionParameters, streamIdentifier);
                String eventIdentifier = this.eventDefinitionService.createEvent(alertTitle, description, notificationIdentifier, configuration1, userContext);

                return AggregationAlertPattern.builder().conditions(conditions).eventIdentifier(eventIdentifier).build();
        }
    }

    private DisjunctionAlertPattern createDisjunctionAlertPattern(String notificationIdentifier, AlertRuleRequest request, String alertTitle, UserContext userContext, String userName, TriggeringConditions conditions) throws ValidationException {
        String description = request.getDescription();
        Map conditionParameters = request.conditionParameters();

        TriggeringConditions conditions2 = createTriggeringConditions(request.getSecondStream(), alertTitle + "#2", userName);
        String streamIdentifier = conditions.outputStreamIdentifier();
        EventProcessorConfig configuration = this.conversions.createAggregationCondition(streamIdentifier, conditionParameters);
        String eventIdentifier = this.eventDefinitionService.createEvent(alertTitle, description, notificationIdentifier, configuration, userContext);
        String streamIdentifier2 = conditions2.outputStreamIdentifier();
        EventProcessorConfig configuration2 = this.conversions.createAggregationCondition(streamIdentifier2, conditionParameters);
        String eventIdentifier2 = this.eventDefinitionService.createEvent(alertTitle + "#2", description, notificationIdentifier, configuration2, userContext);

        return DisjunctionAlertPattern.builder()
                .conditions1(conditions).conditions2(conditions2).eventIdentifier1(eventIdentifier).eventIdentifier2(eventIdentifier2)
                .build();
    }

    private CorrelationAlertPattern createCorrelationAlertPattern(String notificationIdentifier, AlertRuleRequest request, String alertTitle, UserContext userContext, String userName, TriggeringConditions conditions) throws ValidationException {
        String description = request.getDescription();
        AlertType alertType = request.getConditionType();
        Map conditionParameters = request.conditionParameters();

        TriggeringConditions conditions2 = createTriggeringConditions(request.getSecondStream(), alertTitle + "#2", userName);
        String streamIdentifier = conditions.outputStreamIdentifier();
        String streamIdentifier2 = conditions2.outputStreamIdentifier();
        EventProcessorConfig configuration = this.conversions.createCorrelationCondition(alertType, streamIdentifier, streamIdentifier2, conditionParameters);
        String eventIdentifier = this.eventDefinitionService.createEvent(alertTitle, description, notificationIdentifier, configuration, userContext);
        return CorrelationAlertPattern.builder().conditions1(conditions).conditions2(conditions2).eventIdentifier(eventIdentifier).build();
    }

    private AlertPattern updateAlertPattern(AlertPattern previousAlertPattern, String notificationIdentifier,
                                            AlertRuleRequest request, AlertType previousAlertType, String title,
                                            UserContext userContext, String userName) throws ValidationException {
        AlertRuleStream streamConfiguration = request.getStream();
        AlertRuleStream streamConfiguration2 = request.getSecondStream();
        AlertType alertType = request.getConditionType();
        if (previousAlertType != alertType) {
            deleteAlertPattern(previousAlertPattern);
            return createAlertPattern(notificationIdentifier, request, title, userContext, userName);
        }

        String title2 = title + "#2";
        // TODO increase readability: extract three methods?
        if (previousAlertPattern instanceof CorrelationAlertPattern previousPattern) {
            TriggeringConditions previousConditions = previousPattern.conditions1();
            TriggeringConditions conditions = updateTriggeringConditions(previousConditions, title, streamConfiguration, userName);
            TriggeringConditions previousConditions2 = previousPattern.conditions2();
            TriggeringConditions conditions2 = this.updateTriggeringConditions(previousConditions2, title2, streamConfiguration2, userName);

            String streamIdentifier = conditions.outputStreamIdentifier();
            String streamIdentifier2 = conditions2.outputStreamIdentifier();
            EventProcessorConfig configuration = this.conversions.createCorrelationCondition(alertType, streamIdentifier, streamIdentifier2, request.conditionParameters());
            this.eventDefinitionService.updateEvent(title, request.getDescription(), previousPattern.eventIdentifier(), configuration);

            return previousPattern.toBuilder().conditions1(conditions).build();
        } else if (previousAlertPattern instanceof DisjunctionAlertPattern previousPattern) {
            TriggeringConditions previousConditions = previousPattern.conditions1();
            TriggeringConditions conditions = updateTriggeringConditions(previousConditions, title, streamConfiguration, userName);
            TriggeringConditions previousConditions2 = previousPattern.conditions2();
            TriggeringConditions conditions2 = this.updateTriggeringConditions(previousConditions2, title2, streamConfiguration2, userName);

            String streamIdentifier = conditions.outputStreamIdentifier();
            EventProcessorConfig configuration = this.conversions.createEventConfiguration(request.getConditionType(), request.conditionParameters(), streamIdentifier);
            this.eventDefinitionService.updateEvent(title, request.getDescription(), previousPattern.eventIdentifier1(), configuration);

            String streamIdentifier2 = conditions2.outputStreamIdentifier();
            EventProcessorConfig configuration2 = this.conversions.createAggregationCondition(streamIdentifier2, request.conditionParameters());
            this.eventDefinitionService.updateEvent(title2, request.getDescription(), previousPattern.eventIdentifier2(), configuration2);

            return previousPattern.toBuilder().conditions1(conditions).build();
        } else if (previousAlertPattern instanceof AggregationAlertPattern previousPattern) {
            TriggeringConditions previousConditions = previousPattern.conditions();
            TriggeringConditions conditions = updateTriggeringConditions(previousConditions, title, streamConfiguration, userName);
            String streamIdentifier = conditions.outputStreamIdentifier();
            EventProcessorConfig configuration = this.conversions.createEventConfiguration(request.getConditionType(), request.conditionParameters(), streamIdentifier);
            this.eventDefinitionService.updateEvent(title, request.getDescription(), previousPattern.eventIdentifier(), configuration);

            return previousPattern.toBuilder().conditions(conditions).build();
        }

        throw new RuntimeException("Unreachable code");
    }

    @PUT
    @Path("/{title}")
    @Timed
    @RequiresAuthentication
    @RequiresPermissions(AlertRuleRestPermissions.WIZARD_ALERTS_RULES_UPDATE)
    @ApiOperation(value = "Update a alert")
    @ApiResponses(value = {@ApiResponse(code = 400, message = "The supplied request is not valid.")})
    @AuditEvent(type = AlertWizardAuditEventTypes.WIZARD_ALERTS_RULES_UPDATE)
    public Response update(@ApiParam(name = TITLE, required = true)
                           @PathParam(TITLE) String title,
                           @ApiParam(name = "JSON body", required = true) @Valid @NotNull AlertRuleRequest request,
                           @Context UserContext userContext
    ) throws UnsupportedEncodingException, NotFoundException, ValidationException {

        this.conversions.checkIsValidRequest(request);

        AlertRule previousAlert = this.alertRuleService.load(title);
        String notificationIdentifier = previousAlert.getNotificationID();
        String userName = getCurrentUser().getName();

        this.notificationService.updateNotification(title, notificationIdentifier, request.getSeverity());

        AlertType previousAlertType = previousAlert.getAlertType();
        AlertPattern pattern = updateAlertPattern(previousAlert.pattern(), notificationIdentifier, request,
                previousAlertType, title, userContext, userName);

        AlertRule alertRule = AlertRule.create(
                title,
                request.getConditionType(),
                pattern,
                previousAlert.getNotificationID(),
                previousAlert.getCreatedAt(),
                userName,
                DateTime.now());
        alertRule = this.alertRuleService.update(java.net.URLDecoder.decode(title, ENCODING), alertRule);

        GetDataAlertRule result = this.constructDataAlertRule(alertRule);
        return Response.accepted().entity(result).build();
    }

    private void deleteTriggeringConditions(TriggeringConditions conditions) {
        this.streamPipelineService.deleteStreamFromIdentifier(conditions.filteringStreamIdentifier());
        if (!conditions.outputStreamIdentifier().equals(conditions.filteringStreamIdentifier())) {
            this.streamPipelineService.deleteStreamFromIdentifier(conditions.outputStreamIdentifier());
        }
        deletePipelineIfAny(conditions.pipeline());
    }

    private void deletePipelineIfAny(Pipeline pipeline) {
        if (pipeline == null) {
            return;
        }
        this.streamPipelineService.deletePipeline(pipeline.identifier(), pipeline.ruleIdentifier());
        for (FieldRule fieldRule: this.nullSafe(pipeline.fieldRules())) {
            this.alertListUtilsService.decrementUsage(fieldRule.getValue());
        }
    }

    private void deleteEvent(String eventIdentifier) {
        if (eventIdentifier == null) {
            return;
        }
        this.eventDefinitionService.delete(eventIdentifier);
    }

    private void deleteAlertPattern(AlertPattern alertPattern) {
        if (alertPattern instanceof CorrelationAlertPattern pattern) {
            deleteTriggeringConditions(pattern.conditions1());
            deleteTriggeringConditions(pattern.conditions2());
            deleteEvent(pattern.eventIdentifier());
        } else if (alertPattern instanceof DisjunctionAlertPattern pattern) {
            deleteTriggeringConditions(pattern.conditions1());
            deleteTriggeringConditions(pattern.conditions2());
            deleteEvent(pattern.eventIdentifier1());
            deleteEvent(pattern.eventIdentifier2());
        } else if (alertPattern instanceof AggregationAlertPattern pattern) {
            deleteTriggeringConditions(pattern.conditions());
            deleteEvent(pattern.eventIdentifier());
        }
    }

    @DELETE
    @Path("/{title}")
    @RequiresAuthentication
    @RequiresPermissions(AlertRuleRestPermissions.WIZARD_ALERTS_RULES_DELETE)
    @ApiOperation(value = "Delete a alert")
    @ApiResponses(value = {
            @ApiResponse(code = 404, message = "Alert not found."),
            @ApiResponse(code = 400, message = "Invalid ObjectId.")
    })
    @AuditEvent(type = AlertWizardAuditEventTypes.WIZARD_ALERTS_RULES_DELETE)
    public void delete(@ApiParam(name = TITLE, required = true)
                       @PathParam(TITLE) String title,
                       @Context UserContext userContext
    ) throws MongoException, UnsupportedEncodingException {
        String alertTitle = java.net.URLDecoder.decode(title, ENCODING);

        try {
            AlertRule alertRule = this.alertRuleService.load(alertTitle);

            deleteAlertPattern(alertRule.pattern());
            if (alertRule.getNotificationID() != null && !alertRule.getNotificationID().isEmpty()) {
                // TODO move this down into AlertRuleUtilsService and remove the use for eventNotificationsResource
                this.eventNotificationsResource.delete(alertRule.getNotificationID(), userContext);
            }
        } catch (NotFoundException e) {
            LOG.error("Cannot find alert " + alertTitle, e);
        }

        this.alertRuleService.destroy(alertTitle);
    }

    // TODO remove this method => should have a more regular code (empty lists instead of null)!!!
    private  Collection nullSafe(Collection c) {
        return (c == null) ? Collections.emptyList() : c;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy