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