Skip to content

Commit

Permalink
gh-4146 Fix audit events for stream delete
Browse files Browse the repository at this point in the history
  • Loading branch information
at055612 committed Mar 4, 2024
1 parent a039550 commit b6d61da
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,6 @@ private void validateSelection(final String actionType, final Runnable runnable)
runnable.run();
}
});

} else {
runnable.run();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ public static User createUser(final UserName userName) {
return builder.build();
}

public static Data createData(final String name, final String value) {
return Data.builder()
.withName(name)
.withValue(value)
.build();
}

public static Group createGroup(final stroom.security.shared.User group) {
Objects.requireNonNull(group);
if (!group.isGroup()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,45 @@
import stroom.meta.shared.MetaResource;
import stroom.meta.shared.MetaRow;
import stroom.meta.shared.SelectionSummary;
import stroom.meta.shared.Status;
import stroom.meta.shared.UpdateStatusRequest;
import stroom.query.api.v2.ExpressionOperator;
import stroom.query.api.v2.ExpressionTerm;
import stroom.query.api.v2.ExpressionTerm.Condition;
import stroom.util.NullSafe;
import stroom.util.date.DateUtil;
import stroom.util.logging.LambdaLogger;
import stroom.util.logging.LambdaLoggerFactory;
import stroom.util.logging.LogUtil;
import stroom.util.shared.Range;
import stroom.util.shared.ResultPage;

import event.logging.SearchEventAction;
import event.logging.BaseObject;
import event.logging.Criteria;
import event.logging.Data;
import event.logging.DeleteEventAction;
import event.logging.EventAction;
import event.logging.MultiObject;
import event.logging.OtherObject;
import event.logging.Query;
import event.logging.SimpleQuery;
import event.logging.UpdateEventAction;
import jakarta.inject.Inject;
import jakarta.inject.Provider;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@AutoLogged
class MetaResourceImpl implements MetaResource {

private static final LambdaLogger LOGGER = LambdaLoggerFactory.getLogger(MetaResourceImpl.class);
private static final Pattern IN_LIST_DELIMITER_PATTERN = Pattern.compile(
Pattern.quote(Condition.IN_CONDITION_DELIMITER));

private final Provider<MetaService> metaServiceProvider;
private final Provider<StroomEventLoggingService> eventLoggingServiceProvider;

Expand All @@ -60,32 +85,165 @@ public Meta fetch(final long id) {
public Integer updateStatus(final UpdateStatusRequest request) {
final StroomEventLoggingService eventLoggingService = eventLoggingServiceProvider.get();

final ExpressionOperator expression = request.getCriteria().getExpression();
final ExpressionOperator expression = Objects.requireNonNull(request.getCriteria().getExpression());
final boolean isDelete = request.getNewStatus() == Status.DELETED;
String currentStatus = NullSafe.getOrElse(
request.getCurrentStatus(), Status::getDisplayValue, "unspecified/unknown");
final String newStatus = NullSafe.getOrElse(
request.getNewStatus(), Status::getDisplayValue, "unspecified/unknown");

final BaseObject baseObjectBefore;
final BaseObject baseObjectAfter;
final String description;
final List<Data> dataItems = new ArrayList<>();

if (expression.getChildren().size() == 1
&& expression.getChildren().get(0) instanceof final ExpressionTerm term
&& term.hasCondition(Condition.EQUALS)) {
final String metaIdStr = term.getValue();

final String currentStatus = (request.getCurrentStatus() != null)
?
request.getCurrentStatus().getDisplayValue()
: "selected";
final String newStatus = (request.getNewStatus() != null)
?
request.getNewStatus().getDisplayValue()
: "unspecified / unknown";
try {
long metaId = Long.parseLong(metaIdStr);
final Meta meta = metaServiceProvider.get().getMeta(metaId, true);
dataItems.add(Data.builder()
.withName("Feed")
.withValue(meta.getFeedName())
.build());
dataItems.add(Data.builder()
.withName("Type")
.withValue(meta.getTypeName())
.build());
currentStatus = meta.getStatus().getDisplayValue();
} catch (Exception e) {
LOGGER.error(LogUtil.message("Error fetching meta for id '{}' for audit logging: {}",
metaIdStr, LogUtil.exceptionMessage(e), e));
}

final SearchEventAction action = SearchEventAction.builder()
.withQuery(StroomEventLoggingUtil.convertExpression(expression))
.build();
baseObjectBefore = OtherObject.builder()
.withId(term.getValue())
.withState(currentStatus)
.build();
baseObjectAfter = OtherObject.builder()
.withId(term.getValue())
.withState(newStatus)
.build();
description = isDelete
? "Delete stream with ID " + metaIdStr
: "Update the status of stream with ID " + metaIdStr
+ " from " + currentStatus + " to " + newStatus;
} else if (expression.getChildren().size() == 1
&& expression.getChildren().get(0) instanceof final ExpressionTerm term
&& term.hasCondition(Condition.IN)) {
final String streamIdsStr = term.getValue();
final String[] idArr = NullSafe.getOrElse(
streamIdsStr,
IN_LIST_DELIMITER_PATTERN::split,
new String[0]);
final int count = idArr.length;
baseObjectBefore = null;
baseObjectAfter = Criteria.builder()
.withQuery(Query.builder()
.withSimple(SimpleQuery.builder()
.withInclude(streamIdsStr)
.build())
.build())
.build();
description = isDelete
? "Delete " + count + " streams"
: "Update the status of " + count + " streams from " + currentStatus + " to " + newStatus;
addSelectionSummaryDataItems(dataItems, request.getCriteria());
} else {
baseObjectBefore = null;
baseObjectAfter = Criteria.builder()
.withQuery(StroomEventLoggingUtil.convertExpression(expression))
.build();
description = isDelete
? "Delete streams matching a criteria"
: "Update the status of streams matching a criteria from " + currentStatus + " to " + newStatus;
addSelectionSummaryDataItems(dataItems, request.getCriteria());
}

final EventAction eventAction;
if (isDelete) {
DeleteEventAction.Builder<Void> deleteBuilder = DeleteEventAction.builder()
.addData(dataItems);
NullSafe.firstNonNull(baseObjectBefore, baseObjectAfter)
.ifPresent(deleteBuilder::withObjects);
eventAction = deleteBuilder.build();
} else {
UpdateEventAction.Builder<Void> updateBuilder = UpdateEventAction.builder()
.withAfter(MultiObject.builder()
.withObjects(baseObjectAfter)
.build())
.addData(dataItems);
if (baseObjectBefore != null) {
updateBuilder.withBefore(MultiObject.builder()
.withObjects(baseObjectBefore)
.build());
}
eventAction = updateBuilder.build();
}

return eventLoggingService.loggedWorkBuilder()
.withTypeId(StroomEventLoggingUtil.buildTypeId(this, "updateStatus"))
.withDescription("Modify the status of " + currentStatus + " streams to " + newStatus)
.withDefaultEventAction(action)
.withDescription(description)
.withDefaultEventAction(eventAction)
.withSimpleLoggedResult(() -> metaServiceProvider.get().updateStatus(
request.getCriteria(),
request.getCurrentStatus(),
request.getNewStatus()))
.getResultAndLog();
}

private void addSelectionSummaryDataItems(
final List<Data> dataItems,
final FindMetaCriteria criteria) {
try {
final SelectionSummary selectionSummary = getSelectionSummary(criteria);
if (selectionSummary != null) {
addData(dataItems, "Count", selectionSummary.getItemCount());
addData(dataItems, "FeedCount", selectionSummary.getFeedCount());
addData(dataItems, "TypeCount", selectionSummary.getTypeCount());
addData(dataItems, "ProcessorCount", selectionSummary.getProcessorCount());
addData(dataItems, "PipelineCount", selectionSummary.getProcessorCount());
addData(dataItems, "StatusCount", selectionSummary.getStatusCount());

final String minCreateTime = NullSafe.get(
selectionSummary.getAgeRange(),
Range::getFrom,
DateUtil::createNormalDateTimeString);
addData(dataItems, "MinCreateTime", minCreateTime);

final String maxCreateTime = NullSafe.get(
selectionSummary.getAgeRange(),
Range::getTo,
DateUtil::createNormalDateTimeString);
addData(dataItems, "MaxCreateTime", maxCreateTime);
}
} catch (Exception e) {
LOGGER.error(LogUtil.message("Error building selection summary for criteria {}: {}",
criteria, LogUtil.exceptionMessage(e), e));
}
}

private void addData(final List<Data> list, final String name, final String value) {
if (value != null) {
Objects.requireNonNull(list).add(Data.builder()
.withName(name)
.withValue(value)
.build());
}
}

private void addData(final List<Data> list, final String name, final Long value) {
if (value != null) {
Objects.requireNonNull(list).add(Data.builder()
.withName(name)
.withValue(String.valueOf(value))
.build());
}
}

@AutoLogged(OperationType.SEARCH)
@Override
public ResultPage<MetaRow> findMetaRow(final FindMetaCriteria criteria) {
Expand Down
24 changes: 24 additions & 0 deletions unreleased_changes/20240304_172409_526__4146.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
* Issue **#4146** : Fix audit events for deleting/restoring streams.


```sh
# ********************************************************************************
# Issue title: Model stream deletion events as a `Delete` activity
# Issue link: https://github.com/gchq/stroom/issues/4146
# ********************************************************************************

# ONLY the top line will be included as a change entry in the CHANGELOG.
# The entry should be in GitHub flavour markdown and should be written on a SINGLE
# line with no hard breaks. You can have multiple change files for a single GitHub issue.
# The entry should be written in the imperative mood, i.e. 'Fix nasty bug' rather than
# 'Fixed nasty bug'.
#
# Examples of acceptable entries are:
#
#
# * Issue **123** : Fix bug with an associated GitHub issue in this repository
#
# * Issue **namespace/other-repo#456** : Fix bug with an associated GitHub issue in another repository
#
# * Fix bug with no associated GitHub issue.
```

0 comments on commit b6d61da

Please sign in to comment.