Skip to content

Commit f41b641

Browse files
committed
FIX all sort
1 parent 8661965 commit f41b641

File tree

10 files changed

+182
-31
lines changed

10 files changed

+182
-31
lines changed

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/business/AlertRuleService.java

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,16 @@
1818
package com.airbus_cyber_security.graylog.wizard.alert.business;
1919

2020
import com.airbus_cyber_security.graylog.wizard.alert.model.AlertRule;
21+
import com.google.common.collect.ImmutableList;
2122
import com.mongodb.BasicDBObject;
23+
import com.mongodb.client.AggregateIterable;
24+
import com.mongodb.client.MongoCollection;
25+
import com.mongodb.client.model.Aggregates;
26+
import com.mongodb.client.model.Field;
27+
import com.mongodb.client.model.Variable;
28+
import org.bson.Document;
2229
import org.bson.conversions.Bson;
30+
import org.graylog.events.processor.DBEventDefinitionService;
2331
import org.graylog2.bindings.providers.MongoJackObjectMapperProvider;
2432
import org.graylog2.database.MongoConnection;
2533
import org.graylog2.database.NotFoundException;
@@ -37,6 +45,7 @@
3745
import java.util.List;
3846
import java.util.Set;
3947
import java.util.function.Predicate;
48+
import java.util.stream.StreamSupport;
4049

4150

