From b999f3351364fbeebdef908f50af5c447cbda6e4 Mon Sep 17 00:00:00 2001 From: Chris Malloy Date: Wed, 17 Jan 2024 05:23:00 -0400 Subject: [PATCH] Require origin in messages channels for authentication --- .../jasper/component/MessagesImplStomp.java | 17 ++++++++++++++--- src/main/java/jasper/domain/proj/HasOrigin.java | 15 +++++++++++++++ src/main/java/jasper/security/Auth.java | 15 ++++++++++----- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/jasper/component/MessagesImplStomp.java b/src/main/java/jasper/component/MessagesImplStomp.java index 3a6ab2b2..eb49c589 100644 --- a/src/main/java/jasper/component/MessagesImplStomp.java +++ b/src/main/java/jasper/component/MessagesImplStomp.java @@ -10,6 +10,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import static jasper.domain.proj.HasOrigin.originHierarchy; import static org.apache.commons.lang3.StringUtils.isNotBlank; @Profile("!no-websocket") @@ -25,18 +26,28 @@ public class MessagesImplStomp implements Messages { public void updateRef(Ref ref) { // TODO: Debounce var encodedUrl = URLEncoder.encode(ref.getUrl(), StandardCharsets.UTF_8); - stomp.convertAndSend("/topic/ref/" + (isNotBlank(ref.getOrigin()) ? ref.getOrigin() : "default") + "/" + encodedUrl, mapper.domainToUpdateDto(ref)); + var origins = originHierarchy(ref.getOrigin()); + for (var o : origins) { + var origin = isNotBlank(o) ? o : "default"; + stomp.convertAndSend("/topic/ref/" + origin + "/" + encodedUrl, mapper.domainToUpdateDto(ref)); + } if (ref.getTags() != null){ ref.addHierarchicalTags(); for (var tag : ref.getTags()) { var encodedTag = URLEncoder.encode(tag, StandardCharsets.UTF_8); - stomp.convertAndSend("/topic/tag/" + encodedTag, tag); + for (var o : origins) { + var origin = isNotBlank(o) ? o : "default"; + stomp.convertAndSend("/topic/tag/" + origin + "/" + encodedTag, tag); + } } } if (ref.getSources() != null) for (var source : ref.getSources()) { var encodedSource = URLEncoder.encode(source, StandardCharsets.UTF_8); - stomp.convertAndSend("/topic/response/" + encodedSource, ref.getUrl()); + for (var o : origins) { + var origin = isNotBlank(o) ? o : "default"; + stomp.convertAndSend("/topic/response/" + origin + "/" + encodedSource, ref.getUrl()); + } } } } diff --git a/src/main/java/jasper/domain/proj/HasOrigin.java b/src/main/java/jasper/domain/proj/HasOrigin.java index 02400e83..b1880353 100644 --- a/src/main/java/jasper/domain/proj/HasOrigin.java +++ b/src/main/java/jasper/domain/proj/HasOrigin.java @@ -1,5 +1,8 @@ package jasper.domain.proj; +import java.util.ArrayList; +import java.util.List; + import static org.apache.commons.lang3.StringUtils.isBlank; public interface HasOrigin { @@ -28,4 +31,16 @@ static String subOrigin(String local, String origin) { if (origin.startsWith("@")) origin = origin.substring(1); return local + '.' + origin; } + + static List originHierarchy(String origin) { + if (isBlank(origin)) return List.of(""); + var result = new ArrayList(); + result.add(origin); + while (origin.contains(".")) { + origin = origin.substring(0, origin.lastIndexOf(".")); + result.add(origin); + } + result.add(""); + return result; + } } diff --git a/src/main/java/jasper/security/Auth.java b/src/main/java/jasper/security/Auth.java index 3d240afa..fcf7e4cc 100644 --- a/src/main/java/jasper/security/Auth.java +++ b/src/main/java/jasper/security/Auth.java @@ -192,8 +192,8 @@ public boolean local(String origin) { * Is this origin a sub-origin. */ public boolean subOrigin(String origin) { - if (isBlank(origin)) return false; if (isBlank(getOrigin())) return true; + if (local(origin)) return true; return origin.startsWith(getOrigin()+"."); } @@ -204,7 +204,6 @@ public boolean subOrigin(String origin) { */ public boolean tenantAccess(String origin) { if (!props.isMultiTenant()) return true; - if (local(origin)) return true; if (subOrigin(origin)) return true; if (getClient().getTenantAccess() == null) return false; for (var t : getClient().getTenantAccess()) { @@ -320,9 +319,12 @@ public boolean canSubscribeTo(String destination) { if (!minRole()) return false; if (destination == null) return false; if (destination.startsWith("/topic/tag/")) { - var tag = destination.substring("/topic/tag/".length()); + var topic = destination.substring("/topic/ref/".length()); + var origin = topic.substring(0, topic.indexOf('/')); + if (origin.equals("default")) origin = ""; + var tag = topic.substring(topic.indexOf('/') + 1); var decodedTag = URLDecoder.decode(tag, StandardCharsets.UTF_8); - return canReadTag(decodedTag); + return canReadTag(decodedTag + origin); } else if (destination.startsWith("/topic/ref/")) { var topic = destination.substring("/topic/ref/".length()); var origin = topic.substring(0, topic.indexOf('/')); @@ -331,7 +333,10 @@ public boolean canSubscribeTo(String destination) { var decodedUrl = URLDecoder.decode(url, StandardCharsets.UTF_8); return canReadRef(decodedUrl, origin); } else if (destination.startsWith("/topic/response/")) { - return true; + var topic = destination.substring("/topic/response/".length()); + var origin = topic.substring(0, topic.indexOf('/')); + if (origin.equals("default")) origin = ""; + return subOrigin(origin); } return false; }