Skip to content

Commit 6429fcf

Browse files
committed
Merge branch 'backlog/google-tfa' into release/v11
# Conflicts: # backend/src/main/resources/config/liquibase/changelog/20250814001_adding_suricata.xml # backend/src/main/resources/config/liquibase/master.xml # frontend/src/environments/environment.ts
2 parents 586d530 + 8de2639 commit 6429fcf

47 files changed

Lines changed: 1733 additions & 225 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/pom.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,31 @@
351351
<artifactId>tika-core</artifactId>
352352
<version>2.9.1</version>
353353
</dependency>
354+
355+
<dependency>
356+
<groupId>com.warrenstrange</groupId>
357+
<artifactId>googleauth</artifactId>
358+
<version>1.4.0</version>
359+
</dependency>
360+
361+
<dependency>
362+
<groupId>com.google.zxing</groupId>
363+
<artifactId>core</artifactId>
364+
<version>3.4.1</version>
365+
</dependency>
366+
367+
<dependency>
368+
<groupId>com.google.zxing</groupId>
369+
<artifactId>javase</artifactId>
370+
<version>3.4.1</version>
371+
</dependency>
372+
373+
<dependency>
374+
<groupId>com.github.ben-manes.caffeine</groupId>
375+
<artifactId>caffeine</artifactId>
376+
<version>3.0.5</version>
377+
</dependency>
378+
354379
</dependencies>
355380

356381
<build>

