From e2155bb9c08209d68ee0245b0bfc2ed64bd51aa9 Mon Sep 17 00:00:00 2001 From: Daniel Gerhardt <code@dgerhardt.net> Date: Mon, 28 Aug 2017 13:02:29 +0200 Subject: [PATCH] Add workaround to allow access from WebSocket server to service layer Unfortunately, Spring Security cannot easily be used with our WebSocket implementation since connections share a Thread and therefore share a SecurityContext. An AuthenticationToken for WebSocket access to the service layer is created. --- .../ApplicationPermissionEvaluator.java | 12 +++++++----- .../websocket/ArsnovaSocketioServerImpl.java | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java index e5d350666..1bb5faf5c 100644 --- a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java +++ b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java @@ -21,7 +21,6 @@ import de.thm.arsnova.entities.Comment; import de.thm.arsnova.entities.Content; import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.User; -import de.thm.arsnova.exceptions.UnauthorizedException; import de.thm.arsnova.persistance.CommentRepository; import de.thm.arsnova.persistance.ContentRepository; import de.thm.arsnova.persistance.SessionRepository; @@ -113,8 +112,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { case "read": return targetSession.isActive(); case "create": - /* There are currently no limitations on session creation. */ - return true; + return !username.isEmpty(); case "owner": case "update": case "delete": @@ -148,7 +146,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { final String permission) { switch (permission) { case "create": - return sessionRepository.findOne(targetComment.getSessionId()).isActive(); + return !username.isEmpty() && sessionRepository.findOne(targetComment.getSessionId()).isActive(); case "owner": case "update": return targetComment.getCreator() != null && targetComment.getCreator().equals(username); @@ -174,7 +172,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { private String getUsername(final Authentication authentication) { if (authentication == null || authentication instanceof AnonymousAuthenticationToken) { - throw new UnauthorizedException(); + return ""; } if (authentication instanceof Pac4jAuthenticationToken) { @@ -199,4 +197,8 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { return authentication.getName(); } + + private boolean isWebsocketAccess(Authentication auth) { + return auth instanceof AnonymousAuthenticationToken && auth.getAuthorities().contains("ROLE_WEBSOCKET_ACCESS"); + } } diff --git a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java index 0b5a27cb7..a473408f0 100644 --- a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java +++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java @@ -48,6 +48,11 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required; import org.springframework.scheduling.annotation.Async; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import javax.annotation.PreDestroy; @@ -91,6 +96,7 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova private String storepass; private final Configuration config; private SocketIOServer server; + private boolean securityInitialized; public ArsnovaSocketioServerImpl() { config = new Configuration(); @@ -235,7 +241,9 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova @Override @Timed public void onConnect(final SocketIOClient client) { - /* No implementation - only used for monitoring */ + if (!securityInitialized) { + initializeSecurity(); + } } }); @@ -678,4 +686,13 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer, Arsnova @Override public void visit(DeleteSessionEvent event) { } + + private void initializeSecurity() { + Authentication auth = new AnonymousAuthenticationToken("websocket", "websocket", + AuthorityUtils.createAuthorityList("ROLE_WEBSOCKET_ACCESS")); + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(auth); + SecurityContextHolder.setContext(context); + securityInitialized = true; + } } -- GitLab