Skip to content

Commit b8e4068

Browse files
committed
Merge branch '7.8'
2 parents 8e79895 + b096501 commit b8e4068

File tree

11 files changed

+168
-187
lines changed

11 files changed

+168
-187
lines changed

.github/workflows/build_and_release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
# Make sure the wrapper jar has not been tampered with
3131
- name: Validate gradle wrapper jar
3232
id: validate_gradle_wrapper
33-
uses: gradle/actions/wrapper-validation@v3
33+
uses: gradle/actions/wrapper-validation@v4
3434

3535
# Set variables in github's special env file which are then automatically
3636
# read into env vars in each subsequent step

stroom-config/stroom-config-app/src/main/java/stroom/config/app/SessionConfig.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class SessionConfig extends AbstractConfig implements IsStroomConfig {
1717

1818
public static final String PROP_NAME_MAX_INACTIVE_INTERVAL = "maxInactiveInterval";
1919

20-
public static final StroomDuration DEFAULT_MAX_INACTIVE_INTERVAL = StroomDuration.ofDays(1);
20+
public static final StroomDuration DEFAULT_MAX_INACTIVE_INTERVAL = StroomDuration.ofDays(7);
2121

2222
private final StroomDuration maxInactiveInterval;
2323

stroom-config/stroom-config-app/src/test/resources/stroom/config/app/expected.yaml

+6-1
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,11 @@ appConfig:
780780
expireAfterWrite: "PT1M"
781781
maximumSize: 1000
782782
refreshAfterWrite: null
783+
authenticationStateCache:
784+
expireAfterAccess: null
785+
expireAfterWrite: "PT10M"
786+
maximumSize: 1000
787+
refreshAfterWrite: null
783788
maxApiKeyExpiryAge: "P365D"
784789
openId:
785790
authEndpoint: null
@@ -939,7 +944,7 @@ appConfig:
939944
strictTransportSecurity: "max-age=31536000; includeSubDomains; preload"
940945
xssProtection: "1; mode=block"
941946
session:
942-
maxInactiveInterval: "P1D"
947+
maxInactiveInterval: "P7D"
943948
sessionCookie:
944949
httpOnly: true
945950
sameSite: "STRICT"

stroom-core-client/src/main/java/stroom/dispatch/client/RestFactoryImpl.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
import stroom.task.client.Task;
55
import stroom.task.client.TaskMonitor;
66
import stroom.task.client.TaskMonitorFactory;
7+
import stroom.util.client.Console;
78
import stroom.util.shared.GwtNullSafe;
89

910
import com.google.gwt.core.client.GWT;
1011
import com.google.gwt.event.shared.GwtEvent;
1112
import com.google.gwt.event.shared.HasHandlers;
13+
import com.google.gwt.http.client.Response;
14+
import com.google.gwt.user.client.Window.Location;
1215
import com.google.inject.Inject;
1316
import com.google.web.bindery.event.shared.EventBus;
1417
import org.fusesource.restygwt.client.Defaults;
@@ -157,8 +160,19 @@ public TaskExecutorImpl(final HasHandlers hasHandlers,
157160

158161
@Override
159162
public void exec() {
160-
final RestErrorHandler errorHandler = GwtNullSafe
163+
final RestErrorHandler innerErrorHandler = GwtNullSafe
161164
.requireNonNullElseGet(this.errorHandler, () -> new DefaultErrorHandler(hasHandlers, null));
165+
final RestErrorHandler errorHandler = error -> {
166+
final int statusCode = GwtNullSafe
167+
.getOrElse(error, RestError::getMethod, Method::getResponse, Response::getStatusCode, -1);
168+
if (statusCode == Response.SC_UNAUTHORIZED) {
169+
// Reload as we have been logged out.
170+
Console.log(() -> "Unauthorised request, assuming user session is invalid, reloading...");
171+
Location.reload();
172+
} else {
173+
innerErrorHandler.onError(error);
174+
}
175+
};
162176
final TaskMonitorFactory taskMonitorFactory = GwtNullSafe
163177
.requireNonNullElseGet(this.taskMonitorFactory, () -> new DefaultTaskMonitorFactory(hasHandlers));
164178

stroom-security/stroom-security-impl/src/main/java/stroom/security/impl/AuthenticationConfig.java

+20-7
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ public class AuthenticationConfig extends AbstractConfig implements IsStroomConf
1717
public static final String PROP_NAME_OPENID = "openId";
1818
public static final String PROP_NAME_PREVENT_LOGIN = "preventLogin";
1919
public static final String PROP_NAME_API_KEY_CACHE = "apiKeyCache";
20+
public static final String PROP_NAME_AUTHENTICATION_STATE_CACHE = "authenticationStateCache";
2021
public static final String PROP_NAME_MAX_API_KEY_EXPIRY_AGE = "maxApiKeyExpiryAge";
2122

2223
private final CacheConfig apiKeyCache;
24+
private final CacheConfig authenticationStateCache;
2325
private final StroomDuration maxApiKeyExpiryAge;
2426
private final StroomOpenIdConfig openIdConfig;
2527
private final boolean preventLogin;
@@ -29,6 +31,10 @@ public AuthenticationConfig() {
2931
.maximumSize(1_000L)
3032
.expireAfterWrite(StroomDuration.ofSeconds(60))
3133
.build();
34+
authenticationStateCache = CacheConfig.builder()
35+
.maximumSize(1_000L)
36+
.expireAfterWrite(StroomDuration.ofMinutes(10))
37+
.build();
3238
maxApiKeyExpiryAge = StroomDuration.ofDays(365);
3339
openIdConfig = new StroomOpenIdConfig();
3440
preventLogin = false;
@@ -37,11 +43,12 @@ public AuthenticationConfig() {
3743
@JsonCreator
3844
public AuthenticationConfig(
3945
@JsonProperty(PROP_NAME_API_KEY_CACHE) final CacheConfig apiKeyCache,
46+
@JsonProperty(PROP_NAME_AUTHENTICATION_STATE_CACHE) final CacheConfig authenticationStateCache,
4047
@JsonProperty(PROP_NAME_MAX_API_KEY_EXPIRY_AGE) final StroomDuration maxApiKeyExpiryAge,
4148
@JsonProperty(PROP_NAME_OPENID) final StroomOpenIdConfig openIdConfig,
4249
@JsonProperty(PROP_NAME_PREVENT_LOGIN) final boolean preventLogin) {
43-
4450
this.apiKeyCache = apiKeyCache;
51+
this.authenticationStateCache = authenticationStateCache;
4552
this.maxApiKeyExpiryAge = maxApiKeyExpiryAge;
4653
this.openIdConfig = openIdConfig;
4754
this.preventLogin = preventLogin;
@@ -52,6 +59,11 @@ public CacheConfig getApiKeyCache() {
5259
return apiKeyCache;
5360
}
5461

62+
@JsonProperty(PROP_NAME_AUTHENTICATION_STATE_CACHE)
63+
public CacheConfig getAuthenticationStateCache() {
64+
return authenticationStateCache;
65+
}
66+
5567
@JsonProperty(PROP_NAME_MAX_API_KEY_EXPIRY_AGE)
5668
@JsonPropertyDescription("The maximum expiry age for new API keys. Defaults to 365 days.")
5769
@NotNull
@@ -65,7 +77,7 @@ public StroomOpenIdConfig getOpenIdConfig() {
6577
}
6678

6779
@JsonPropertyDescription("Prevent new logins to the system. This is useful if the system is scheduled to " +
68-
"have an outage.")
80+
"have an outage.")
6981
@JsonProperty(PROP_NAME_PREVENT_LOGIN)
7082
public boolean isPreventLogin() {
7183
return preventLogin;
@@ -74,10 +86,11 @@ public boolean isPreventLogin() {
7486
@Override
7587
public String toString() {
7688
return "AuthenticationConfig{" +
77-
"apiKeyCache=" + apiKeyCache +
78-
", maxApiKeyExpiryAge=" + maxApiKeyExpiryAge +
79-
", openIdConfig=" + openIdConfig +
80-
", preventLogin=" + preventLogin +
81-
'}';
89+
"apiKeyCache=" + apiKeyCache +
90+
", authenticationStateCache=" + authenticationStateCache +
91+
", maxApiKeyExpiryAge=" + maxApiKeyExpiryAge +
92+
", openIdConfig=" + openIdConfig +
93+
", preventLogin=" + preventLogin +
94+
'}';
8295
}
8396
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package stroom.security.impl;
2+
3+
import stroom.cache.api.CacheManager;
4+
import stroom.cache.api.StroomCache;
5+
import stroom.security.common.impl.AuthenticationState;
6+
import stroom.util.logging.LambdaLogger;
7+
import stroom.util.logging.LambdaLoggerFactory;
8+
import stroom.util.logging.LogUtil;
9+
10+
import jakarta.inject.Inject;
11+
import jakarta.inject.Provider;
12+
import jakarta.inject.Singleton;
13+
14+
import java.security.SecureRandom;
15+
import java.util.Base64;
16+
import java.util.Optional;
17+
18+
@Singleton
19+
public class AuthenticationStateCache {
20+
21+
private static final LambdaLogger LOGGER = LambdaLoggerFactory.getLogger(AuthenticationStateCache.class);
22+
23+
private final StroomCache<String, AuthenticationState> cache;
24+
25+
@Inject
26+
public AuthenticationStateCache(final Provider<AuthenticationConfig> configProvider,
27+
final CacheManager cacheManager) {
28+
cache = cacheManager.create("Authentication State Cache",
29+
() -> configProvider.get().getAuthenticationStateCache());
30+
}
31+
32+
/**
33+
* A 'state' is a single use, cryptographically random string,
34+
* and it's use here is to prevent replay attacks.
35+
* <p>
36+
* State is used in the authentication flow - the hash is included in the original AuthenticationRequest
37+
* that Stroom makes to the Authentication Service. When Stroom is subsequently called the state is provided in the
38+
* URL to allow verification that the return request was expected.
39+
*/
40+
public AuthenticationState create(final String url,
41+
final boolean prompt) {
42+
final String stateId = createRandomString(20);
43+
final String nonce = createRandomString(20);
44+
45+
final AuthenticationState state = new AuthenticationState(stateId, url, nonce, prompt);
46+
LOGGER.debug(() -> LogUtil.message("Creating {}", state));
47+
48+
cache.put(stateId, state);
49+
return state;
50+
}
51+
52+
@SuppressWarnings("unchecked")
53+
public Optional<AuthenticationState> getAndRemove(final String stateId) {
54+
final Optional<AuthenticationState> optional = cache.getIfPresent(stateId);
55+
if (optional.isPresent()) {
56+
cache.invalidate(stateId);
57+
}
58+
return optional;
59+
}
60+
61+
private String createRandomString(final int length) {
62+
final SecureRandom secureRandom = new SecureRandom();
63+
final byte[] bytes = new byte[length];
64+
secureRandom.nextBytes(bytes);
65+
return Base64.getUrlEncoder().encodeToString(bytes);
66+
}
67+
}

stroom-security/stroom-security-impl/src/main/java/stroom/security/impl/AuthenticationStateSessionUtil.java

-105
This file was deleted.

0 commit comments

Comments
 (0)