diff --git a/core-common/src/main/java/eu/arrowhead/common/CommonConstants.java b/core-common/src/main/java/eu/arrowhead/common/CommonConstants.java
index 45456f73d..cd492653b 100644
--- a/core-common/src/main/java/eu/arrowhead/common/CommonConstants.java
+++ b/core-common/src/main/java/eu/arrowhead/common/CommonConstants.java
@@ -138,6 +138,8 @@ public class CommonConstants {
 	public static final String CBOR = "CBOR";
 	public static final String HTTP_SECURE_JSON = HTTP + "-SECURE-" + JSON; 
 	public static final String HTTP_INSECURE_JSON = HTTP + "-INSECURE-" + JSON;
+	public static final String WS_SECURE_JSON = WS + "-SECURE-" + JSON;
+	public static final String WS_INSECURE_JSON = WS + "-INSECURE-" + JSON;
 	public static final String UNKNOWN_ORIGIN = "<unknown>";
 	
 	public static final String SERVICE_REGISTRY_ADDRESS = "sr_address";
diff --git a/core-common/src/main/java/eu/arrowhead/common/SecurityUtilities.java b/core-common/src/main/java/eu/arrowhead/common/SecurityUtilities.java
index 5d6d3da6f..8333a0040 100644
--- a/core-common/src/main/java/eu/arrowhead/common/SecurityUtilities.java
+++ b/core-common/src/main/java/eu/arrowhead/common/SecurityUtilities.java
@@ -65,6 +65,7 @@
 import org.springframework.util.Assert;
 import org.springframework.util.Base64Utils;
 import org.springframework.util.StringUtils;
+import org.springframework.http.server.ServletServerHttpRequest;
 
 @Component
 public class SecurityUtilities {
@@ -109,6 +110,14 @@ public static String getCertificateCNFromRequest(final HttpServletRequest reques
         return null;
     }
 
+    //-------------------------------------------------------------------------------------------------
+    public static String getCertificateCNFromServerRequest(final ServletServerHttpRequest request) {
+        Assert.notNull(request, "request must not be null");
+        final HttpServletRequest servletRequest = request.getServletRequest();
+
+        return getCertificateCNFromRequest(servletRequest);
+    }
+
     //-------------------------------------------------------------------------------------------------
     private static String getCommonNameFromCloudCertificate(final Principal principal) {
         Assert.notNull(principal, "Principal must not be null");
diff --git a/datamanager/pom.xml b/datamanager/pom.xml
index 9b1dcd16b..6653ba9fd 100644
--- a/datamanager/pom.xml
+++ b/datamanager/pom.xml
@@ -40,6 +40,7 @@
 		                  			<includes>
 		                  				<include>application.properties</include>
 		                  				<include>log4j2.xml</include>
+		                  				<include>accesslist.acl</include>
 		                  			</includes>
 		                		</resource>
 		              		</resources>
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerController.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerController.java
index 63b59f646..476316668 100644
--- a/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerController.java
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerController.java
@@ -5,11 +5,11 @@
  * terms of the Eclipse Public License 2.0 which is available at
  * http://www.eclipse.org/legal/epl-2.0.
  *
- * SPDX-License-Identifier: EPL-2.0 
+ * SPDX-License-Identifier: EPL-2.0
  *
- * Contributors: 
+ * Contributors:
  *   {Lulea University of Technology} - implementation
- *   Arrowhead Consortia - conceptualization 
+ *   Arrowhead Consortia - conceptualization
  ********************************************************************************/
 package eu.arrowhead.core.datamanager;
 
@@ -59,6 +59,7 @@
 import eu.arrowhead.common.exception.DataNotFoundException;
 import eu.arrowhead.common.exception.InvalidParameterException;
 import eu.arrowhead.core.datamanager.database.service.DataManagerDBService;
+import eu.arrowhead.core.datamanager.service.DataManagerDriver;
 import eu.arrowhead.core.datamanager.service.ProxyService;
 import eu.arrowhead.core.datamanager.service.ProxyElement;
 import eu.arrowhead.core.datamanager.service.HistorianService;
@@ -90,12 +91,15 @@ public class DataManagerController {
 	@Autowired
 	private HistorianService historianService;
 
+	@Autowired
+	private DataManagerDriver dataManagerDriver;
+
 	@Autowired
 	private DataManagerDBService dataManagerDBService;
-	
+
 	//=================================================================================================
 	// methods
-	
+
 	//-------------------------------------------------------------------------------------------------
 	@ApiOperation(value = "Return an echo message with the purpose of testing the core service availability", response = String.class, tags = { CoreCommonConstants.SWAGGER_TAG_CLIENT })
 	@ApiResponses (value = {
@@ -167,9 +171,9 @@ public class DataManagerController {
 		@RequestParam MultiValueMap<String, String> params
 		) {
 
-    if(Utilities.isEmpty(systemName) || Utilities.isEmpty(serviceName)) {
-      throw new InvalidParameterException(OP_NOT_VALID_ERROR_MESSAGE, HttpStatus.SC_BAD_REQUEST, CommonConstants.OP_DATAMANAGER_HISTORIAN);
-    }
+    		if(Utilities.isEmpty(systemName) || Utilities.isEmpty(serviceName)) {
+		      throw new InvalidParameterException(OP_NOT_VALID_ERROR_MESSAGE, HttpStatus.SC_BAD_REQUEST, CommonConstants.OP_DATAMANAGER_HISTORIAN);
+		}
 		logger.debug("historianServiceGet for {}/{}", systemName, serviceName);
 
 		double from=-1, to=-1;
@@ -256,7 +260,7 @@ public class DataManagerController {
         }
 		logger.debug("historianServicePut for {}/{}", systemName, serviceName);
 
-		validateSenMLMessage(systemName, serviceName, message);
+		dataManagerDriver.validateSenMLMessage(systemName, serviceName, message);
 
 		historianService.createEndpoint(systemName, serviceName);
 
@@ -265,7 +269,7 @@ public class DataManagerController {
 			head.setBt((double)System.currentTimeMillis() / 1000);
 		}
 
-		validateSenMLContent(message);
+		dataManagerDriver.validateSenMLContent(message);
 
 		final boolean statusCode = historianService.updateEndpoint(systemName, serviceName, message);
 		if (!statusCode) {
@@ -362,14 +366,14 @@ public class DataManagerController {
             }
 			logger.debug("proxyServicePut for {}/{}", systemName, serviceName);
 
-			validateSenMLMessage(systemName, serviceName, message);
+			dataManagerDriver.validateSenMLMessage(systemName, serviceName, message);
 
 			SenML head = message.firstElement();
 			if(head.getBt() == null) {
 				head.setBt((double)System.currentTimeMillis() / 1000);
 			}
 
-			validateSenMLContent(message);
+			dataManagerDriver.validateSenMLContent(message);
 
 			final ProxyElement pe = proxyService.getEndpointFromService(systemName, serviceName);
 			if (pe == null) {
@@ -388,82 +392,4 @@ public class DataManagerController {
 	//=================================================================================================
 	// assistant methods
 	
-	
-	//=================================================================================================
-  private void validateSenMLMessage(String systemName, String serviceName, Vector<SenML> message) {
-	try {
-    	Assert.notNull(systemName, "systemName is null.");
-    	Assert.notNull(serviceName, "serviceName is null.");
-    	Assert.notNull(message, "message is null.");
-    	Assert.isTrue(!message.isEmpty(), "message is empty");
-
-    	SenML head = (SenML)message.get(0);
-		Assert.notNull(head.getBn(), "bn is null.");
-	} catch(Exception e){
-		throw new BadPayloadException("Missing mandatory field");
-	}
-  }
-
-  //-------------------------------------------------------------------------------------------------
-  public void validateSenMLContent(final Vector<SenML> message) {
-	try {
-
-    	/* check that bn, bt and bu are included only once, and in the first object */
-    	Iterator<SenML> entry = message.iterator();
-    	int bnc=0, btc=0, buc=0;
-    	while (entry.hasNext()) {
-      		SenML element = entry.next();
-      		if (element.getBn() != null) {
-        		bnc++;
-    	  	}
-      		if (element.getBt() != null) {
-        		btc++;
-      		}
-      		if (element.getBu() != null) {
-        		buc++;
-      		}
-    	}
-
-    	/* bu can only exist once. bt can only exist one, bu can exist 0 or 1 times */
-    	Assert.isTrue(!(bnc != 1 || btc != 1 || buc > 1), "invalid bn/bt/bu");
-
-    	/* bn must exist in [0] */
-    	SenML element = (SenML)message.get(0);
-    	Assert.notNull(element.getBn(), "bn is missing");
-
-    	/* bt must exist in [0] */
-    	Assert.notNull(element.getBt(), "bt is missing");
-
-    	/* bt cannot be negative */
-    	Assert.isTrue(element.getBt() >= 0.0, "a negative base time is not allowed");
-
-    	/* bu must exist in [0], if it exists */
-    	Assert.isTrue(!(element.getBu() == null && buc == 1), "invalid use of bu");
-
-    	/* check that v, bv, sv, etc are included only once per object */
-    	entry = message.iterator();
-    	while (entry.hasNext()) {
-      		element = (SenML)entry.next();
-
-      		int valueCount = 0;
-      		if (element.getV() != null) {
-        		valueCount++;
-      		}
-      		if (element.getVs() != null) {
-        		valueCount++;
-      		}
-      		if (element.getVd() != null) {
-        		valueCount++;
-      		}
-      		if (element.getVb() != null) {
-        		valueCount++;
-			}
-
-      		Assert.isTrue(!(valueCount > 1 && element.getS() == null), "too many value tags");
-		}
-	} catch(Exception e) {
-		throw new BadPayloadException("Illegal request");
-	}
-
-  }	
 }
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerWSConfig.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerWSConfig.java
new file mode 100644
index 000000000..efb22501e
--- /dev/null
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/DataManagerWSConfig.java
@@ -0,0 +1,108 @@
+/********************************************************************************
+ * Copyright (c) 2021 {Lulea University of Technology}
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 
+ *
+ * Contributors: 
+ *   {Lulea University of Technology} - implementation
+ *   Arrowhead Consortia - conceptualization 
+ ********************************************************************************/
+package eu.arrowhead.core.datamanager;
+
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import eu.arrowhead.common.SecurityUtilities;
+import eu.arrowhead.common.CommonConstants;
+import eu.arrowhead.core.datamanager.HistorianWSHandler;
+import eu.arrowhead.core.datamanager.security.DatamanagerACLFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.server.HandshakeInterceptor;
+
+ 
+@Configuration
+@EnableWebSocket
+public class DataManagerWSConfig implements WebSocketConfigurer {
+ 
+    private final Logger logger = LogManager.getLogger(DataManagerWSConfig.class);
+
+    @Value("${server.ssl.enabled}")
+    private boolean sslEnabled;
+
+    @Value("${websockets.enabled}")
+    private boolean websocketsEnabled;
+
+    @Autowired
+    HistorianWSHandler historianWSHandler;
+
+    @Autowired
+    DatamanagerACLFilter dmACLFilter;
+
+    @Override
+    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
+	    if (websocketsEnabled) {
+            logger.info("WebSocket support enabled");
+            webSocketHandlerRegistry.addHandler(historianWSHandler, CommonConstants.DATAMANAGER_URI + CommonConstants.OP_DATAMANAGER_HISTORIAN + "/ws/*/*").addInterceptors(auctionInterceptor());
+	    } else {
+            logger.info("WebSocket support disabled");
+        }
+    }
+
+    @Bean
+    public HandshakeInterceptor auctionInterceptor() {
+        return new HandshakeInterceptor() {
+            public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, 
+                WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
+
+                String path = request.getURI().getPath();
+                final String serviceId = path.substring(path.lastIndexOf('/') + 1);
+		        path = path.substring(0, path.lastIndexOf('/'));
+                final String systemId = path.substring(path.lastIndexOf('/') + 1);
+                String CN = null;
+
+                // if running in secure mode, check authorization (ACL)
+                if(sslEnabled) {  
+                    if (request instanceof org.springframework.http.server.ServletServerHttpRequest) {
+                        CN = SecurityUtilities.getCertificateCNFromServerRequest((ServletServerHttpRequest)request);
+                        if(CN == null) {
+                            logger.info("No valid client certificate found");
+                            return false;
+                        }
+                        attributes.put("CN", CN);
+                    } else {
+                        return false;
+                    }
+                }
+
+                // Add to the websocket session
+                attributes.put("systemId", systemId);
+                attributes.put("serviceId", serviceId);
+                
+                return true;
+            }
+
+            public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
+                // tbd
+            }
+
+        };
+    }
+}
+
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/HistorianWSHandler.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/HistorianWSHandler.java
new file mode 100644
index 000000000..4670531ba
--- /dev/null
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/HistorianWSHandler.java
@@ -0,0 +1,133 @@
+/********************************************************************************
+ * Copyright (c) 2021 {Lulea University of Technology}
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 
+ *
+ * Contributors: 
+ *   {Lulea University of Technology} - implementation
+ *   Arrowhead Consortia - conceptualization 
+ ********************************************************************************/
+package eu.arrowhead.core.datamanager;
+ 
+import java.io.IOException;
+import java.util.List;
+import java.util.Vector;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+
+import eu.arrowhead.common.CommonConstants;
+import eu.arrowhead.core.datamanager.service.HistorianService;
+import eu.arrowhead.core.datamanager.security.DatamanagerACLFilter;
+import eu.arrowhead.common.Utilities;
+import eu.arrowhead.core.datamanager.service.DataManagerDriver;
+import eu.arrowhead.core.datamanager.security.DatamanagerACLFilter;
+import eu.arrowhead.common.dto.shared.SenML;
+
+import com.google.gson.*;
+import com.google.gson.reflect.TypeToken;
+
+@Component
+public class HistorianWSHandler extends TextWebSocketHandler {
+ 
+    private final Logger logger = LogManager.getLogger(HistorianWSHandler.class);
+    private Gson gson = new Gson();
+    
+    private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
+ 
+    @Value("${server.ssl.enabled}")
+    private boolean sslEnabled;
+
+    @Autowired
+    private HistorianService historianService;
+
+    @Autowired
+    private DataManagerDriver dataManagerDriver;
+
+    @Autowired
+    DatamanagerACLFilter dataManagerACLFilter;
+
+    //=================================================================================================
+    // methods
+
+    @Override
+    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
+        sessions.add(session);
+        super.afterConnectionEstablished(session);
+    }
+ 
+    @Override
+    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
+        sessions.remove(session);
+        super.afterConnectionClosed(session, status);
+    }
+ 
+    @Override
+    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
+        super.handleTextMessage(session, message);
+        
+        Map<String,Object> attributes = session.getAttributes();
+	    String systemName, serviceName, payload;
+
+	    try {
+		    systemName = (String)session.getAttributes().get("systemId");
+		    serviceName = (String)session.getAttributes().get("serviceId");
+            String CN = systemName;
+            if (sslEnabled ) {
+                CN = (String)attributes.get("CN");
+            }
+            
+            payload = message.getPayload();
+
+		    //logger.debug("Got message for {}/{}", systemName, serviceName);
+            boolean authorized = dataManagerACLFilter.checkRequest(CN, "PUT", CommonConstants.DATAMANAGER_URI + CommonConstants.OP_DATAMANAGER_HISTORIAN + "/ws/" + attributes.get("systemId") + "/" + attributes.get("serviceId"));
+            if(authorized) {
+                Vector<SenML> sml = gson.fromJson(payload, new TypeToken<Vector<SenML>>(){}.getType());
+		        dataManagerDriver.validateSenMLMessage(systemName, serviceName, sml);
+
+		        SenML head = sml.firstElement();
+		        if(head.getBt() == null) {
+			        head.setBt((double)System.currentTimeMillis() / 1000);
+		        } else {
+                    double deltaTime = ((double)System.currentTimeMillis() / 1000) - head.getBt();
+                    deltaTime *= 1000.0;
+                    //logger.info("Message took {} ms ", String.format("%.3f", deltaTime));
+                }
+
+		        dataManagerDriver.validateSenMLContent(sml);
+                historianService.createEndpoint(systemName, serviceName);
+    		    historianService.updateEndpoint(systemName, serviceName, sml);
+
+            } else {
+		        session.close();
+            }
+	    } catch(Exception e) {
+		    session.close();
+		    return;
+
+	    }
+
+	    //logger.debug("Incoming msg: \n" + payload + "\n from " + systemName + "/" + serviceName); //TODO: decide if EventHandler like forwaridng should be added!
+        /*sessions.forEach(webSocketSession -> {
+            try {
+                webSocketSession.sendMessage(message); //XXX: only send to sessions that are connected to the system+service combo!!
+            } catch (IOException e) {
+                logger.error("Error occurred.", e);
+            }
+        });*/
+    }
+}
+
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/database/service/DataManagerDBService.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/database/service/DataManagerDBService.java
index fe3af1960..f2a9cc154 100644
--- a/datamanager/src/main/java/eu/arrowhead/core/datamanager/database/service/DataManagerDBService.java
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/database/service/DataManagerDBService.java
@@ -65,40 +65,39 @@ public class DataManagerDBService {
 
 	//-------------------------------------------------------------------------------------------------
 	private Connection getConnection() throws SQLException {
-	  return DriverManager.getConnection(url, user, password);
+		return DriverManager.getConnection(url, user, password);
 	}
 
 
 	//-------------------------------------------------------------------------------------------------
 	private void closeConnection(final Connection conn) throws SQLException {
-	  conn.close();
+		conn.close();
 	}
 
 	//-------------------------------------------------------------------------------------------------
 	private int serviceToID(final String systemName, final String serviceName, final Connection conn) {
-    logger.debug("serviceToID for {}/{}", systemName, serviceName);
-
-	  int id=-1;
-
-	  PreparedStatement stmt;
-	  try {
-	    String sql = "SELECT id FROM dmhist_services WHERE system_name=? AND service_name=? LIMIT 1;";
-	    stmt = conn.prepareStatement(sql);
-	    stmt.setString(1, systemName);
-	    stmt.setString(2, serviceName);
-	    ResultSet rs = stmt.executeQuery();
-
-	    rs.next();
-	    id  = rs.getInt("id");
-
-	    rs.close();
-	    stmt.close();
-	  } catch(Exception e) {
-      logger.debug("serviceToID: " + e.toString());
-	    id = -1;
-	  }
-
-	  return id;
+    	logger.debug("serviceToID for {}/{}", systemName, serviceName);
+
+		int id=-1;
+
+		PreparedStatement stmt;
+		try {
+	    	String sql = "SELECT id FROM dmhist_services WHERE system_name=? AND service_name=? LIMIT 1;";
+	    	stmt = conn.prepareStatement(sql);
+	    	stmt.setString(1, systemName);
+	    	stmt.setString(2, serviceName);
+	    	ResultSet rs = stmt.executeQuery();
+	    	rs.next();
+	    	id  = rs.getInt("id");
+
+	    	rs.close();
+	    	stmt.close();
+	  	} catch(Exception e) {
+	      	logger.debug("serviceToID: " + e.toString());
+		    id = -1;
+	  	}
+
+		return id;
 	}
 
 	//-------------------------------------------------------------------------------------------------
@@ -258,7 +257,8 @@ public boolean updateEndpoint(final String systemName, final String serviceName,
 	  Connection conn = null;
 	  try {
 	    conn = getConnection();
-      conn.setAutoCommit(false);
+      	conn.setAutoCommit(false);
+		
 	    int sid = serviceToID(systemName, serviceName, conn);
 	    if (sid != -1) {
 	      String sql = "INSERT INTO dmhist_messages(sid, bt, mint, maxt, msg) VALUES(?, ?, ?, ?, ?)";
@@ -278,7 +278,7 @@ public boolean updateEndpoint(final String systemName, final String serviceName,
 
 	      // that was the entire message, now insert each individual JSON object in the message
 	      String bu = message.get(0).getBu();
-        for (SenML m : message) {
+          for (SenML m : message) {
           double t = 0;
           if (m.getT() != null) {
             if (m.getT() < SenML.RELATIVE_TIMESTAMP_INDICATOR) {
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerACLFilter.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerACLFilter.java
new file mode 100644
index 000000000..68a37f052
--- /dev/null
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerACLFilter.java
@@ -0,0 +1,307 @@
+/********************************************************************************
+ * Copyright (c) 2021 {Lulea University of Technology}
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 
+ *
+ * Contributors: 
+ *   {Lulea University of Technology} - implementation
+ *   Arrowhead Consortia - conceptualization 
+ ********************************************************************************/
+package eu.arrowhead.core.datamanager.security;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Scanner;
+
+import java.util.ArrayList;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import eu.arrowhead.common.CommonConstants;
+import eu.arrowhead.common.CoreCommonConstants;
+import org.springframework.beans.factory.annotation.Value;
+import eu.arrowhead.common.SecurityUtilities; 
+
+
+import eu.arrowhead.common.dto.shared.CertificateType;
+import org.springframework.beans.factory.annotation.Autowired;
+import javax.annotation.PostConstruct;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import org.springframework.context.ApplicationContext;
+import org.springframework.boot.SpringApplication;
+import org.springframework.stereotype.Component;
+import eu.arrowhead.common.Utilities;
+import eu.arrowhead.common.exception.AuthException;
+
+
+@Component
+@ConditionalOnProperty(name = CommonConstants.SERVER_SSL_ENABLED, matchIfMissing = true) 
+public class DatamanagerACLFilter {
+    private final Logger logger = LogManager.getLogger(DatamanagerACLFilter.class);
+
+    @Autowired
+    private ApplicationContext context;
+
+    @Value("${acl.file}")
+    private String aclFileName;
+
+    ArrayList<AclRule> rules = null;
+
+    //=================================================================================================
+    // members
+    private static final boolean USE_DEFAULTS = true;
+
+    public static final String DM_HIST_OP = CommonConstants.DATAMANAGER_URI + CommonConstants.OP_DATAMANAGER_HISTORIAN;
+    public static final String DM_PROXY_OP = CommonConstants.DATAMANAGER_URI + CommonConstants.OP_DATAMANAGER_PROXY;
+    public static final String DM_HIST_OP_WS = DM_HIST_OP + "/ws";
+
+    public static final String METHOD_GET  = "GET";
+    public static final String METHOD_PUT  = "PUT";
+    public static final String METHOD_POST = "POST";
+    public static final String METHOD_DEL  = "DELETE";
+
+    public static final String ACL_METHODS = "gpPd";
+    public static final String ACL_METHOD_GET  = "g";
+    public static final String ACL_METHOD_PUT  = "p";
+    public static final String ACL_METHOD_POST = "P";
+    public static final String ACL_METHOD_DEL  = "d";
+
+    public static final String ACL_LINE_COMMENT1 = "#";
+    public static final String ACL_LINE_COMMENT2 = ";";
+    public static final String ACL_SYS_WILDCARD  = "$SYS";
+    public static final String ACL_SRV_WILDCARD  = "*";
+
+    public static final String ACL_SYS_DELIM  = ":";
+    public static final String ACL_RULE_DELIM  = ",";
+    public static final String ACL_PATH_DELIM  = "@";
+    public static final String ACL_PATH_SEPARATOR  = "/";
+    public static final String ACL_DEFAULT_RULE = "$SYS: gpPd@$SYS/*";
+    
+        
+	//=================================================================================================
+	// assistant methods
+
+    public DatamanagerACLFilter() {
+        
+    }
+
+    @PostConstruct
+    public void init() {
+        boolean res = loadFile(aclFileName);
+        if(res==false) {
+            logger.info("Could not load ACL file!");
+
+            if(USE_DEFAULTS) {
+                logger.info("Loading defaults ...");
+                String[] defaults = new String[1];
+                defaults[0] = ACL_DEFAULT_RULE;
+                load(defaults);
+            } else {
+                logger.info("Exiting ...");
+                int exitCode = 0;
+                SpringApplication.exit(context, () -> exitCode);
+                System.exit(exitCode);
+            }
+        }
+    }
+
+
+    //-------------------------------------------------------------------------------------------------
+    public boolean checkRequest(final String systemCN, final String operation, final String path) throws Exception {
+        logger.debug("checkRequest for CN: {}, op: {} for '{}'", systemCN, operation, path);
+
+        String endPath = "";
+        if (path.contains(DM_HIST_OP_WS)) {
+            endPath = path.substring(path.indexOf(DM_HIST_OP_WS) + DM_HIST_OP_WS.length() + 1);
+        }else if (path.contains(DM_HIST_OP)) {
+            endPath = path.substring(path.indexOf(DM_HIST_OP) + DM_HIST_OP.length() + 1);
+        } else if (path.contains(DM_PROXY_OP)){
+            endPath = path.substring(path.indexOf(DM_PROXY_OP)+ DM_PROXY_OP.length() + 1);
+        }
+
+        logger.info("endPath: {}", endPath);
+        final String[] targetPath = endPath.split(ACL_PATH_SEPARATOR);
+        String op = "";
+
+        switch(operation.toUpperCase().trim()) {
+            case METHOD_GET:
+                op = ACL_METHOD_GET;
+                break;
+            case METHOD_PUT:
+                op = ACL_METHOD_PUT;
+                break;
+            case METHOD_POST:
+                op = ACL_METHOD_POST;
+                break;
+            case METHOD_DEL:
+                op = ACL_METHOD_DEL;
+                break;
+            default:
+                throw new AuthException("Unknown method");
+        }
+
+        for(AclRule rule: rules) {
+
+            if(rule.systemName.equals(systemCN)) {
+
+                for(AclEntry acl: rule.acls) {
+                    final String[] pathParts = acl.path.split(ACL_PATH_SEPARATOR);
+                    final String pathSystem = pathParts[0].trim();
+                    final String pathService = pathParts[1].trim();
+
+                    if(acl.path.equals(endPath)) {
+                        
+                        if(acl.operations.contains(op)) {
+                            return true;
+                        }
+                    } else if(pathSystem.equals(targetPath[0]) && pathService.equals(ACL_SRV_WILDCARD)) {
+                        if(acl.operations.contains(op)) {
+                            return true;
+                        }
+                    } else if(pathSystem.equals(ACL_SRV_WILDCARD) && pathService.equals(ACL_SRV_WILDCARD)) {
+                        if(acl.operations.contains(op)) {
+                            return true;
+                        }
+                    } 
+
+                }
+            } else if(rule.systemName.equals(ACL_SYS_WILDCARD)) {
+
+                for(AclEntry acl: rule.acls) {
+                    final String[] pathParts = acl.path.split(ACL_PATH_SEPARATOR);
+                    String pathSystem = "";
+                    String pathService = "";
+                    if (pathParts.length == 2) {
+                        pathSystem = pathParts[0].trim();
+                        pathService = pathParts[1].trim();
+                    }
+
+                    if(targetPath.length == 1) {
+                        if((pathSystem.equals(ACL_SYS_WILDCARD) && targetPath[0].equals(systemCN))) {
+                            if(acl.operations.contains(op)) {
+                                return true;
+                            }
+                        }
+                    } else {
+                        if((pathSystem.equals(ACL_SYS_WILDCARD) && targetPath[0].equals(systemCN)) && (pathService.equals(ACL_SRV_WILDCARD) || pathService.equals(targetPath[1]))) {
+                            if(acl.operations.contains(op)) {
+                                return true;
+                            }
+                        }
+                    }
+
+                }
+            }
+
+        }
+        
+        return false;
+    }
+
+
+    //-------------------------------------------------------------------------------------------------
+    public boolean load(final String[] lines) {
+        rules = new ArrayList<AclRule>();
+
+        try {
+            for(String line: lines) {
+                line = line.trim();
+                if(line.startsWith(ACL_LINE_COMMENT1) || line.startsWith(ACL_LINE_COMMENT1) || line.equals("")) {
+                    continue;
+                }
+
+                String[] parts = line.split(ACL_SYS_DELIM);
+                final AclRule r = new AclRule(parts[0].trim());
+                final String rulesData[] = parts[1].trim().split(ACL_RULE_DELIM);
+                for (String rule : rulesData) {
+                    r.addACLEntries(rule.trim());
+                }
+                rules.add(r);
+            }
+
+        } catch(Exception e) {
+            logger.info("Misformad ACL file!");
+            rules = new ArrayList<AclRule>();
+
+            return false;
+        }
+
+        return true;
+    }
+
+
+    //-------------------------------------------------------------------------------------------------
+    public boolean loadFile(final String filename) {
+        final ArrayList<String> lines = new ArrayList<String>();
+
+        try {
+            Scanner myReader = new Scanner(new File(filename));
+            while (myReader.hasNextLine()) {
+              String line = myReader.nextLine().trim();
+              lines.add(line);
+            }
+            myReader.close();
+
+            return load(lines.toArray(new String[lines.size()]));
+
+        } catch (FileNotFoundException e) {
+            logger.debug("Could not load ACL file!");
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    //-------------------------------------------------------------------------------------------------
+    private class AclEntry {
+        String operations;
+        String path;
+
+        public AclEntry() {
+
+        }
+    }
+
+    //-------------------------------------------------------------------------------------------------
+    private class AclRule {
+        String systemName;
+        ArrayList<AclEntry> acls = new ArrayList<AclEntry>();
+
+        
+        public AclRule(final String systemName){
+            this.systemName = systemName;
+        }
+
+
+        private void addACLEntries(final String entryString) throws Exception{
+            final String[] parts = entryString.split(ACL_RULE_DELIM);
+
+            for (String part : parts) {
+                addACLEntry(part);
+            }
+        }
+        
+        private void addACLEntry(final String entryString) throws Exception {
+            final String[] parts = entryString.split(ACL_PATH_DELIM);
+            final String operations = parts[0].trim().toLowerCase();
+            final String path = parts[1].trim();
+
+            // allow CRUD only operations
+            for (int i = 0; i < operations.length(); i++) {
+                String op = "" + operations.charAt(i);
+                if(ACL_METHODS.indexOf(op) == -1) {
+                    throw new Exception("illegal operation: " + op);
+                }
+            }
+            final AclEntry acl = new AclEntry();
+            acl.operations = operations;
+            acl.path = path;
+            acls.add(acl);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerAccessControlFilter.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerAccessControlFilter.java
index f47d5be91..560ce8d5c 100644
--- a/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerAccessControlFilter.java
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/security/DatamanagerAccessControlFilter.java
@@ -13,11 +13,15 @@
  ********************************************************************************/
 package eu.arrowhead.core.datamanager.security;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
 import eu.arrowhead.common.CommonConstants;
 import eu.arrowhead.common.CoreCommonConstants;
 import eu.arrowhead.common.SecurityUtilities;
 import eu.arrowhead.common.dto.shared.CertificateType;
 import eu.arrowhead.common.security.CoreSystemAccessControlFilter;
+import eu.arrowhead.core.datamanager.security.DatamanagerACLFilter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.stereotype.Component;
@@ -25,17 +29,21 @@
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
-//import eu.arrowhead.common.dto.shared.SenML;
 import eu.arrowhead.common.Utilities;
-//import eu.arrowhead.common.core.CoreSystem;
 import eu.arrowhead.common.exception.AuthException;
 
 import java.util.Map;
+import java.net.URI;
 
 @Component
 @ConditionalOnProperty(name = CommonConstants.SERVER_SSL_ENABLED, matchIfMissing = true) 
 public class DatamanagerAccessControlFilter extends CoreSystemAccessControlFilter {
-	
+
+        private final Logger logger = LogManager.getLogger(DatamanagerAccessControlFilter.class);
+
+        @Autowired
+        DatamanagerACLFilter dataManagerACLFilter;
+
 	//=================================================================================================
         // members
         
@@ -50,35 +58,22 @@ protected void checkClientAuthorized(final String clientCN, final String method,
 		final String cloudCN = getServerCloudCN();
 
 		if (requestTarget.endsWith(CommonConstants.ECHO_URI)) {
-      // Everybody in the local cloud can test the server => no further check is necessary
-      return;
-		} 
+                        // Everybody in the local cloud can test the server => no further check is necessary
+                        return;
+		}
 
-    // only the system named $SysName is allowed to write to <historian or proxy>/$SysName/$SrvName
-    if (!method.toLowerCase().equals("get")) {
-      final String dataManagerHistorianURI = CommonConstants.DATAMANAGER_URI + CommonConstants.OP_DATAMANAGER_HISTORIAN+"/";
-      int sysNameStartPosition = requestTarget.indexOf(dataManagerHistorianURI);
-      int sysNameStopPosition = -1;
-      if ( sysNameStartPosition != -1) {
-        sysNameStopPosition = requestTarget.indexOf("/", sysNameStartPosition + dataManagerHistorianURI.length());
-        String requestTargetSystemName = requestTarget.substring(sysNameStartPosition + dataManagerHistorianURI.length(), sysNameStopPosition);
-
-        checkIfRequesterSystemNameisEqualsWithClientNameFromCN(requestTargetSystemName, clientCN);
-        return;
-      }
-
-      if ( sysNameStartPosition == -1) {
-        final String dataManagerProxyURI = CommonConstants.DATAMANAGER_URI + CommonConstants.OP_DATAMANAGER_PROXY+"/";
-        sysNameStartPosition = requestTarget.indexOf(dataManagerProxyURI);
-        sysNameStopPosition = requestTarget.indexOf("/", sysNameStartPosition + dataManagerProxyURI.length());
-        String requestTargetSystemName = requestTarget.substring(sysNameStartPosition + dataManagerProxyURI.length(), sysNameStopPosition);
-
-        checkIfRequesterSystemNameisEqualsWithClientNameFromCN(requestTargetSystemName, clientCN);
-        return;
-      } else {
-        throw new AuthException("Illegal request");
-      }
-    }
+		try {
+                        
+                        if(dataManagerACLFilter.checkRequest(clientCN, method, requestTarget)) {
+                                logger.debug("Authorized");
+                        } else {
+                                logger.debug("Unauthorized!");
+                                throw new AuthException("Not authorized");
+                        }
+                        
+                } catch(Exception e) {
+                        throw new AuthException("Error during authorization");
+                }
 
 	}
 
diff --git a/datamanager/src/main/java/eu/arrowhead/core/datamanager/service/DataManagerDriver.java b/datamanager/src/main/java/eu/arrowhead/core/datamanager/service/DataManagerDriver.java
new file mode 100644
index 000000000..281c29536
--- /dev/null
+++ b/datamanager/src/main/java/eu/arrowhead/core/datamanager/service/DataManagerDriver.java
@@ -0,0 +1,116 @@
+package eu.arrowhead.core.datamanager.service;
+
+import java.util.Vector;
+import java.util.Iterator;
+
+import javax.annotation.Resource;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.web.util.UriComponents;
+
+import eu.arrowhead.common.CommonConstants;
+import eu.arrowhead.common.CoreCommonConstants;
+import eu.arrowhead.common.Utilities;
+import eu.arrowhead.common.core.CoreSystemService;
+import eu.arrowhead.common.exception.ArrowheadException;
+import eu.arrowhead.common.exception.BadPayloadException;
+import eu.arrowhead.common.exception.InvalidParameterException;
+import eu.arrowhead.common.dto.shared.SenML;
+
+@Component
+public class DataManagerDriver {
+
+	//=================================================================================================
+	// members
+
+	private static final Logger logger = LogManager.getLogger(DataManagerDriver.class);
+
+	//=================================================================================================
+	// methods
+	
+	//=================================================================================================
+  	public void validateSenMLMessage(final String systemName, final String serviceName, final Vector<SenML> message) {
+	  try {
+      	    Assert.notNull(systemName, "systemName is null.");
+    	    Assert.notNull(serviceName, "serviceName is null.");
+    	    Assert.notNull(message, "message is null.");
+    	    Assert.isTrue(!message.isEmpty(), "message is empty");
+
+    	    SenML head = (SenML)message.get(0);
+	    Assert.notNull(head.getBn(), "bn is null.");
+	  } catch(Exception e) {
+	    throw new BadPayloadException("Missing mandatory field");
+	  }
+      }
+
+
+      //-------------------------------------------------------------------------------------------------
+      public void validateSenMLContent(final Vector<SenML> message) {
+	try {
+
+    	/* check that bn, bt and bu are included only once, and in the first object */
+    	Iterator<SenML> entry = message.iterator();
+    	int bnc=0, btc=0, buc=0;
+    	while (entry.hasNext()) {
+      		SenML element = entry.next();
+      		if (element.getBn() != null) {
+        		bnc++;
+    	  	}
+      		if (element.getBt() != null) {
+        		btc++;
+      		}
+      		if (element.getBu() != null) {
+        		buc++;
+      		}
+    	}
+
+    	/* bu can only exist once. bt can only exist one, bu can exist 0 or 1 times */
+    	Assert.isTrue(!(bnc != 1 || btc != 1 || buc > 1), "invalid bn/bt/bu");
+
+    	/* bn must exist in [0] */
+    	SenML element = (SenML)message.get(0);
+    	Assert.notNull(element.getBn(), "bn is missing");
+
+    	/* bt must exist in [0] */
+    	Assert.notNull(element.getBt(), "bt is missing");
+
+    	/* bt cannot be negative */
+    	Assert.isTrue(element.getBt() >= 0.0, "a negative base time is not allowed");
+
+    	/* bu must exist in [0], if it exists */
+    	Assert.isTrue(!(element.getBu() == null && buc == 1), "invalid use of bu");
+
+    	/* check that v, bv, sv, etc are included only once per object */
+    	entry = message.iterator();
+    	while (entry.hasNext()) {
+      		element = (SenML)entry.next();
+
+      		int valueCount = 0;
+      		if (element.getV() != null) {
+        		valueCount++;
+      		}
+      		if (element.getVs() != null) {
+        		valueCount++;
+      		}
+      		if (element.getVd() != null) {
+        		valueCount++;
+      		}
+      		if (element.getVb() != null) {
+        		valueCount++;
+		}
+
+      		Assert.isTrue(!(valueCount > 1 && element.getS() == null), "too many value tags");
+		}
+	} catch(Exception e) {
+		throw new BadPayloadException("Illegal request");
+	}
+
+  }
+}
diff --git a/datamanager/src/main/resources/accesslist.acl b/datamanager/src/main/resources/accesslist.acl
new file mode 100644
index 000000000..d7d3bb767
--- /dev/null
+++ b/datamanager/src/main/resources/accesslist.acl
@@ -0,0 +1,9 @@
+# generic
+$SYS: gpPd@$SYS/* 
+
+# sysop
+sysop: gpPd@*/*
+
+# specific example
+#service_registry.testcloud2.aitia.arrowhead.eu: gpd@examplesys1/temperature, g@examplesys2/temperature,
+
diff --git a/datamanager/src/main/resources/application.properties b/datamanager/src/main/resources/application.properties
index 2fdb70ce6..8ea4f3c5e 100644
--- a/datamanager/src/main/resources/application.properties
+++ b/datamanager/src/main/resources/application.properties
@@ -21,6 +21,11 @@ server.port=8461
 domain.name=127.0.0.1
 domain.port=8461
 
+# Websockets
+websockets.enabled=true
+
+acl.file=accesslist.acl
+
 ############################################
 ###       CUSTOM PARAMETERS              ###
 ############################################
diff --git a/datamanager/src/test/java/eu/arrowhead/core/datamanager/DataManagerControllerSystemTest.java b/datamanager/src/test/java/eu/arrowhead/core/datamanager/DataManagerControllerSystemTest.java
index 3fc042698..4b2ba6d5c 100644
--- a/datamanager/src/test/java/eu/arrowhead/core/datamanager/DataManagerControllerSystemTest.java
+++ b/datamanager/src/test/java/eu/arrowhead/core/datamanager/DataManagerControllerSystemTest.java
@@ -1,9 +1,6 @@
 package eu.arrowhead.core.datamanager;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-//import eu.arrowhead.common.dto.internal.SystemListResponseDTO;
-//import eu.arrowhead.common.dto.shared.SystemRequestDTO;
-//import eu.arrowhead.common.dto.shared.SystemResponseDTO;
 import eu.arrowhead.common.dto.shared.DataManagerSystemsResponseDTO;
 import eu.arrowhead.common.exception.InvalidParameterException;
 import eu.arrowhead.core.datamanager.database.service.DataManagerDBService;
@@ -81,7 +78,7 @@ public void setup() {
     @Test
     public void echoHistorian() throws Exception {
         final MvcResult response = this.mockMvc.perform(get(DATAMANAGER_ECHO_URI)
-                                                                .accept(MediaType.APPLICATION_JSON))
+                                               .accept(MediaType.APPLICATION_JSON))
                                                .andExpect(status().isOk())
                                                .andReturn();
         assertEquals("Got it!", response.getResponse().getContentAsString());
@@ -90,7 +87,7 @@ public void echoHistorian() throws Exception {
     @Test
     public void testProxy() throws Exception {
         final MvcResult response = this.mockMvc.perform(get(DATAMANAGER_PROXY_URI)
-                                                                .accept(MediaType.APPLICATION_JSON))
+                                               .accept(MediaType.APPLICATION_JSON))
                                                .andExpect(status().isOk())
                                                .andReturn();
         final DataManagerSystemsResponseDTO responseBody = objectMapper.readValue(response.getResponse().getContentAsString(), DataManagerSystemsResponseDTO.class);
@@ -100,7 +97,7 @@ public void testProxy() throws Exception {
     @Test
     public void testHistorian() throws Exception {
         final MvcResult response = this.mockMvc.perform(get(DATAMANAGER_HISTORIAN_URI)
-                                                                .accept(MediaType.APPLICATION_JSON))
+                                               .accept(MediaType.APPLICATION_JSON))
                                                .andExpect(status().isOk())
                                                .andReturn();
         final DataManagerSystemsResponseDTO responseBody = objectMapper.readValue(response.getResponse().getContentAsString(), DataManagerSystemsResponseDTO.class);
diff --git a/pom.xml b/pom.xml
index 3f9c91728..226446539 100644
--- a/pom.xml
+++ b/pom.xml
@@ -199,12 +199,12 @@
 		</dependency>
 -->
 		
-		<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
-		<!--<dependency>
-      			<groupId>com.google.code.gson</groupId>
-      			<artifactId>gson</artifactId>
-      			<version>2.8.5</version>
-		</dependency>-->
+	<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
+	<dependency>
+      		<groupId>com.google.code.gson</groupId>
+      		<artifactId>gson</artifactId>
+      		<version>2.8.6</version>
+	</dependency>
 
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
