Commit f4109efa authored by Jörg Richter's avatar Jörg Richter

WebSocketService: add `sendToReadAllowed()` (#384)

parent e4ed6bfa
Pipeline #10506 passed with stage
in 8 minutes and 7 seconds
......@@ -3,6 +3,7 @@ package systems.dmx.core.impl;
import systems.dmx.core.osgi.CoreActivator;
import systems.dmx.core.service.Cookies;
import systems.dmx.core.service.CoreService;
import systems.dmx.core.service.accesscontrol.Operation;
import systems.dmx.core.service.websocket.WebSocketConnection;
import systems.dmx.core.service.websocket.WebSocketService;
......@@ -58,14 +59,14 @@ public class WebSocketServiceImpl implements WebSocketService {
@Override
public void sendToAllButOrigin(String message) {
// Note: basically copied to Messenger.java (module dmx-topicmaps), including clientId() helper
// TODO: DRY. Provide central factory for the predicate + cookie logic
//
// Note: the predicate is evaluated in another thread (SendMessageWorker). So to read out the client-id
// cookie -- which is stored thread-locally -- we call clientId() from *this* thread (instead from predicate)
// and hold the result in the predicate's closure.
String clientId = clientId();
queueMessage(message, conn -> !conn.getClientId().equals(clientId));
queueMessage(message, isOrigin().negate());
}
@Override
public void sendToReadAllowed(String message, long objectId) {
// don't send back to origin
// only send if receiver has READ permission for object
queueMessage(message, isOrigin().negate().and(isReadAllowed(objectId)));
}
@Override
......@@ -122,6 +123,31 @@ public class WebSocketServiceImpl implements WebSocketService {
worker.queueMessage(message, connectionFilter);
}
// ---
private Predicate<WebSocketConnection> isOrigin() {
// Note: the returned predicate is evaluated in another thread (SendMessageWorker). So to read out the client-id
// cookie -- which is stored thread-locally -- we call clientId() from *this* thread (instead from predicate)
// and hold the result in the predicate's closure.
String clientId = clientId();
return conn -> {
boolean isOrigin = conn.getClientId().equals(clientId);
if (isOrigin) {
logger.info(conn.getClientId() + " " + conn.getUsername() + " (origin) -> " + false);
}
return isOrigin;
};
}
private Predicate<WebSocketConnection> isReadAllowed(long objectId) {
return conn -> {
boolean isReadAllowed = dmx.getPrivilegedAccess().hasPermission(conn.getUsername(), Operation.READ, objectId);
logger.info(conn.getClientId() + " " + conn.getUsername() + " -> " + isReadAllowed);
return isReadAllowed;
};
}
// ---
/**
* @return the WebSocket connection that is associated with the current request (based on "dmx_client_id" cookie),
......
......@@ -12,6 +12,8 @@ public interface WebSocketService {
void sendToAllButOrigin(String message);
void sendToReadAllowed(String message, long objectId);
void sendToSome(String message, Predicate<WebSocketConnection> connectionFilter);
// ---
......
......@@ -49,7 +49,7 @@ export default ({store}) => {
const topicModel = new dm5.Topic(topicType.newTopicModel(value)).fillChildren()
// console.log('createTopic', topicModel)
dm5.restClient.createTopic(topicModel).then(topic => {
console.log('Created', topic)
// console.log('Created', topic)
revealTopic(topic)
store.dispatch('_processDirectives', topic.directives)
})
......
......@@ -3,9 +3,7 @@ package systems.dmx.topicmaps;
import systems.dmx.core.Topic;
import systems.dmx.core.model.topicmaps.ViewAssoc;
import systems.dmx.core.model.topicmaps.ViewTopic;
import systems.dmx.core.service.Cookies;
import systems.dmx.core.service.CoreService;
import systems.dmx.core.service.accesscontrol.Operation;
import systems.dmx.core.service.websocket.WebSocketService;
import org.codehaus.jettison.json.JSONObject;
......@@ -22,14 +20,14 @@ class Messenger {
// ---------------------------------------------------------------------------------------------- Instance Variables
private CoreService dmx;
private WebSocketService wss;
private Logger logger = Logger.getLogger(getClass().getName());
// ---------------------------------------------------------------------------------------------------- Constructors
Messenger(CoreService dmx) {
this.dmx = dmx;
Messenger(WebSocketService wss) {
this.wss = wss;
}
// ----------------------------------------------------------------------------------------- Package Private Methods
......@@ -50,7 +48,7 @@ class Messenger {
void addTopicToTopicmap(long topicmapId, ViewTopic topic) {
try {
sendToAuthorized(new JSONObject()
sendToReadAllowed(new JSONObject()
.put("type", "addTopicToTopicmap")
.put("args", new JSONObject()
.put("topicmapId", topicmapId)
......@@ -64,7 +62,7 @@ class Messenger {
void addAssocToTopicmap(long topicmapId, ViewAssoc assoc) {
try {
sendToAuthorized(new JSONObject()
sendToReadAllowed(new JSONObject()
.put("type", "addAssocToTopicmap")
.put("args", new JSONObject()
.put("topicmapId", topicmapId)
......@@ -127,33 +125,10 @@ class Messenger {
// ------------------------------------------------------------------------------------------------- Private Methods
private void sendToAllButOrigin(JSONObject message) {
dmx.getWebSocketService().sendToAllButOrigin(message.toString());
wss.sendToAllButOrigin(message.toString());
}
private void sendToAuthorized(JSONObject message, long objectId) {
// Note: the predicate is evaluated in another thread (WebSocketService's SendMessageWorker). So to read out
// the client-id cookie -- which is stored thread-locally -- we call clientId() from *this* thread (instead
// from predicate) and hold the result in the predicate's closure.
String clientId = clientId();
//
dmx.getWebSocketService().sendToSome(message.toString(), conn -> {
// don't send back to origin
boolean isOrigin = conn.getClientId().equals(clientId);
if (isOrigin) {
logger.info(conn.getClientId() + " " + conn.getUsername() + " (origin) -> " + false);
return false;
}
// only send if receiver is authorized
boolean isReadable = dmx.getPrivilegedAccess().hasPermission(conn.getUsername(), Operation.READ, objectId);
logger.info(conn.getClientId() + " " + conn.getUsername() + " -> " + isReadable);
return isReadable;
});
}
// ---
private String clientId() {
Cookies cookies = Cookies.get();
return cookies.has("dmx_client_id") ? cookies.get("dmx_client_id") : null;
private void sendToReadAllowed(JSONObject message, long objectId) {
wss.sendToReadAllowed(message.toString(), objectId);
}
}
......@@ -13,7 +13,6 @@ import systems.dmx.core.model.topicmaps.ViewAssoc;
import systems.dmx.core.model.topicmaps.ViewTopic;
import systems.dmx.core.model.topicmaps.ViewProps;
import systems.dmx.core.osgi.PluginActivator;
import systems.dmx.core.service.CoreService;
import systems.dmx.core.service.Transactional;
import systems.dmx.core.util.DMXUtils;
import systems.dmx.core.util.IdList;
......@@ -423,7 +422,7 @@ public class TopicmapsPlugin extends PluginActivator implements TopicmapsService
@Override
public void init() {
me = new Messenger(dmx);
me = new Messenger(dmx.getWebSocketService());
}
......
......@@ -277,9 +277,9 @@ const actions = {
player1: {roleTypeUri: 'dmx.core.default', ...playerId1},
player2: {roleTypeUri: 'dmx.core.default', ...playerId2}
}
console.log('createAssoc', assocModel)
// console.log('createAssoc', assocModel)
dm5.restClient.createAssoc(assocModel).then(assoc => {
console.log('Created', assoc)
// console.log('Created', assoc)
dispatch('revealAssoc', {assoc})
dispatch('_processDirectives', assoc.directives)
})
......
......@@ -3,27 +3,27 @@ import dm5 from 'dm5'
const actions = {
createTopicType ({dispatch}, {name, pos}) {
console.log('Creating topic type', name)
// console.log('Creating topic type', name)
dm5.restClient.createTopicType(defaultTopicType(name)).then(topicType => {
console.log('Created', topicType)
// console.log('Created', topicType)
dispatch('putTopicType', topicType)
dispatch('revealTopic', {topic: topicType, pos})
})
},
createAssocType ({dispatch}, {name, pos}) {
console.log('Creating assoc type', name)
// console.log('Creating assoc type', name)
dm5.restClient.createAssocType(defaultAssocType(name)).then(assocType => {
console.log('Created', assocType)
// console.log('Created', assocType)
dispatch('putAssocType', assocType)
dispatch('revealTopic', {topic: assocType, pos})
})
},
createRoleType ({dispatch}, {name, pos}) {
console.log('Creating role type', name)
// console.log('Creating role type', name)
dm5.restClient.createRoleType(defaultRoleType(name)).then(roleType => {
console.log('Created', roleType)
// console.log('Created', roleType)
dispatch('putRoleType', roleType)
dispatch('revealTopic', {topic: roleType, pos})
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment