Skip to content

Commit d06848a

Browse files
authored
Merge pull request #1550 from utmstack/release/v11.1.6
Release/v11.1.6
2 parents 5f17d81 + 4ff0253 commit d06848a

File tree

34 files changed

+3658
-551
lines changed

34 files changed

+3658
-551
lines changed

CHANGELOG.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# UTMStack 11.1.5 – Release Notes
1+
# UTMStack 11.1.6 – Release Notes
22

3-
The **UTMStack v11.1.5** update delivers important fixes and usability improvements to enhance stability and user experience.
3+
The **UTMStack v11.1.6** update delivers important fixes and usability improvements to enhance stability and user experience.
44

55
## Improvements & Fixes
6-
- Standardized `utm_visualization` field names by replacing legacy O365 keys with new conventions.
7-
- Enhanced responsive behavior for TFA enrollment components based on viewport height.
6+
- Enhanced Threat and Windows activity dashboards with new filters and aggregations for better data analysis.
7+
- Improved email notifications for alerts, providing clearer information and enhanced formatting for better user experience.
88

backend/src/main/java/com/park/utmstack/domain/shared_types/alert/Event.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@
33
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
44
import com.fasterxml.jackson.annotation.JsonInclude;
55
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.park.utmstack.util.MapUtil;
67
import lombok.Data;
8+
import lombok.Getter;
9+
import org.springframework.util.StringUtils;
710

11+
import java.time.Instant;
12+
import java.time.ZoneId;
13+
import java.time.format.DateTimeFormatter;
814
import java.util.List;
15+
import java.util.Locale;
916
import java.util.Map;
1017

1118
@Data
19+
@Getter
1220
@JsonInclude(JsonInclude.Include.NON_NULL)
1321
@JsonIgnoreProperties(ignoreUnknown = true)
1422
public class Event {
@@ -39,5 +47,18 @@ public class Event {
3947

4048
private List<String> errors;
4149
private Map<String, ComplianceValues> compliance;
50+
51+
public Map<String, String> getLogxFlatted() {
52+
return MapUtil.flattenToStringMap(log, true);
53+
}
54+
55+
public String getTimestampFormatted() {
56+
try {
57+
return StringUtils.hasText(timestamp) ? DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withLocale(Locale.getDefault()).withZone(
58+
ZoneId.systemDefault()).format(Instant.parse(timestamp)) : null;
59+
} catch (Exception e) {
60+
return null;
61+
}
62+
}
4263
}
4364

backend/src/main/java/com/park/utmstack/repository/network_scan/UtmNetworkScanRepository.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ void updateGroup(@Param("assetIds") List<Long> assetIds,
114114

115115
@Modifying
116116
@Query(nativeQuery = true, value = "with sources as (select ds.source from utm_data_input_status ds where ds.data_type in :types)" +
117-
" delete from utm_network_scan asset where asset.asset_ip in (select src.source from sources src) " +
118-
"or asset.asset_name in (select src.source from sources src)")
117+
" delete from utm_network_scan asset where (asset.asset_ip in (select src.source from sources src) " +
118+
"or asset.asset_name in (select src.source from sources src)) and asset.is_agent is false")
119119
void deleteAllAssetsByDataType(@Param("types") List<String> types);
120120

121121
@Query("select distinct n.assetOsPlatform from UtmNetworkScan n where n.assetOsPlatform is not null and n.isAgent is true")
@@ -129,4 +129,11 @@ void updateGroup(@Param("assetIds") List<Long> assetIds,
129129
"JOIN UtmAssetGroup ag ON ns.groupId = ag.id " +
130130
"WHERE ns.groupId IS NOT NULL")
131131
List<Object[]> findAllAssetGroupMappings();
132+
133+
List<UtmNetworkScan> findByAssetIpInOrAssetNameIn(List<String> assetIps, List<String> assetNames);
134+
135+
List<UtmNetworkScan> findByIsAgentTrue();
136+
137+
List<UtmNetworkScan> findByAssetNameIn(List<String> assetNames);
138+
132139
}