4251
// TODO split this into AlertRuleCollection and move it down in the persistence namespace
@@ -46,13 +55,16 @@ public class AlertRuleService extends PaginatedDbService<AlertRule> {
4655
private final Validator validator;
4756
private static final Logger LOG = LoggerFactory.getLogger(AlertRuleService.class);
4857
private static final String TITLE = "title";
58+
private static final List<String> STRING_FIELDS = List.of("title", "description", "creator_user_id");
59+
private final MongoCollection<Document> collection;
4960

5061
@Inject
5162
public AlertRuleService(MongoConnection mongoConnection, MongoJackObjectMapperProvider mapperProvider,
5263
Validator validator) {
5364
super(mongoConnection, mapperProvider, AlertRule.class, COLLECTION_NAME);
5465
this.validator = validator;
5566
this.db.createIndex(new BasicDBObject(TITLE, 1), new BasicDBObject("unique", true));
67+
this.collection = mongoConnection.getMongoDatabase().getCollection(COLLECTION_NAME);
5668
}
5769

5870
public AlertRule create(AlertRule alert) {
@@ -94,18 +106,65 @@ public boolean isPresent(String title) {
94106
return (this.db.getCount(DBQuery.is(TITLE, title)) > 0);
95107
}
96108

97-
public PaginatedList<AlertRule> searchPaginated(SearchQuery query, Predicate<AlertRule> filter,
98-
Bson sort, int page, int perPage) {
109+
public PaginatedList<AlertRule> searchPaginated(SearchQuery query, Predicate<AlertRule> predicate,
110+
String order, String sortField, int page, int perPage) {
99111
final Bson dbQuery = query.toBson();
100-
final PaginatedList<AlertRule> list = filter == null ?
101-
findPaginatedWithQueryAndSort(dbQuery, sort, page, perPage) :
102-
findPaginatedWithQueryFilterAndSort(dbQuery, filter, sort, page, perPage);
103-
104-
return new PaginatedList<>(
105-
list,
106-
list.pagination().total(),
107-
page,
108-
perPage
109-
);
112+
113+
var pipelineBuilder = ImmutableList.<Bson>builder()
114+
.add(Aggregates.match(dbQuery));
115+
116+
if (sortField.equals("priority") || sortField.equals("description")) {
117+
String eventField = "$event_definition." + sortField;
118+
pipelineBuilder.add(Aggregates.lookup(
119+
DBEventDefinitionService.COLLECTION_NAME,
120+
List.of(new Variable<>("event_identifier", doc("$toObjectId", "$alert_pattern.event_identifier")),
121+
new Variable<>("event_identifier1", doc("$toObjectId", "$alert_pattern.event_identifier1"))),
122+
List.of(Aggregates.match(doc("$or",
123+
List.of(
124+
doc("$expr", doc("$eq", List.of("$_id", "$$event_identifier"))),
125+
doc("$expr", doc("$eq", List.of("$_id", "$$event_identifier1")))
126+
)))),
127+
"event_definition"
128+
))
129+
.add(Aggregates.set(new Field<>(sortField, doc("$first", eventField))))
130+
.add(Aggregates.unset("event_definition"));
131+
}
132+
133+
if (isStringField(sortField)) {
134+
pipelineBuilder.add(Aggregates.set(new Field<>("lower" + sortField, doc("$toLower", "$" + sortField))))
135+
.add(Aggregates.sort(getSortBuilder(order, "lower" + sortField)))
136+
.add(Aggregates.unset("lower" + sortField));
137+
} else {
138+
pipelineBuilder.add(Aggregates.sort(getSortBuilder(order, sortField)));
139+
}
140+
141+
final AggregateIterable<Document> result = collection.aggregate(pipelineBuilder.build());
142+
143+
final List<AlertRule> alertRuleList = StreamSupport.stream(result.spliterator(), false)
144+
.map(AlertRule::fromDocument)
145+
.filter(predicate)
146+
.toList();
147+
148+
final long grandTotal = db.find(DBQuery.empty()).toArray()
149+
.stream()
150+
.filter(predicate)
151+
.count();
152+
153+
final List<AlertRule> paginatedAlerts = perPage > 0
154+
? alertRuleList.stream()
155+
.skip((long) perPage * Math.max(0, page - 1))
156+
.limit(perPage)
157+
.toList()
158+
: alertRuleList;
159+
160+
return new PaginatedList<>(paginatedAlerts, alertRuleList.size(), page, perPage, grandTotal);
161+
}
162+
163+
private Document doc(String key, Object value) {
164+
return new Document(key, value);
165+
}
166+
167+
private boolean isStringField(String sortField) {
168+
return STRING_FIELDS.contains(sortField);
110169
}
111170
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/AggregationAlertPattern.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.auto.value.AutoValue;
2525

2626
import jakarta.validation.constraints.NotNull;
27+
import org.bson.Document;
2728

2829
/**
2930
* To encode most simple rules: a single path (condition -> aggregation event) which trigger the same notification.
@@ -64,4 +65,11 @@ public static Builder create() {
6465

6566
public abstract AggregationAlertPattern build();
6667
}
68+
69+
public static AggregationAlertPattern fromDocument(Document document) {
70+
return builder().
71+
eventIdentifier(document.getString(FIELD_EVENT_IDENTIFIER)).
72+
conditions(TriggeringConditions.fromDocument(document.get(FIELD_CONDITIONS, Document.class))).
73+
build();
74+
}
6775
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/AlertRule.java

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import com.fasterxml.jackson.annotation.JsonAutoDetect;
2121
import com.fasterxml.jackson.annotation.JsonCreator;
2222
import com.fasterxml.jackson.annotation.JsonProperty;
23+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
2324
import com.google.auto.value.AutoValue;
25+
import org.bson.Document;
2426
import org.joda.time.DateTime;
2527

2628
import jakarta.annotation.Nullable;
@@ -32,54 +34,86 @@
3234
@AutoValue
3335
@JsonAutoDetect
3436
public abstract class AlertRule {
35-
public final static String ID = "id";
37+
public static final String FIELD_ID = "_id";
38+
public static final String FIELD_TITLE = "title";
39+
public static final String FIELD_ALERT_TYPE = "alert_type";
40+
public static final String FIELD_ALERT_PATTERN = "alert_pattern";
41+
public static final String FIELD_NOTIFICATION = "notification";
42+
public static final String FIELD_CREATED_AT = "created_at";
43+
public static final String FIELD_LAST_MODIFIED = "last_modified";
44+
public static final String FIELD_CREATOR_USER_ID = "creator_user_id";
3645

3746
@Id
3847
@ObjectId
3948
@Nullable
40-
@JsonProperty(ID)
49+
@JsonProperty("id")
4150
public abstract String id();
4251

43-
@JsonProperty("title")
52+
@JsonProperty(FIELD_TITLE)
4453
@NotNull
4554
public abstract String getTitle();
4655

47-
@JsonProperty("alert_type")
56+
@JsonProperty(FIELD_ALERT_TYPE)
4857
@Nullable
4958
public abstract AlertType getAlertType();
5059

51-
@JsonProperty("alert_pattern")
60+
@JsonProperty(FIELD_ALERT_PATTERN)
5261
@NotNull
5362
public abstract AlertPattern pattern();
5463

5564
// TODO rename into notificationIdentifier
56-
@JsonProperty("notification")
65+
@JsonProperty(FIELD_NOTIFICATION)
5766
@Nullable
5867
public abstract String getNotificationID();
5968

60-
@JsonProperty("created_at")
69+
@JsonProperty(FIELD_CREATED_AT)
6170
@Nullable
6271
public abstract DateTime getCreatedAt();
6372

6473
// TODO rename int creatorUserIdentifier
65-
@JsonProperty("creator_user_id")
74+
@JsonProperty(FIELD_CREATOR_USER_ID)
6675
@Nullable
6776
public abstract String getCreatorUserId();
6877

69-
@JsonProperty("last_modified")
78+
@JsonProperty(FIELD_LAST_MODIFIED)
7079
@Nullable
7180
public abstract DateTime getLastModified();
7281

7382
// TODO should replace the create functions by a Builder (see EventDefinitionDTO)
7483
@JsonCreator
7584
public static AlertRule create(@JsonProperty("_id") String objectId,
76-
@JsonProperty("title") String title,
77-
@JsonProperty("alert_type") AlertType alertType,
78-
@JsonProperty("alert_pattern") AlertPattern pattern,
79-
@JsonProperty("notification") String notificationID,
80-
@JsonProperty("created_at") DateTime createdAt,
81-
@JsonProperty("creator_user_id") String creatorUserId,
82-
@JsonProperty("last_modified") DateTime lastModified){
85+
@JsonProperty(FIELD_TITLE) String title,
86+
@JsonProperty(FIELD_ALERT_TYPE) AlertType alertType,
87+
@JsonProperty(FIELD_ALERT_PATTERN) AlertPattern pattern,
88+
@JsonProperty(FIELD_NOTIFICATION) String notificationID,
89+
@JsonProperty(FIELD_CREATED_AT) DateTime createdAt,
90+
@JsonProperty(FIELD_CREATOR_USER_ID) String creatorUserId,
91+
@JsonProperty(FIELD_LAST_MODIFIED) DateTime lastModified){
8392
return new AutoValue_AlertRule(objectId, title, alertType, pattern, notificationID, createdAt, creatorUserId, lastModified);
8493
}
94+
95+
public static AlertRule fromDocument(Document document) {
96+
Document patternDoc = document.get(FIELD_ALERT_PATTERN, Document.class);
97+
AlertPattern pattern = null;
98+
if (patternDoc != null) {
99+
String patternClass = patternDoc.getString(JsonTypeInfo.Id.CLASS.getDefaultPropertyName());
100+
if (patternClass.equals(AutoValue_AggregationAlertPattern.class.getName())) {
101+
pattern = AggregationAlertPattern.fromDocument(patternDoc);
102+
} else if (patternClass.equals(AutoValue_CorrelationAlertPattern.class.getName())) {
103+
pattern = CorrelationAlertPattern.fromDocument(patternDoc);
104+
} else if (patternClass.equals(AutoValue_DisjunctionAlertPattern.class.getName())) {
105+
pattern = DisjunctionAlertPattern.fromDocument(patternDoc);
106+
}
107+
}
108+
109+
return new AutoValue_AlertRule(
110+
document.getObjectId(FIELD_ID).toHexString(),
111+
document.getString(FIELD_TITLE),
112+
AlertType.valueOf(document.getString(FIELD_ALERT_TYPE)),
113+
pattern,
114+
document.getString(FIELD_NOTIFICATION),
115+
new DateTime(document.getDate(FIELD_CREATED_AT)),
116+
document.getString(FIELD_CREATOR_USER_ID),
117+
new DateTime(document.getDate(FIELD_LAST_MODIFIED)));
118+
}
85119
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/CorrelationAlertPattern.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.auto.value.AutoValue;
2525

2626
import jakarta.validation.constraints.NotNull;
27+
import org.bson.Document;
2728

2829
/**
2930
* To encode AND/THEN rules: two separate conditions which are combined with a correlation event
@@ -72,4 +73,12 @@ public static Builder create() {
7273

7374
public abstract CorrelationAlertPattern build();
7475
}
76+
77+
public static CorrelationAlertPattern fromDocument(Document document) {
78+
return builder().
79+
eventIdentifier(document.getString(FIELD_EVENT_IDENTIFIER)).
80+
conditions1(TriggeringConditions.fromDocument(document.get(FIELD_CONDITIONS1, Document.class))).
81+
conditions2(TriggeringConditions.fromDocument(document.get(FIELD_CONDITIONS2, Document.class))).
82+
build();
83+
}
7584
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/DisjunctionAlertPattern.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.auto.value.AutoValue;
2424

2525
import jakarta.validation.constraints.NotNull;
26+
import org.bson.Document;
2627

2728
/**
2829
* To encode Or rule: two separate paths (condition -> aggregation event) which trigger the same notification.
@@ -79,4 +80,13 @@ public static Builder create() {
7980

8081
public abstract DisjunctionAlertPattern build();
8182
}
83+
84+
public static DisjunctionAlertPattern fromDocument(Document document) {
85+
return builder().
86+
eventIdentifier1(document.getString(FIELD_EVENT_IDENTIFIER1)).
87+
eventIdentifier2(document.getString(FIELD_EVENT_IDENTIFIER2)).
88+
conditions1(TriggeringConditions.fromDocument(document.get(FIELD_CONDITIONS1, Document.class))).
89+
conditions2(TriggeringConditions.fromDocument(document.get(FIELD_CONDITIONS2, Document.class))).
90+
build();
91+
}
8292
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/FieldRule.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import jakarta.annotation.Nullable;
2727
import jakarta.validation.constraints.NotNull;
28+
import org.bson.Document;
2829

2930
@AutoValue
3031
@JsonAutoDetect
@@ -55,4 +56,13 @@ public static FieldRule create(@JsonProperty("id") String id,
5556
return new AutoValue_FieldRule(id, field, type, value);
5657
}
5758

59+
public static FieldRule fromDocument(Document document) {
60+
return create(
61+
document.getString("id"),
62+
document.getString("field"),
63+
document.getInteger("type"),
64+
document.getString("value")
65+
);
66+
}
67+
5868
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/Pipeline.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.google.auto.value.AutoValue;
2525

2626
import jakarta.validation.constraints.NotNull;
27+
import org.bson.Document;
28+
2729
import java.util.List;
2830

2931
@AutoValue
@@ -71,4 +73,12 @@ public static Builder create() {
7173

7274
public abstract Pipeline build();
7375
}
76+
77+
public static Pipeline fromDocument(Document document) {
78+
return builder().
79+
identifier(document.getString(FIELD_IDENTIFIER)).
80+
ruleIdentifier(document.getString(FIELD_RULE_IDENTIFIER)).
81+
fieldRules(document.getList(FIELD_FIELD_RULES, Document.class).stream().map(FieldRule::fromDocument).toList()).
82+
build();
83+
}
7484
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/model/TriggeringConditions.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import jakarta.annotation.Nullable;
2727
import jakarta.validation.constraints.NotNull;
28+
import org.bson.Document;
2829
import org.graylog2.plugin.streams.Stream;
2930

3031
/**
@@ -94,4 +95,13 @@ public static Builder create() {
9495

9596
public abstract TriggeringConditions build();
9697
}
98+
99+
public static TriggeringConditions fromDocument(Document document) {
100+
return builder().
101+
matchingType(Stream.MatchingType.valueOf(document.getString(FIELD_MATCHING_TYPE))).
102+
filteringStreamIdentifier(document.getString(FIELD_FILTERING_STREAM)).
103+
outputStreamIdentifier(document.getString(FIELD_OUTPUT_STREAM)).
104+
105+
build();
106+
}
97107
}

src/main/java/com/airbus_cyber_security/graylog/wizard/alert/rest/AlertRuleResource.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ public PageListResponse<GetDataAlertRule> getPage(@ApiParam(name = "page") @Quer
656656
allowableValues = "title,user,created,lastModified")
657657
@DefaultValue(DEFAULT_SORT_FIELD) @QueryParam("sort") String sort,
658658
@ApiParam(name = "order", value = "The sort direction", allowableValues = "asc, desc")
659-
@DefaultValue(DEFAULT_SORT_DIRECTION) @QueryParam("order") SortOrder order) {
659+
@DefaultValue(DEFAULT_SORT_DIRECTION) @QueryParam("order") String order) {
660660

661661
SearchQuery searchQuery;
662662
try {
@@ -668,7 +668,8 @@ public PageListResponse<GetDataAlertRule> getPage(@ApiParam(name = "page") @Quer
668668
final PaginatedList<AlertRule> result = this.alertRuleService.searchPaginated(
669669
searchQuery,
670670
alertRule -> true,
671-
order.toBsonSort(sortAttr),
671+
order,
672+
sortAttr,
672673
page,
673674
perPage);
674675

src/web/wizard/components/rules/AlertRulesContainer.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ const AlertRulesContainer = ({ fieldOrder }) => {
9292
{key: 'created', label: intl.formatMessage({id: 'wizard.created', defaultMessage: 'Created'}), config: 'Created', sortable: true},
9393
{key: 'lastModified', label: intl.formatMessage({id: 'wizard.lastModified', defaultMessage: 'Last Modified'}), config: 'Last Modified', sortable: true},
9494
{key: 'user', label: intl.formatMessage({id: 'wizard.user', defaultMessage: 'User'}), config: 'User', sortable: true},
95-
{key: 'status', label: intl.formatMessage({id: 'wizard.status', defaultMessage: 'Status'}), config: 'Status', sortable: true},
95+
{key: 'status', label: intl.formatMessage({id: 'wizard.status', defaultMessage: 'Status'}), config: 'Status', sortable: false},
9696
{key: 'rule', label: intl.formatMessage({id: 'wizard.rule', defaultMessage: 'Rule'}), config: 'Rule', sortable: false}
9797
];
9898
const availablePriorityTypes = [

0 commit comments

Comments
 (0)