backend/src/main/java/com/park/utmstack/config/Constants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public final class Constants {
3535
public static final String PROP_DATE_SETTINGS_TIMEZONE = "utmstack.time.zone";
3636
public static final String PROP_NETWORK_SCAN_API_URL = "utmstack.networkScan.apiUrl";
3737
public static final String PROP_TFA_ENABLE = "utmstack.tfa.enable";
38+
public static final String PROP_TFA_METHOD = "utmstack.tfa.method";
39+
public static final int EXPIRES_IN_SECONDS = 30;
40+
public static final int INIT_EXPIRES_IN_SECONDS = 300;
41+
public static final String TFA_ISSUER = "UTMStack";
3842

3943
// ----------------------------------------------------------------------------------
4044
// - Modules checks
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.park.utmstack.config;
2+
3+
import com.github.benmanes.caffeine.cache.Cache;
4+
import com.github.benmanes.caffeine.cache.Caffeine;
5+
import com.park.utmstack.domain.tfa.TfaSetupState;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.context.annotation.Configuration;
8+
9+
import java.time.Duration;
10+
11+
@Configuration
12+
public class TfaCacheConfig {
13+
14+
@Bean
15+
public Cache<String, TfaSetupState> tfaSetupCache() {
16+
return Caffeine.newBuilder()
17+
.expireAfterWrite(Duration.ofMinutes(10))
18+
.maximumSize(1000)
19+
.build();
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.park.utmstack.domain.tfa;
2+
3+
import java.util.Arrays;
4+
5+
public enum TfaMethod {
6+
EMAIL,
7+
TOTP;
8+
9+
public static TfaMethod fromString(String value) {
10+
return Arrays.stream(values())
11+
.filter(m -> m.name().equalsIgnoreCase(value))
12+
.findFirst()
13+
.orElseThrow(() -> new IllegalArgumentException("Unsupported TFA method: " + value));
14+
}
15+
}
16+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.park.utmstack.domain.tfa;
2+
3+
import lombok.Getter;
4+
import lombok.ToString;
5+
6+
@Getter
7+
@ToString
8+
public class TfaSetupState {
9+
private final String secret;
10+
private final long expiresAt;
11+
private final long setupStartedAt;
12+
13+
public TfaSetupState(String secret, long expiresAt) {
14+
this.secret = secret;
15+
this.expiresAt = expiresAt;
16+
this.setupStartedAt = System.currentTimeMillis();
17+
}
18+
19+
public boolean isExpired() {
20+
return System.currentTimeMillis() > expiresAt;
21+
}
22+
23+
public long getRemainingSeconds() {
24+
long remaining = expiresAt - System.currentTimeMillis();
25+
return Math.max(remaining / 1000, 0);
26+
}
27+
}
28+
29+

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import com.park.utmstack.config.Constants;
44
import com.park.utmstack.domain.UtmConfigurationParameter;
55
import com.park.utmstack.domain.application_events.enums.ApplicationEventType;
6+
import com.park.utmstack.domain.tfa.TfaMethod;
67
import com.park.utmstack.repository.UtmConfigurationParameterRepository;
78
import com.park.utmstack.service.application_events.ApplicationEventService;
9+
import com.park.utmstack.service.tfa.TfaService;
810
import com.park.utmstack.util.CipherUtil;
911
import com.park.utmstack.util.exceptions.UtmMailException;
1012
import org.slf4j.Logger;
@@ -24,7 +26,7 @@
2426
import java.util.Optional;
2527
import java.util.stream.Collectors;
2628

27-
import static com.park.utmstack.config.Constants.DATE_FORMAT_SETTING_ID;
29+
import static com.park.utmstack.config.Constants.*;
2830

2931
/**
3032
* Service Implementation for managing UtmConfigurationParameter.
@@ -40,15 +42,17 @@ public class UtmConfigurationParameterService {
4042
private final UserService userService;
4143
private final MailService mailService;
4244
private final ApplicationEventService applicationEventService;
45+
private final TfaService tfaService;
4346

4447
public UtmConfigurationParameterService(UtmConfigurationParameterRepository configParamRepository,
4548
UserService userService,
4649
MailService mailService,
47-
ApplicationEventService applicationEventService) {
50+
ApplicationEventService applicationEventService, TfaService tfaService) {
4851
this.configParamRepository = configParamRepository;
4952
this.userService = userService;
5053
this.mailService = mailService;
5154
this.applicationEventService = applicationEventService;
55+
this.tfaService = tfaService;
5256
}
5357

5458
@EventListener(ApplicationReadyEvent.class)
@@ -80,11 +84,24 @@ public void init() {
8084
public void saveAll(List<UtmConfigurationParameter> params) throws UtmMailException {
8185
final String ctx = CLASSNAME + ".saveAll";
8286
try {
83-
// If the configuration to save is: Enable Two-Factor Authentication then we need to check
84-
// if the email configuration is OK
85-
params.stream().filter(p -> p.getConfParamShort().equals(Constants.PROP_TFA_ENABLE)
86-
&& Boolean.parseBoolean(p.getConfParamValue()))
87-
.findFirst().ifPresent(tfa -> validateMailConfOnMFAActivation());
87+
88+
Boolean tfaEnabledParam = params.stream()
89+
.filter(p -> p.getConfParamShort().equals(Constants.PROP_TFA_ENABLE))
90+
.findFirst()
91+
.map(p -> Boolean.parseBoolean(p.getConfParamValue()))
92+
.orElse(Boolean.valueOf(CFG.get(PROP_TFA_ENABLE)));
93+
94+
TfaMethod tfaMethodParam = params.stream()
95+
.filter(p -> p.getConfParamShort().equals(Constants.PROP_TFA_METHOD))
96+
.findFirst()
97+
.map(p -> TfaMethod.valueOf(p.getConfParamValue()))
98+
.orElse(null);
99+
100+
if (tfaEnabledParam && tfaMethodParam != null) {
101+
tfaService.persistConfiguration(tfaMethodParam);
102+
log.info("TFA enabled with method: {}", tfaMethodParam);
103+
}
104+
88105

89106
Map<String, String> cfg = new HashMap<>();
90107
for (UtmConfigurationParameter p : params) {
@@ -148,7 +165,7 @@ public Map<String, String> getValueMapForDateSetting() throws Exception {
148165
}
149166
}
150167

151-
private void validateMailConfOnMFAActivation() throws UtmMailException {
168+
public void validateMailConfOnMFAActivation() throws UtmMailException {
152169
final String ctx = CLASSNAME + ".validateMailConfOnMFAActivation";
153170
try {
154171
mailService.sendCheckEmail(List.of(userService.getCurrentUserLogin().getEmail()));
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.park.utmstack.service.dto.jwt;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.park.utmstack.domain.tfa.TfaMethod;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Data;
7+
8+
@Data
9+
public class JWTToken {
10+
private String idToken;
11+
private boolean authenticated;
12+
private String tfaMethod;
13+
14+
public JWTToken(String idToken, boolean authenticated, String tfaMethod) {
15+
this.idToken = idToken;
16+
this.authenticated = authenticated;
17+
this.tfaMethod = tfaMethod;
18+
}
19+
20+
public JWTToken(String idToken, boolean authenticated) {
21+
this.idToken = idToken;
22+
this.authenticated = authenticated;
23+
this.tfaMethod = null;
24+
}
25+
26+
@JsonProperty("id_token")
27+
String getIdToken() {
28+
return idToken;
29+
}
30+
31+
void setIdToken(String idToken) {
32+
this.idToken = idToken;
33+
}
34+
35+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.park.utmstack.service.dto.tfa.init;
2+
3+
import com.park.utmstack.domain.tfa.TfaMethod;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@AllArgsConstructor
10+
@NoArgsConstructor
11+
public class Delivery {
12+
private TfaMethod method;
13+
private String target;
14+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.park.utmstack.service.dto.tfa.init;
2+
3+
import com.park.utmstack.domain.tfa.TfaMethod;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@AllArgsConstructor
10+
@NoArgsConstructor
11+
public class TfaInitRequest {
12+
private TfaMethod method;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.park.utmstack.service.dto.tfa.init;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
@Data
8+
@AllArgsConstructor
9+
@NoArgsConstructor
10+
public class TfaInitResponse {
11+
private String status;
12+
private Delivery delivery;
13+
private long expiresInSeconds;
14+
}

0 commit comments

Comments
 (0)