backend/src/main/java/com/park/utmstack/security/saml/Saml2LoginSuccessHandler.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,11 @@ public void onAuthenticationSuccess(HttpServletRequest request,
5959

6060
Collection<? extends GrantedAuthority> authorities = Objects.requireNonNull(user.getAuthorities())
6161
.stream()
62-
.map(Objects::toString)
63-
.filter(r -> r.startsWith("ROLE_"))
64-
.map(SimpleGrantedAuthority::new)
62+
.map(a -> new SimpleGrantedAuthority(a.getName()))
6563
.toList();
6664

6765
UsernamePasswordAuthenticationToken auth =
68-
new UsernamePasswordAuthenticationToken((Object) username, null, authorities);
66+
new UsernamePasswordAuthenticationToken(username, null, authorities);
6967

7068
SecurityContextHolder.getContext().setAuthentication(auth);
7169

backend/src/main/java/com/park/utmstack/service/MailService.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.park.utmstack.domain.application_events.enums.ApplicationEventType;
66
import com.park.utmstack.domain.incident.UtmIncident;
77
import com.park.utmstack.domain.mail_sender.MailConfig;
8+
import com.park.utmstack.domain.shared_types.alert.Event;
89
import com.park.utmstack.domain.shared_types.alert.UtmAlert;
910
import com.park.utmstack.domain.shared_types.LogType;
1011
import com.park.utmstack.service.application_events.ApplicationEventService;
@@ -250,7 +251,7 @@ public void sendPasswordResetMail(User user) {
250251
}
251252

252253
@Async
253-
public void sendAlertEmail(List<String> emailsTo, UtmAlert alert, List<LogType> relatedLogs) {
254+
public void sendAlertEmail(List<String> emailsTo, UtmAlert alert, List<Event> relatedLogs) {
254255
final String ctx = CLASS_NAME + ".sendAlertEmail";
255256
try {
256257
JavaMailSender javaMailSender = getJavaMailSender();
@@ -333,7 +334,7 @@ public void sendIncidentEmail(List<String> emailsTo, List<UtmAlert> alerts, UtmI
333334
* @throws Exception In case of any error
334335
*/
335336
private ByteArrayResource buildAlertEmailAttachment(Context context, UtmAlert alert,
336-
List<LogType> relatedLogs) throws Exception {
337+
List<Event> relatedLogs) throws Exception {
337338
final String ctx = CLASS_NAME + ".buildAlertEmailAttachment";
338339
try {
339340
ByteArrayOutputStream bout = new ByteArrayOutputStream();
@@ -351,9 +352,9 @@ private ByteArrayResource buildAlertEmailAttachment(Context context, UtmAlert al
351352
}
352353
}
353354

354-
private void buildRelatedEventCsvAttachment(List<LogType> relatedLogs, ZipOutputStream zipOut) {
355+
private void buildRelatedEventCsvAttachment(List<Event> relatedLogs, ZipOutputStream zipOut) {
355356
final String ctx = CLASS_NAME + ".buildRelatedEventCsvAttachment";
356-
Map<String, List<LogType>> evtTypes = new HashMap<>();
357+
Map<String, List<Event>> evtTypes = new HashMap<>();
357358

358359
// Separating event types
359360
relatedLogs.forEach(doc -> {

backend/src/main/java/com/park/utmstack/service/UtmAlertTagRuleService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ private void assignAssetGroupsToReviewAlerts() {
269269
ctx._source.assetGroupName = '%s';
270270
}
271271
""",
272-
assetName.replace("'", "\\'"), // Escapar comillas simples
272+
assetName.replace("'", "\\'"),
273273
groupId,
274274
groupName.replace("'", "\\'")
275275
));
@@ -282,7 +282,7 @@ private void assignAssetGroupsToReviewAlerts() {
282282
List<FilterType> filters = new ArrayList<>();
283283
filters.add(new FilterType(Constants.alertStatus, OperatorType.IS,
284284
AlertStatus.AUTOMATIC_REVIEW.getCode()));
285-
filters.add(new FilterType("dataSource", OperatorType.IS_NOT, null));
285+
filters.add(new FilterType("dataSource", OperatorType.EXIST, null));
286286

287287
Query query = SearchUtil.toQuery(filters);
288288
String indexPattern = Constants.SYS_INDEX_PATTERN.get(SystemIndexPattern.ALERTS);

backend/src/main/java/com/park/utmstack/service/UtmDataInputStatusService.java

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.scheduling.annotation.Scheduled;
3939
import org.springframework.stereotype.Service;
4040
import org.springframework.transaction.annotation.Transactional;
41+
import org.springframework.transaction.support.TransactionSynchronizationManager;
4142
import org.springframework.util.CollectionUtils;
4243
import org.springframework.util.StringUtils;
4344

@@ -157,7 +158,7 @@ private void checkDataInputStatus(List<UtmDataInputStatus> inputs, String server
157158

158159
long currentTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
159160
List<UtmDataInputStatus> inTime = inputs.stream().filter(row -> (currentTimeInSeconds - row.getTimestamp()) < 3600)
160-
.collect(Collectors.toList());
161+
.toList();
161162
if (!CollectionUtils.isEmpty(inTime))
162163
return;
163164

@@ -219,7 +220,7 @@ public void syncDataInputStatus() {
219220
* Gets the sources from utm_data_input_status that are not registered in utm_network_scan table
220221
* and create new assets with it. This method is a schedule with a delay of 1 hour
221222
*/
222-
@Scheduled(fixedDelay = 15000, initialDelay = 30000)
223+
@Scheduled(fixedDelay = 30000, initialDelay = 60000)
223224
public void syncSourcesToAssets() {
224225
final String ctx = CLASSNAME + ".syncSourcesToAssets";
225226
try {
@@ -231,55 +232,73 @@ public void syncSourcesToAssets() {
231232
}
232233
}
233234

235+
@Transactional
234236
public void synchronizeSourcesToAssets() {
235237
final String ctx = CLASSNAME + ".syncSourcesToAssets";
238+
236239
try {
237-
final List<String> excludeOfTypes = dataTypesRepository.findAllByIncludedFalse().stream()
238-
.map(UtmDataTypes::getDataType).collect(Collectors.toList());
239-
excludeOfTypes.addAll(Arrays.asList("utmstack", "UTMStack", DataSourceConstants.IBM_AS400_TYPE));
240-
241-
List<UtmDataInputStatus> sources = dataInputStatusRepository.extractSourcesToExport(excludeOfTypes);
242-
if (!CollectionUtils.isEmpty(sources)) {
243-
//return;
244-
245-
Map<String, Boolean> sourcesWithStatus = extractSourcesWithUpDownStatus(sources);
246-
List<UtmNetworkScan> assets = networkScanService.findAll();
247-
248-
List<UtmNetworkScan> saveOrUpdate = new ArrayList<>();
249-
sourcesWithStatus.forEach((key, value) -> {
250-
Optional<UtmNetworkScan> assetOpt = assets.stream()
251-
.filter(asset -> ((StringUtils.hasText(asset.getAssetIp()) && asset.getAssetIp().equals(key))
252-
|| (StringUtils.hasText(asset.getAssetName()) && asset.getAssetName().equals(key))))
253-
.findFirst();
254-
if (assetOpt.isPresent()) {
255-
UtmNetworkScan utmAsset = assetOpt.get();
256-
if (Objects.isNull(utmAsset.getUpdateLevel())
257-
|| utmAsset.getUpdateLevel().equals(UpdateLevel.DATASOURCE)) {
258-
utmAsset.assetAlive(value)
259-
.updateLevel(UpdateLevel.DATASOURCE)
260-
.assetStatus(AssetStatus.CHECK)
261-
.modifiedAt(LocalDateTime.now().toInstant(ZoneOffset.UTC));
262-
saveOrUpdate.add(utmAsset);
263-
}
264-
} else {
265-
saveOrUpdate.add(new UtmNetworkScan(key, value));
266-
}
267-
});
268-
269-
assets.forEach(asset -> {
270-
if (!sourcesWithStatus.containsKey(asset.getAssetIp()) && !sourcesWithStatus.containsKey(asset.getAssetName())
271-
&& !Objects.isNull(asset.getUpdateLevel()) && asset.getUpdateLevel().equals(UpdateLevel.DATASOURCE)) {
272-
asset.assetStatus(AssetStatus.MISSING).updateLevel(null)
273-
.modifiedAt(LocalDateTime.now().toInstant(ZoneOffset.UTC));
274-
saveOrUpdate.add(asset);
275-
}
276-
});
277240

278-
networkScanService.saveAll(saveOrUpdate);
241+
List<String> excludeDataTypes = dataTypesRepository.findAllByIncludedFalse()
242+
.stream()
243+
.map(UtmDataTypes::getDataType)
244+
.collect(Collectors.toList());
245+
246+
excludeDataTypes.addAll(Arrays.asList("utmstack", "UTMStack", DataSourceConstants.IBM_AS400_TYPE));
247+
248+
List<UtmDataInputStatus> sources = dataInputStatusRepository.extractSourcesToExport(excludeDataTypes);
249+
if (CollectionUtils.isEmpty(sources)) {
250+
return;
279251
}
280-
// Finally, delete excluded assets
281-
networkScanRepository.deleteAllAssetsByDataType(excludeOfTypes);
252+
253+
Map<String, Boolean> sourcesWithStatus = extractSourcesWithUpDownStatus(sources);
254+
255+
List<String> keys = new ArrayList<>(sourcesWithStatus.keySet());
256+
List<UtmNetworkScan> assets = networkScanRepository.findByAssetIpInOrAssetNameIn(keys, keys);
257+
258+
Map<String, UtmNetworkScan> assetsByKey = new HashMap<>();
259+
260+
assets.forEach(a -> {
261+
if (StringUtils.hasText(a.getAssetIp())) assetsByKey.put(a.getAssetIp(), a);
262+
if (StringUtils.hasText(a.getAssetName())) assetsByKey.put(a.getAssetName(), a);
263+
});
264+
265+
for (Map.Entry<String, Boolean> entry : sourcesWithStatus.entrySet()) {
266+
String key = entry.getKey();
267+
Boolean alive = entry.getValue();
268+
269+
UtmNetworkScan asset = assetsByKey.get(key);
270+
271+
if (asset != null) {
272+
if (asset.getUpdateLevel() == null || asset.getUpdateLevel().equals(UpdateLevel.DATASOURCE) || asset.getUpdateLevel().equals(UpdateLevel.AGENT)) {
273+
asset.assetAlive(alive)
274+
.updateLevel(UpdateLevel.DATASOURCE)
275+
.assetStatus(AssetStatus.CHECK)
276+
.modifiedAt(LocalDateTime.now().toInstant(ZoneOffset.UTC));
277+
278+
networkScanService.save(asset);
279+
}
280+
} else {
281+
networkScanService.save(new UtmNetworkScan(key, alive));
282+
}
283+
}
284+
285+
assets.forEach(asset -> {
286+
boolean missing = !sourcesWithStatus.containsKey(asset.getAssetIp())
287+
&& !sourcesWithStatus.containsKey(asset.getAssetName());
288+
289+
if (missing && UpdateLevel.DATASOURCE.equals(asset.getUpdateLevel())) {
290+
asset.assetStatus(AssetStatus.MISSING)
291+
.updateLevel(null)
292+
.modifiedAt(LocalDateTime.now().toInstant(ZoneOffset.UTC));
293+
294+
networkScanService.save(asset);
295+
}
296+
});
297+
298+
networkScanRepository.deleteAllAssetsByDataType(excludeDataTypes);
299+
282300
} catch (Exception e) {
301+
log.error("{}: Error synchronizing sources to assets - {}", ctx, e.getMessage(), e);
283302
throw new RuntimeException(ctx + ": " + e.getLocalizedMessage());
284303
}
285304
}

0 commit comments

Comments
 (0)