Skip to content

Commit d4171fd

Browse files
committed
Cache Access Controls
1 parent 88b2254 commit d4171fd

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

src/main/java/eu/openanalytics/containerproxy/service/AccessControlService.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,46 @@
2424
import eu.openanalytics.containerproxy.model.spec.ProxyAccessControl;
2525
import eu.openanalytics.containerproxy.model.spec.ProxySpec;
2626
import eu.openanalytics.containerproxy.spec.IProxySpecProvider;
27+
import eu.openanalytics.containerproxy.spec.expression.SpecExpressionContext;
28+
import eu.openanalytics.containerproxy.spec.expression.SpecExpressionResolver;
29+
import org.apache.commons.lang3.tuple.Pair;
2730
import org.springframework.context.annotation.Lazy;
31+
import org.springframework.context.event.EventListener;
2832
import org.springframework.security.authentication.AnonymousAuthenticationToken;
2933
import org.springframework.security.core.Authentication;
34+
import org.springframework.security.web.session.HttpSessionDestroyedEvent;
3035
import org.springframework.stereotype.Service;
36+
import org.springframework.web.context.request.RequestAttributes;
37+
import org.springframework.web.context.request.RequestContextHolder;
38+
39+
import java.util.Map;
40+
import java.util.Optional;
41+
import java.util.concurrent.ConcurrentHashMap;
3142

3243
@Service
3344
public class AccessControlService {
3445

3546
private final IAuthenticationBackend authBackend;
3647
private final UserService userService;
3748
private final IProxySpecProvider specProvider;
49+
private final SpecExpressionResolver specExpressionResolver;
50+
51+
/* This map is used to cache whether a user has access to an app or not.
52+
* The reason is two-fold:
53+
* - for every request made (including static files of apps etc) the access control is checked
54+
* - when using the `access-expression` feature, checking the access control means evaluating a SpEL expression
55+
* I.e. the check can be complex and is performed a lot.
56+
* This cache uses the SessionId of the user and not the userId for two reasons:
57+
* - this ensures that the key is unique
58+
* - the roles/properties of a user change when they re-login
59+
*/
60+
private final Map<Pair<String, String>, Boolean> authorizationCache = new ConcurrentHashMap<>();
3861

39-
public AccessControlService(@Lazy IAuthenticationBackend authBackend, UserService userService, IProxySpecProvider specProvider) {
62+
public AccessControlService(@Lazy IAuthenticationBackend authBackend, UserService userService, IProxySpecProvider specProvider, SpecExpressionResolver specExpressionResolver) {
4063
this.authBackend = authBackend;
4164
this.userService = userService;
4265
this.specProvider = specProvider;
66+
this.specExpressionResolver = specExpressionResolver;
4367
}
4468

4569
public boolean canAccess(Authentication auth, String specId) {
@@ -140,5 +164,10 @@ public boolean allowedByExpression(Authentication auth, ProxySpec spec) {
140164
return specExpressionResolver.evaluateToBoolean(spec.getAccessControl().getExpression(), context);
141165
}
142166

167+
@EventListener
168+
public void onSessionDestroyedEvent(HttpSessionDestroyedEvent event) {
169+
// remove all entries in cache for this sessionId
170+
authorizationCache.keySet().removeIf(it -> it.getLeft().equals(event.getId()));
171+
}
143172

144173
}

0 commit comments

Comments
 (0)