This is an automated email from the ASF dual-hosted git repository.

zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new cfbcaac8b8 Add dataset permissions (#4099)
cfbcaac8b8 is described below

commit cfbcaac8b854e9eb329946bf5edd5e4144641aa4
Author: Dominik Riemer <[email protected]>
AuthorDate: Tue Jan 20 15:23:39 2026 +0100

    Add dataset permissions (#4099)
    
    Co-authored-by: Jacqueline Höllig <[email protected]>
    Co-authored-by: Jacqueline Höllig 
<[email protected]>
    Co-authored-by: Philipp Zehnder <[email protected]>
---
 .../streampipes/client/api/IStreamPipesClient.java |   2 +
 .../api/config/IStreamPipesClientConfig.java       |   8 +
 .../streampipes/client/StreamPipesClient.java      |  10 +-
 .../streampipes/client/http/HttpRequest.java       |   6 +-
 .../client/model/StreamPipesClientConfig.java      |  18 +
 .../commons/constants/HttpConstants.java           |   1 +
 .../api/IDataExplorerSchemaManagement.java         |   2 +-
 .../influx/DataExplorerManagerInflux.java          |   7 +-
 .../iotdb/DataExplorerManagerIotDb.java            |   7 +-
 .../dataexplorer/DataExplorerSchemaManagement.java |  13 +-
 .../DataExplorerSchemaManagementTest.java          |  24 +-
 .../sinks/internal/jvm/datalake/DataLakeSink.java  |   5 +-
 .../model/client/user/DefaultPrivilege.java        |   7 +
 .../permission/DataLakePermissionManager.java      |  46 ++
 .../manager/storage/PipelineStorageService.java    |   2 +-
 .../impl/datalake/AbstractDataLakeResource.java    |  80 +++
 .../impl/datalake/DataLakeMeasureResource.java     |  28 +-
 .../rest/impl/datalake/DataLakeResource.java       |  47 +-
 .../impl/datalake/PersistedDataStreamResource.java |   4 +-
 .../streampipes/rest/security/AuthConstants.java   |   7 +
 .../core/filter/TokenAuthenticationFilter.java     |  23 +-
 .../core/migrations/AvailableMigrations.java       |   6 +-
 .../v099/CreateAssetPermissionMigration.java       |   3 +-
 .../v099/CreateDatasetPermissionMigration.java     | 118 +++++
 .../v099/RemoveDuplicatedAssetPermissions.java     | 106 ++++
 .../management/authorization/PrivilegeManager.java |   4 +-
 .../user/management/authorization/RoleManager.java |  19 +-
 .../support/utils/dataExplorer/DataExplorerBtns.ts |   8 +
 .../utils/dataExplorer/DataExplorerUtils.ts        |   7 +
 ui/cypress/support/utils/dataset/DatasetBtns.ts    |  23 +
 ui/cypress/support/utils/dataset/DatasetUtils.ts   |  44 ++
 .../userManagement/testUserRoleDataset.spec.ts     | 221 ++++++++
 ui/deployment/modules.yml                          |   2 +-
 .../src/lib/model/gen/streampipes-model-client.ts  |   7 +-
 .../src/lib/model/gen/streampipes-model.ts         |   3 +-
 .../sp-exception-message.component.html            |   1 +
 ui/src/app/_enums/user-privilege.enum.ts           |   3 +
 .../chart-overview-table.component.html            |  12 +-
 .../file-overview/file-overview.component.html     |  15 +-
 .../dashboard-toolbar.component.html               |   1 +
 .../datalake-configuration.component.html          | 581 +++++++++++----------
 .../datalake-configuration.component.ts            |  28 +
 42 files changed, 1223 insertions(+), 336 deletions(-)

diff --git 
a/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/IStreamPipesClient.java
 
b/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/IStreamPipesClient.java
index 44f5064431..90b37b5a1d 100644
--- 
a/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/IStreamPipesClient.java
+++ 
b/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/IStreamPipesClient.java
@@ -61,4 +61,6 @@ public interface IStreamPipesClient extends Serializable {
   IFileApi fileApi();
   
   IDataLakeResourceApi dataLakeResourceApi();
+
+  IStreamPipesClient onBehalfOf(String userSid);
 }
diff --git 
a/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/config/IStreamPipesClientConfig.java
 
b/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/config/IStreamPipesClientConfig.java
index 8f49b487e1..5f71eba926 100644
--- 
a/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/config/IStreamPipesClientConfig.java
+++ 
b/streampipes-client-api/src/main/java/org/apache/streampipes/client/api/config/IStreamPipesClientConfig.java
@@ -20,9 +20,17 @@ package org.apache.streampipes.client.api.config;
 
 import org.apache.streampipes.messaging.SpProtocolDefinitionFactory;
 
+import org.apache.http.Header;
+
+import java.util.Set;
+
 public interface IStreamPipesClientConfig {
 
   void addTransportProtocol(SpProtocolDefinitionFactory<?> 
protocolDefinitionFactory);
 
+  void addCustomHeader(String name, String value);
+
+  Set<Header> getCustomHeaders();
+
   ClientConnectionUrlResolver getConnectionConfig();
 }
diff --git 
a/streampipes-client/src/main/java/org/apache/streampipes/client/StreamPipesClient.java
 
b/streampipes-client/src/main/java/org/apache/streampipes/client/StreamPipesClient.java
index d159809b1e..9870a73054 100644
--- 
a/streampipes-client/src/main/java/org/apache/streampipes/client/StreamPipesClient.java
+++ 
b/streampipes-client/src/main/java/org/apache/streampipes/client/StreamPipesClient.java
@@ -39,6 +39,7 @@ import 
org.apache.streampipes.client.api.credentials.CredentialsProvider;
 import org.apache.streampipes.client.model.StreamPipesClientConfig;
 import org.apache.streampipes.client.model.StreamPipesClientConnectionConfig;
 import org.apache.streampipes.client.paths.ApiPath;
+import org.apache.streampipes.commons.constants.HttpConstants;
 import org.apache.streampipes.messaging.SpProtocolDefinitionFactory;
 import org.apache.streampipes.model.mail.SpEmail;
 
@@ -47,7 +48,7 @@ public class StreamPipesClient implements
 
   private static final Integer SP_DEFAULT_PORT = 80;
 
-  private StreamPipesClientConfig config;
+  private final StreamPipesClientConfig config;
 
   private StreamPipesClient(ClientConnectionUrlResolver connectionConfig) {
     this.config = new StreamPipesClientConfig(connectionConfig);
@@ -231,4 +232,11 @@ public class StreamPipesClient implements
   public DataLakeResourceApi dataLakeResourceApi () {
     return new DataLakeResourceApi (config);
   }
+
+  @Override
+  public IStreamPipesClient onBehalfOf(String userSid) {
+    var scoped = new StreamPipesClient(config.getConnectionConfig());
+    scoped.config.addCustomHeader(HttpConstants.X_ON_BEHALF_OF, userSid);
+    return scoped;
+  }
 }
diff --git 
a/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
 
b/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
index 75afd01750..cbcc8550e5 100644
--- 
a/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
+++ 
b/streampipes-client/src/main/java/org/apache/streampipes/client/http/HttpRequest.java
@@ -63,11 +63,15 @@ public abstract class HttpRequest<K, V, T> {
   protected Header[] standardJsonHeaders() {
     List<Header> headers = new 
ArrayList<>(connectionConfig.getCredentials().makeHeaders());
     headers.add(Headers.acceptJson());
+    headers.addAll(clientConfig.getCustomHeaders());
     return headers.toArray(new Header[0]);
   }
 
   protected Header[] standardHeaders() {
-    List<Header> headers = new 
ArrayList<>(connectionConfig.getCredentials().makeHeaders());
+    List<Header> headers = new ArrayList<>();
+    headers.addAll(connectionConfig.getCredentials().makeHeaders());
+    headers.addAll(clientConfig.getCustomHeaders());
+
     return headers.toArray(new Header[0]);
   }
 
diff --git 
a/streampipes-client/src/main/java/org/apache/streampipes/client/model/StreamPipesClientConfig.java
 
b/streampipes-client/src/main/java/org/apache/streampipes/client/model/StreamPipesClientConfig.java
index 92e9426587..083877ce85 100644
--- 
a/streampipes-client/src/main/java/org/apache/streampipes/client/model/StreamPipesClientConfig.java
+++ 
b/streampipes-client/src/main/java/org/apache/streampipes/client/model/StreamPipesClientConfig.java
@@ -22,12 +22,20 @@ import 
org.apache.streampipes.client.api.config.IStreamPipesClientConfig;
 import org.apache.streampipes.messaging.SpProtocolDefinitionFactory;
 import org.apache.streampipes.messaging.SpProtocolManager;
 
+import org.apache.http.Header;
+import org.apache.http.message.BasicHeader;
+
+import java.util.HashSet;
+import java.util.Set;
+
 public class StreamPipesClientConfig implements IStreamPipesClientConfig {
 
   private final ClientConnectionUrlResolver connectionConfig;
+  private final Set<Header> customHeaders;
 
   public StreamPipesClientConfig(ClientConnectionUrlResolver connectionConfig) 
{
     this.connectionConfig = connectionConfig;
+    this.customHeaders = new HashSet<>();
   }
 
   @Override
@@ -35,6 +43,16 @@ public class StreamPipesClientConfig implements 
IStreamPipesClientConfig {
     SpProtocolManager.INSTANCE.register(protocolDefinitionFactory);
   }
 
+  @Override
+  public void addCustomHeader(String name, String value) {
+    customHeaders.add(new BasicHeader(name, value));
+  }
+
+  @Override
+  public Set<Header> getCustomHeaders() {
+    return customHeaders;
+  }
+
   @Override
   public ClientConnectionUrlResolver getConnectionConfig() {
     return connectionConfig;
diff --git 
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
 
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
index 9866f10e82..22ce5b0c02 100644
--- 
a/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
+++ 
b/streampipes-commons/src/main/java/org/apache/streampipes/commons/constants/HttpConstants.java
@@ -26,6 +26,7 @@ public class HttpConstants {
   public static final String APPLICATION_JSON_TYPE = "application/json";
   public static final String X_API_USER = "X-API-USER";
   public static final String X_API_KEY = "X-API-KEY";
+  public static final String X_ON_BEHALF_OF = "X-On-Behalf-Of";
   public static final String BASIC = "Basic ";
 
 }
diff --git 
a/streampipes-data-explorer-api/src/main/java/org/apache/streampipes/dataexplorer/api/IDataExplorerSchemaManagement.java
 
b/streampipes-data-explorer-api/src/main/java/org/apache/streampipes/dataexplorer/api/IDataExplorerSchemaManagement.java
index c171469c52..e8efa046da 100644
--- 
a/streampipes-data-explorer-api/src/main/java/org/apache/streampipes/dataexplorer/api/IDataExplorerSchemaManagement.java
+++ 
b/streampipes-data-explorer-api/src/main/java/org/apache/streampipes/dataexplorer/api/IDataExplorerSchemaManagement.java
@@ -31,7 +31,7 @@ public interface IDataExplorerSchemaManagement {
 
   Optional<DataLakeMeasure> getExistingMeasureByName(String measureName);
 
-  DataLakeMeasure createOrUpdateMeasurement(DataLakeMeasure measure);
+  DataLakeMeasure createOrUpdateMeasurement(DataLakeMeasure measure,String 
principalSid);
 
   void deleteMeasurement(String elementId);
 
diff --git 
a/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
 
b/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
index f88cf6c585..5ec67f0db9 100644
--- 
a/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
+++ 
b/streampipes-data-explorer-influx/src/main/java/org/apache/streampipes/dataexplorer/influx/DataExplorerManagerInflux.java
@@ -29,6 +29,7 @@ import 
org.apache.streampipes.dataexplorer.api.IDataLakeMeasurementSanitizer;
 import org.apache.streampipes.dataexplorer.api.ITimeSeriesStorage;
 import org.apache.streampipes.dataexplorer.influx.client.InfluxClientProvider;
 import 
org.apache.streampipes.dataexplorer.influx.sanitize.DataLakeMeasurementSanitizerInflux;
+import org.apache.streampipes.manager.permission.DataLakePermissionManager;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
@@ -55,7 +56,11 @@ public class DataExplorerManagerInflux implements 
IDataExplorerManager {
   public IDataExplorerSchemaManagement getSchemaManagement() {
     return new DataExplorerSchemaManagement(StorageDispatcher.INSTANCE
         .getNoSqlStore()
-        .getDataLakeStorage());
+        .getDataLakeStorage(),
+        new DataLakePermissionManager(
+            StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage()
+        )
+    );
   }
 
   @Override
diff --git 
a/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
index e931403809..ba0f060b94 100644
--- 
a/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
+++ 
b/streampipes-data-explorer-iotdb/src/main/java/org/apache/streampipes/dataexplorer/iotdb/DataExplorerManagerIotDb.java
@@ -28,6 +28,7 @@ import 
org.apache.streampipes.dataexplorer.api.IDataLakeMeasurementCounter;
 import org.apache.streampipes.dataexplorer.api.IDataLakeMeasurementSanitizer;
 import org.apache.streampipes.dataexplorer.api.ITimeSeriesStorage;
 import 
org.apache.streampipes.dataexplorer.iotdb.sanitize.DataLakeMeasurementSanitizerIotDb;
+import org.apache.streampipes.manager.permission.DataLakePermissionManager;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
@@ -54,7 +55,11 @@ public class DataExplorerManagerIotDb implements 
IDataExplorerManager {
   public IDataExplorerSchemaManagement getSchemaManagement() {
     return new DataExplorerSchemaManagement(StorageDispatcher.INSTANCE
                                                              .getNoSqlStore()
-                                                             
.getDataLakeStorage());
+                                                             
.getDataLakeStorage(),
+        new DataLakePermissionManager(
+            StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage()
+        )
+    );
   }
 
   @Override
diff --git 
a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagement.java
 
b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagement.java
index 718dcba4af..c9a5c9db78 100644
--- 
a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagement.java
+++ 
b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagement.java
@@ -20,6 +20,7 @@ package org.apache.streampipes.dataexplorer;
 
 import org.apache.streampipes.dataexplorer.api.IDataExplorerSchemaManagement;
 import org.apache.streampipes.dataexplorer.utils.DataExplorerUtils;
+import org.apache.streampipes.manager.permission.DataLakePermissionManager;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import 
org.apache.streampipes.model.datalake.DataLakeMeasureSchemaUpdateStrategy;
 import org.apache.streampipes.model.schema.EventProperty;
@@ -28,6 +29,7 @@ import org.apache.streampipes.storage.api.CRUDStorage;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import java.util.UUID;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -35,9 +37,12 @@ import java.util.stream.Stream;
 public class DataExplorerSchemaManagement implements 
IDataExplorerSchemaManagement {
 
   CRUDStorage<DataLakeMeasure> dataLakeStorage;
+  private final DataLakePermissionManager permissionManager;
 
-  public DataExplorerSchemaManagement(CRUDStorage<DataLakeMeasure> 
dataLakeStorage) {
+  public DataExplorerSchemaManagement(CRUDStorage<DataLakeMeasure> 
dataLakeStorage,
+                                      DataLakePermissionManager 
permissionManager) {
     this.dataLakeStorage = dataLakeStorage;
+    this.permissionManager = permissionManager;
   }
 
   @Override
@@ -56,14 +61,18 @@ public class DataExplorerSchemaManagement implements 
IDataExplorerSchemaManageme
    * according to the update strategy defined by the measurement.
    */
   @Override
-  public DataLakeMeasure createOrUpdateMeasurement(DataLakeMeasure measure) {
+  public DataLakeMeasure createOrUpdateMeasurement(DataLakeMeasure measure,
+                                                   String principalSid) {
 
     setDefaultUpdateStrategyIfNoneProvided(measure);
 
     var existingMeasure = getExistingMeasureByName(measure.getMeasureName());
 
     if (existingMeasure.isEmpty()) {
+      measure.setElementId(UUID.randomUUID().toString());
       setSchemaVersionAndStoreMeasurement(measure);
+      
permissionManager.makeAndPersistDataLakePermission(measure.getElementId(), 
principalSid);
+
     } else {
       handleExistingMeasurement(measure, existingMeasure.get());
     }
diff --git 
a/streampipes-data-explorer/src/test/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagementTest.java
 
b/streampipes-data-explorer/src/test/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagementTest.java
index 4dc34531ce..a4030ef91e 100644
--- 
a/streampipes-data-explorer/src/test/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagementTest.java
+++ 
b/streampipes-data-explorer/src/test/java/org/apache/streampipes/dataexplorer/DataExplorerSchemaManagementTest.java
@@ -18,10 +18,12 @@
 
 package org.apache.streampipes.dataexplorer;
 
+import org.apache.streampipes.manager.permission.DataLakePermissionManager;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import 
org.apache.streampipes.model.datalake.DataLakeMeasureSchemaUpdateStrategy;
 import org.apache.streampipes.model.schema.EventProperty;
 import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.api.IPermissionStorage;
 import org.apache.streampipes.test.generator.EventPropertyPrimitiveTestBuilder;
 import org.apache.streampipes.test.generator.EventSchemaTestBuilder;
 import org.apache.streampipes.vocabulary.XSD;
@@ -47,22 +49,28 @@ public class DataExplorerSchemaManagementTest {
   public static final String OLD_PROPERTY = "oldProperty";
 
   private CRUDStorage<DataLakeMeasure> dataLakeStorageMock;
+  private DataLakePermissionManager permissionManagerMock;
 
   @BeforeEach
   public void setUp() {
     dataLakeStorageMock = mock(CRUDStorage.class);
+    IPermissionStorage permissionStorageMock = mock(IPermissionStorage.class);
+    this.permissionManagerMock = new 
DataLakePermissionManager(permissionStorageMock);
   }
 
   @Test
   public void createMeasurementThatNotExisted() {
     when(dataLakeStorageMock.findAll()).thenReturn(List.of());
-    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock);
+    var schemaManagement = new DataExplorerSchemaManagement(
+        dataLakeStorageMock,
+        permissionManagerMock
+    );
 
     var oldMeasure = getSampleMeasure(
         DataLakeMeasureSchemaUpdateStrategy.UPDATE_SCHEMA,
         List.of()
     );
-    var resultingMeasure = 
schemaManagement.createOrUpdateMeasurement(oldMeasure);
+    var resultingMeasure = 
schemaManagement.createOrUpdateMeasurement(oldMeasure,null);
 
     assertEquals(oldMeasure.getMeasureName(), 
resultingMeasure.getMeasureName());
     verify(dataLakeStorageMock, Mockito.times(1))
@@ -82,11 +90,11 @@ public class DataExplorerSchemaManagementTest {
 
     when(dataLakeStorageMock.findAll()).thenReturn(List.of(oldMeasure));
     when(dataLakeStorageMock.getElementById(any())).thenReturn(oldMeasure);
-    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock);
+    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock, permissionManagerMock);
 
     var newMeasure = 
getNewMeasure(DataLakeMeasureSchemaUpdateStrategy.UPDATE_SCHEMA);
 
-    var resultMeasure = schemaManagement.createOrUpdateMeasurement(newMeasure);
+    var resultMeasure = 
schemaManagement.createOrUpdateMeasurement(newMeasure,null);
 
     assertEquals(newMeasure.getMeasureName(), resultMeasure.getMeasureName());
     verify(dataLakeStorageMock, Mockito.times(1))
@@ -108,10 +116,10 @@ public class DataExplorerSchemaManagementTest {
     );
     when(dataLakeStorageMock.findAll()).thenReturn(List.of(oldMeasure));
     when(dataLakeStorageMock.getElementById(any())).thenReturn(oldMeasure);
-    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock);
+    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock, permissionManagerMock);
     var newMeasure = 
getNewMeasure(DataLakeMeasureSchemaUpdateStrategy.EXTEND_EXISTING_SCHEMA);
 
-    var resultMeasure = schemaManagement.createOrUpdateMeasurement(newMeasure);
+    var resultMeasure = 
schemaManagement.createOrUpdateMeasurement(newMeasure,null);
 
     assertEquals(newMeasure.getMeasureName(), resultMeasure.getMeasureName());
     verify(dataLakeStorageMock, Mockito.times(1)).updateElement(any());
@@ -133,11 +141,11 @@ public class DataExplorerSchemaManagementTest {
     when(dataLakeStorageMock.findAll()).thenReturn(List.of(oldMeasure));
     when(dataLakeStorageMock.getElementById(any())).thenReturn(oldMeasure);
 
-    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock);
+    var schemaManagement = new 
DataExplorerSchemaManagement(dataLakeStorageMock, permissionManagerMock);
 
     var newMeasure = 
getNewMeasure(DataLakeMeasureSchemaUpdateStrategy.EXTEND_EXISTING_SCHEMA);
 
-    var resultMeasure = schemaManagement.createOrUpdateMeasurement(newMeasure);
+    var resultMeasure = 
schemaManagement.createOrUpdateMeasurement(newMeasure,null);
     assertEquals(newMeasure.getMeasureName(), resultMeasure.getMeasureName());
     verify(dataLakeStorageMock, Mockito.times(1)).updateElement(any());
     assertEquals(
diff --git 
a/streampipes-extensions/streampipes-sinks-internal-jvm/src/main/java/org/apache/streampipes/sinks/internal/jvm/datalake/DataLakeSink.java
 
b/streampipes-extensions/streampipes-sinks-internal-jvm/src/main/java/org/apache/streampipes/sinks/internal/jvm/datalake/DataLakeSink.java
index eb4565271d..0a1b5bf1ea 100644
--- 
a/streampipes-extensions/streampipes-sinks-internal-jvm/src/main/java/org/apache/streampipes/sinks/internal/jvm/datalake/DataLakeSink.java
+++ 
b/streampipes-extensions/streampipes-sinks-internal-jvm/src/main/java/org/apache/streampipes/sinks/internal/jvm/datalake/DataLakeSink.java
@@ -126,8 +126,11 @@ public class DataLakeSink implements IStreamPipesDataSink, 
SupportsRuntimeConfig
       
measure.setSchemaUpdateStrategy(DataLakeMeasureSchemaUpdateStrategy.UPDATE_SCHEMA);
     }
 
+    var userSid = parameters.getModel().getCorrespondingUser();
+    var client = runtimeContext.getStreamPipesClient().onBehalfOf(userSid);
+
     measure = new DataExplorerDispatcher().getDataExplorerManager()
-                                          
.getMeasurementSanitizer(runtimeContext.getStreamPipesClient(), measure)
+                                          .getMeasurementSanitizer(client, 
measure)
                                           .sanitizeAndRegister();
 
     this.timeSeriesStore = new TimeSeriesStore(
diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/DefaultPrivilege.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/DefaultPrivilege.java
index c445fb5240..7c4014aa56 100644
--- 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/DefaultPrivilege.java
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/DefaultPrivilege.java
@@ -33,6 +33,10 @@ public enum DefaultPrivilege {
   
PRIVILEGE_READ_PIPELINE_ELEMENT(Constants.PRIVILEGE_READ_PIPELINE_ELEMENT_VALUE),
   
PRIVILEGE_WRITE_PIPELINE_ELEMENT(Constants.PRIVILEGE_WRITE_PIPELINE_ELEMENT_VALUE),
 
+  // Datasets
+  PRIVILEGE_READ_DATASET(Constants.PRIVILEGE_READ_DATASET_VALUE),
+  PRIVILEGE_WRITE_DATASET(Constants.PRIVILEGE_WRITE_DATASET_VALUE),
+
   // Dashboard
   PRIVILEGE_READ_DASHBOARD(Constants.PRIVILEGE_READ_DASHBOARD_VALUE),
   PRIVILEGE_WRITE_DASHBOARD(Constants.PRIVILEGE_WRITE_DASHBOARD_VALUE),
@@ -77,6 +81,9 @@ public enum DefaultPrivilege {
     public static final String PRIVILEGE_READ_PIPELINE_ELEMENT_VALUE = 
"PRIVILEGE_READ_PIPELINE_ELEMENT";
     public static final String PRIVILEGE_WRITE_PIPELINE_ELEMENT_VALUE = 
"PRIVILEGE_WRITE_PIPELINE_ELEMENT";
 
+    public static final String PRIVILEGE_READ_DATASET_VALUE = 
"PRIVILEGE_READ_DATASET";
+    public static final String PRIVILEGE_WRITE_DATASET_VALUE  = 
"PRIVILEGE_WRITE_DATASET";
+
     public static final String PRIVILEGE_READ_DASHBOARD_VALUE = 
"PRIVILEGE_READ_DASHBOARD";
     public static final String PRIVILEGE_WRITE_DASHBOARD_VALUE = 
"PRIVILEGE_WRITE_DASHBOARD";
 
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/permission/DataLakePermissionManager.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/permission/DataLakePermissionManager.java
new file mode 100644
index 0000000000..34222c97e8
--- /dev/null
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/permission/DataLakePermissionManager.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.streampipes.manager.permission;
+
+import org.apache.streampipes.model.client.user.Permission;
+import org.apache.streampipes.model.client.user.PermissionBuilder;
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
+import org.apache.streampipes.storage.api.IPermissionStorage;
+
+public class DataLakePermissionManager {
+
+  private final IPermissionStorage permissionStorage;
+
+  public DataLakePermissionManager(IPermissionStorage permissionStorage) {
+    this.permissionStorage = permissionStorage;
+  }
+
+  private Permission createDataLakePermission(String measurement, String 
principalSid) {
+    return PermissionBuilder
+        .create(measurement, DataLakeMeasure.class, principalSid)
+        .build();
+  }
+
+  public void makeAndPersistDataLakePermission(String measurement,
+                                               String ownerSid) {
+
+    Permission p = createDataLakePermission(measurement, ownerSid);
+    permissionStorage.persist(p);
+
+  }
+}
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/storage/PipelineStorageService.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/storage/PipelineStorageService.java
index 09709fac7b..206f819157 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/storage/PipelineStorageService.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/storage/PipelineStorageService.java
@@ -34,6 +34,7 @@ public class PipelineStorageService {
 
   private final Pipeline pipeline;
 
+
   public PipelineStorageService(Pipeline pipeline) {
     this.pipeline = pipeline;
   }
@@ -59,7 +60,6 @@ public class PipelineStorageService {
 
     List<DataSinkInvocation> secs = filter(graphs, DataSinkInvocation.class);
     List<DataProcessorInvocation> sepas = filter(graphs, 
DataProcessorInvocation.class);
-
     pipeline.setSepas(sepas);
     pipeline.setActions(secs);
   }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/AbstractDataLakeResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/AbstractDataLakeResource.java
new file mode 100644
index 0000000000..3fdc5a8998
--- /dev/null
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/AbstractDataLakeResource.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.streampipes.rest.impl.datalake;
+
+import org.apache.streampipes.dataexplorer.api.IDataExplorerSchemaManagement;
+import org.apache.streampipes.dataexplorer.management.DataExplorerDispatcher;
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
+import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
+import org.apache.streampipes.rest.security.SpPermissionEvaluator;
+import org.apache.streampipes.storage.api.IDataLakeMeasureStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import java.util.Objects;
+
+public class AbstractDataLakeResource extends AbstractAuthGuardedRestResource {
+
+  final IDataExplorerSchemaManagement dataLakeMeasureManagement;
+  private final IDataLakeMeasureStorage dataLakeMeasureStorage =
+      StorageDispatcher.INSTANCE.getNoSqlStore().getDataLakeStorage();
+
+  
+
+  public AbstractDataLakeResource() {
+    this.dataLakeMeasureManagement = new 
DataExplorerDispatcher().getDataExplorerManager()
+        .getSchemaManagement();
+  }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasReadAuthority() {
+    return 
isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE);
+  }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasWriteAuthority() {
+    return 
isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_DATASET_VALUE);
+  }
+
+  /**
+   * required by Spring expression, do not delete if IDE shows this as unused
+   * @param measurementName the name of the data lake measure
+   * @param permission read or write privilege
+   * @return true if user has permission, false if not or measure does not 
exist
+   */
+  public boolean checkPermissionByName(String measurementName,
+                                       String permission) {
+
+    var measure = dataLakeMeasureStorage.getByMeasureName(measurementName);
+    if (Objects.nonNull(measure)) {
+      var spPermissionEvaluator = new SpPermissionEvaluator();
+      var authentication = SecurityContextHolder.getContext()
+          .getAuthentication();
+      return spPermissionEvaluator.hasPermission(
+          authentication,
+          measure.getElementId(),
+          permission);
+    }
+    return false;
+  }
+}
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeMeasureResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeMeasureResource.java
index 4c36c5a06c..359efaffe2 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeMeasureResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeMeasureResource.java
@@ -18,15 +18,15 @@
 
 package org.apache.streampipes.rest.impl.datalake;
 
-import org.apache.streampipes.dataexplorer.api.IDataExplorerSchemaManagement;
 import org.apache.streampipes.dataexplorer.management.DataExplorerDispatcher;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
-import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PostFilter;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -43,18 +43,19 @@ import java.util.Objects;
 
 @RestController
 @RequestMapping("/api/v4/datalake/measure")
-public class DataLakeMeasureResource extends AbstractAuthGuardedRestResource {
-
-  private final IDataExplorerSchemaManagement dataLakeMeasureManagement;
+public class DataLakeMeasureResource extends AbstractDataLakeResource {
 
   public DataLakeMeasureResource() {
-    this.dataLakeMeasureManagement = new 
DataExplorerDispatcher().getDataExplorerManager()
-        .getSchemaManagement();
+    super();
   }
 
   @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = 
MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<DataLakeMeasure> addDataLake(@RequestBody 
DataLakeMeasure dataLakeMeasure) {
-    DataLakeMeasure result = 
this.dataLakeMeasureManagement.createOrUpdateMeasurement(dataLakeMeasure);
+    DataLakeMeasure result = 
this.dataLakeMeasureManagement.createOrUpdateMeasurement(
+        dataLakeMeasure,
+        getAuthenticatedUserSid()
+    );
     return ok(result);
   }
 
@@ -68,7 +69,9 @@ public class DataLakeMeasureResource extends 
AbstractAuthGuardedRestResource {
    */
   @Operation(summary = "Retrieve measurement counts", description = "Retrieves 
the entry counts for the specified measurements from the data lake.")
   @GetMapping(path = "/count", produces = MediaType.APPLICATION_JSON_VALUE)
-  public ResponseEntity<Map<String, Integer>> getEntryCountsOfMeasurments(
+  @PreAuthorize("this.hasReadAuthority()")
+  @PostFilter("this.checkPermissionByName(filterObject.key, 'READ')")
+  public Map<String, Integer> getEntryCountsOfMeasurements(
       @Parameter(description = "A list of measurement names to return the 
count.") @RequestParam(value = "measurementNames") List<String> 
measurementNames,
       @Parameter(description = "The number of days from today where the count 
should start") @RequestParam(value = "daysBack", defaultValue = "-1") int 
daysBack) {
     var allMeasurements = this.dataLakeMeasureManagement.getAllMeasurements();
@@ -79,10 +82,11 @@ public class DataLakeMeasureResource extends 
AbstractAuthGuardedRestResource {
             measurementNames,
             daysBack)
         .countMeasurementSizes();
-    return ok(result);
+    return result;
   }
 
   @GetMapping(path = "{id}", produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasReadAuthority() and hasPermission(#elementId, 
'READ')")
   public ResponseEntity<?> getDataLakeMeasure(@PathVariable("id") String 
elementId) {
     var measure = this.dataLakeMeasureManagement.getById(elementId);
     if (Objects.nonNull(measure)) {
@@ -93,6 +97,7 @@ public class DataLakeMeasureResource extends 
AbstractAuthGuardedRestResource {
   }
 
   @GetMapping(path = "byName/{measureName}", produces = 
MediaType.APPLICATION_JSON_VALUE)
+ @PreAuthorize("this.hasReadAuthority() and 
this.checkPermissionByName(#measureName, 'READ')")
   public ResponseEntity<?> getDataLakeMeasureName(@PathVariable("measureName") 
String measureName) {
     var measure = 
this.dataLakeMeasureManagement.getExistingMeasureByName(measureName);
     if (Objects.nonNull(measure)) {
@@ -103,6 +108,7 @@ public class DataLakeMeasureResource extends 
AbstractAuthGuardedRestResource {
   }
 
   @PutMapping(path = "{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
+   @PreAuthorize("this.hasWriteAuthority() and hasPermission(#elementId, 
'WRITE')")
   public ResponseEntity<?> updateDataLakeMeasure(
       @PathVariable("id") String elementId,
       @RequestBody DataLakeMeasure measure) {
@@ -118,6 +124,7 @@ public class DataLakeMeasureResource extends 
AbstractAuthGuardedRestResource {
   }
 
   @DeleteMapping(path = "{id}", produces = MediaType.APPLICATION_JSON_VALUE)
+   @PreAuthorize("this.hasWriteAuthority() and hasPermission(#elementId, 
'READ')")
   public ResponseEntity<?> deleteDataLakeMeasure(@PathVariable("id") String 
elementId) {
     try {
       this.dataLakeMeasureManagement.deleteMeasurement(elementId);
@@ -126,4 +133,5 @@ public class DataLakeMeasureResource extends 
AbstractAuthGuardedRestResource {
       return badRequest(e.getMessage());
     }
   }
+
 }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResource.java
index 84713d2889..5332a92bbb 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResource.java
@@ -20,7 +20,6 @@ package org.apache.streampipes.rest.impl.datalake;
 
 import org.apache.streampipes.commons.exceptions.SpRuntimeException;
 import org.apache.streampipes.dataexplorer.api.IDataExplorerQueryManagement;
-import org.apache.streampipes.dataexplorer.api.IDataExplorerSchemaManagement;
 import org.apache.streampipes.dataexplorer.export.OutputFormat;
 import org.apache.streampipes.dataexplorer.management.DataExplorerDispatcher;
 import org.apache.streampipes.export.DataLakeExportManager;
@@ -31,7 +30,6 @@ import org.apache.streampipes.model.datalake.SpQueryResult;
 import org.apache.streampipes.model.datalake.param.ProvidedRestQueryParams;
 import org.apache.streampipes.model.message.Notifications;
 import org.apache.streampipes.model.monitoring.SpLogMessage;
-import org.apache.streampipes.rest.core.base.impl.AbstractRestResource;
 import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.rest.shared.exception.SpMessageException;
 
@@ -48,6 +46,7 @@ import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PostFilter;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -89,30 +88,26 @@ import static 
org.apache.streampipes.model.datalake.param.SupportedRestQueryPara
 
 @RestController
 @RequestMapping("/api/v4/datalake")
-public class DataLakeResource extends AbstractRestResource {
+public class DataLakeResource extends AbstractDataLakeResource {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(DataLakeResource.class);
   private final IDataExplorerQueryManagement dataExplorerQueryManagement;
-  private final IDataExplorerSchemaManagement dataExplorerSchemaManagement;
   private static DataLakeExportManager dataLakeExportManager = new 
DataLakeExportManager();
 
   public DataLakeResource() {
-    this.dataExplorerSchemaManagement = new DataExplorerDispatcher()
-        .getDataExplorerManager()
-        .getSchemaManagement();
+    super();
     this.dataExplorerQueryManagement = new DataExplorerDispatcher()
         .getDataExplorerManager()
-        .getQueryManagement(this.dataExplorerSchemaManagement);
+        .getQueryManagement(this.dataLakeMeasureManagement);
   }
 
   public DataLakeResource(IDataExplorerQueryManagement 
dataExplorerQueryManagement) {
+    super();
     this.dataExplorerQueryManagement = dataExplorerQueryManagement;
-    this.dataExplorerSchemaManagement = new DataExplorerDispatcher()
-        .getDataExplorerManager()
-        .getSchemaManagement();
   }
 
   @DeleteMapping(path = "/measurements/{measurementID}")
+  @PreAuthorize("this.hasWriteAuthority() and 
this.checkPermissionByName(#measurementID, 'WRITE')")
   @Operation(summary = "Remove data from a single measurement series with 
given id", tags = {
       "Data Lake" }, responses = {
           @ApiResponse(responseCode = "200", description = "Data from 
measurement series successfully removed"),
@@ -133,6 +128,7 @@ public class DataLakeResource extends AbstractRestResource {
   }
 
   @DeleteMapping(path = "/measurements/{measurementID}/drop")
+  @PreAuthorize("this.hasWriteAuthority() and 
this.checkPermissionByName(#measurementID, 'WRITE')")
   @Operation(summary = "Drop a single measurement series with given id from 
Data Lake and "
       + "remove related event property", tags = {
           "Data Lake" }, responses = {
@@ -144,7 +140,7 @@ public class DataLakeResource extends AbstractRestResource {
     boolean isSuccessDataLake = 
this.dataExplorerQueryManagement.deleteData(measurementID);
 
     if (isSuccessDataLake) {
-      boolean isSuccessEventProperty = 
this.dataExplorerSchemaManagement.deleteMeasurementByName(measurementID);
+      boolean isSuccessEventProperty = 
this.dataLakeMeasureManagement.deleteMeasurementByName(measurementID);
       if (isSuccessEventProperty) {
         return ok();
       } else {
@@ -158,16 +154,19 @@ public class DataLakeResource extends 
AbstractRestResource {
           .body("Measurement series with given id not found.");
     }
   }
-
+  
   @GetMapping(path = "/measurements", produces = 
MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Get a list of all measurement series", tags = { "Data 
Lake" }, responses = {
       @ApiResponse(responseCode = "200", description = "array of stored 
measurement series", content = @Content(array = @ArraySchema(schema = 
@Schema(implementation = DataLakeMeasure.class)))) })
-  public ResponseEntity<List<DataLakeMeasure>> getAll() {
-    List<DataLakeMeasure> allMeasurements = 
this.dataExplorerSchemaManagement.getAllMeasurements();
-    return ok(allMeasurements);
+   @PreAuthorize("this.hasReadAuthority()")
+     @PostFilter("hasPermission(filterObject.elementId, 'READ')")
+      public  List<DataLakeMeasure> getAll() {
+    List<DataLakeMeasure> allMeasurements = 
this.dataLakeMeasureManagement.getAllMeasurements();
+    return allMeasurements;
   }
 
   @GetMapping(path = "/measurements/{measurementId}/tags", produces = 
MediaType.APPLICATION_JSON_VALUE)
+   @PreAuthorize("this.hasReadAuthority() and 
this.checkPermissionByName(#measurementId, 'READ')")
   public ResponseEntity<Map<String, Object>> 
getTagValues(@PathVariable("measurementId") String measurementId,
       @RequestParam("fields") String fields) {
     Map<String, Object> tagValues = 
dataExplorerQueryManagement.getTagValues(measurementId, fields);
@@ -175,6 +174,7 @@ public class DataLakeResource extends AbstractRestResource {
   }
 
   @GetMapping(path = "/measurements/{measurementID}", produces = 
MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasReadAuthority() and 
this.checkPermissionByName(#measurementID, 'READ')")
   @Operation(summary = "Get data from a single measurement series by a given 
id", tags = { "Data Lake" }, responses = {
       @ApiResponse(responseCode = "400", description = "Measurement series 
with given id and requested query specification not found"),
       @ApiResponse(responseCode = "200", description = "requested data", 
content = @Content(schema = @Schema(implementation = DataSeries.class))) })
@@ -215,6 +215,7 @@ public class DataLakeResource extends AbstractRestResource {
 
   @PostMapping(path = "/query", produces = MediaType.APPLICATION_JSON_VALUE, 
consumes = MediaType.APPLICATION_JSON_VALUE)
   public ResponseEntity<List<SpQueryResult>> getData(@RequestBody 
List<Map<String, String>> queryParams) {
+    //TODO
     var results = queryParams
         .stream()
         .map(qp -> new ProvidedRestQueryParams(qp.get("measureName"), qp))
@@ -225,6 +226,7 @@ public class DataLakeResource extends AbstractRestResource {
   }
 
   @GetMapping(path = "/measurements/{measurementID}/download", produces = 
MediaType.APPLICATION_OCTET_STREAM_VALUE)
+  @PreAuthorize("this.hasReadAuthority() and 
this.checkPermissionByName(#measurementID, 'READ')")
   @Operation(summary = "Download data from a single measurement series by a 
given id", tags = {
       "Data Lake" }, responses = {
           @ApiResponse(responseCode = "400", description = "Measurement series 
with given id and requested query specification not found"),
@@ -279,6 +281,7 @@ public class DataLakeResource extends AbstractRestResource {
   }
 
   @PostMapping(path = "/measurements/{measurementID}", produces = 
MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasWriteAuthority()")
   @Operation(summary = "Store a measurement series to a data lake with the 
given id", tags = {
       "Data Lake" }, responses = {
           @ApiResponse(responseCode = "400", description = "Can't store the 
given data to this data lake"),
@@ -298,6 +301,7 @@ public class DataLakeResource extends AbstractRestResource {
   }
 
   @DeleteMapping(path = "/measurements")
+  @PreAuthorize(AuthConstants.IS_ADMIN_ROLE)
   @Operation(summary = "Remove all stored measurement series from Data Lake", 
tags = { "Data Lake" }, responses = {
       @ApiResponse(responseCode = "200", description = "All measurement series 
successfully removed") })
   public ResponseEntity<?> removeAll() {
@@ -317,10 +321,10 @@ public class DataLakeResource extends 
AbstractRestResource {
   public ResponseEntity<?> setDataLakeRetention(
       @PathVariable String elementId,
       @RequestBody RetentionTimeConfig retention) {
-    var measure = this.dataExplorerSchemaManagement.getById(elementId);
+    var measure = this.dataLakeMeasureManagement.getById(elementId);
     measure.setRetentionTime(retention);
     try {
-      this.dataExplorerSchemaManagement.updateMeasurement(measure);
+      this.dataLakeMeasureManagement.updateMeasurement(measure);
     } catch (IllegalArgumentException e) {
       return badRequest(e.getMessage());
     }
@@ -329,11 +333,12 @@ public class DataLakeResource extends 
AbstractRestResource {
   }
 
   @DeleteMapping(path = "/{elementId}/cleanup")
+  @PreAuthorize(AuthConstants.IS_ADMIN_ROLE)
   public ResponseEntity<?> deleteDataLakeRetention(@PathVariable String 
elementId) {
-    var measure = this.dataExplorerSchemaManagement.getById(elementId);
+    var measure = this.dataLakeMeasureManagement.getById(elementId);
     measure.deleteRetentionTime();
     try {
-      this.dataExplorerSchemaManagement.updateMeasurement(measure);
+      this.dataLakeMeasureManagement.updateMeasurement(measure);
     } catch (IllegalArgumentException e) {
       return badRequest(e.getMessage());
     }
@@ -348,7 +353,7 @@ public class DataLakeResource extends AbstractRestResource {
       @PathVariable String elementId) {
 
     try {
-      var measure = this.dataExplorerSchemaManagement.getById(elementId);
+      var measure = this.dataLakeMeasureManagement.getById(elementId);
       dataLakeExportManager.cleanupSingleMeasurement(measure);
       return ok();
 
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/PersistedDataStreamResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/PersistedDataStreamResource.java
index 72c0e9cf2c..1bdb55d4fa 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/PersistedDataStreamResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/PersistedDataStreamResource.java
@@ -44,8 +44,8 @@ public class PersistedDataStreamResource extends 
AbstractPipelineExtractionResou
   private static final String MeasureFieldInternalName = "db_measurement";
 
   @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_READ_DATA_EXPLORER_PRIVILEGE)
-  @PostFilter("hasPermission(filterObject.pipelineId, 'READ')")
+  @PreAuthorize(AuthConstants.HAS_READ_DATASET_PRIVILEGE)
+  @PostFilter("hasPermission(filterObject.pipelineId, 'READ') and 
hasPermission(filterObject.measureName, 'READ')")
   public List<DataLakeMeasure> getPersistedDataStreams() {
     return extract(new ArrayList<>(), DataLakeAppId);
   }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/AuthConstants.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/AuthConstants.java
index d3e19c8d56..159c4b83f4 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/AuthConstants.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/AuthConstants.java
@@ -20,6 +20,7 @@ package org.apache.streampipes.rest.security;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_ADAPTER_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_ASSETS_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_DASHBOARD_VALUE;
+import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_DATA_EXPLORER_VIEW_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_FILES_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE;
@@ -28,6 +29,7 @@ import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constant
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_ADAPTER_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_ASSETS_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_DASHBOARD_VALUE;
+import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_DATASET_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_FILES_VALUE;
 import static 
org.apache.streampipes.model.client.user.DefaultPrivilege.Constants.PRIVILEGE_WRITE_GENERIC_STORAGE_VALUE;
@@ -64,6 +66,11 @@ public class AuthConstants {
   public static final String HAS_WRITE_PIPELINE_PRIVILEGE =
       BS + IS_ADMIN_ROLE + OR + HAS_ANY_AUTHORITY + 
PRIVILEGE_WRITE_PIPELINE_VALUE + Q + BE2;
 
+  public static final String HAS_READ_DATASET_PRIVILEGE =
+      BS + IS_ADMIN_ROLE + OR + HAS_ANY_AUTHORITY + 
PRIVILEGE_READ_DATASET_VALUE + Q + BE2;
+  public static final String HAS_WRITE_DATASET_PRIVILEGE =
+      BS + IS_ADMIN_ROLE + OR + HAS_ANY_AUTHORITY + 
PRIVILEGE_WRITE_DATASET_VALUE + Q + BE2;
+
   public static final String HAS_READ_PIPELINE_ELEMENT_PRIVILEGE =
       BS + IS_ADMIN_ROLE + OR + HAS_ANY_AUTHORITY + 
PRIVILEGE_READ_PIPELINE_ELEMENT_VALUE + Q + BE2;
 
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
index 787a5afa92..84018b0b34 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/filter/TokenAuthenticationFilter.java
@@ -19,6 +19,7 @@
 package org.apache.streampipes.service.core.filter;
 
 import org.apache.streampipes.commons.constants.HttpConstants;
+import org.apache.streampipes.model.client.user.DefaultRole;
 import org.apache.streampipes.model.client.user.Principal;
 import org.apache.streampipes.model.client.user.ServiceAccount;
 import org.apache.streampipes.model.client.user.UserAccount;
@@ -54,6 +55,7 @@ import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
 import java.util.Base64;
 import java.util.List;
+import java.util.Objects;
 
 public class TokenAuthenticationFilter extends OncePerRequestFilter {
 
@@ -137,8 +139,14 @@ public class TokenAuthenticationFilter extends 
OncePerRequestFilter {
   private void applySuccessfulAuth(HttpServletRequest request,
                                    String username) {
     Principal user = userStorage.getUser(username);
-    PrincipalUserDetails<?> userDetails = user instanceof UserAccount ? new 
UserAccountDetails((UserAccount) user) :
-        new ServiceAccountDetails((ServiceAccount) user);
+    PrincipalUserDetails<?> userDetails = makeDetails(user);
+    var onBehalfOfHeader = request.getHeader(HttpConstants.X_ON_BEHALF_OF);
+    if (isAdminUser(userDetails) && onBehalfOfHeader != null) {
+      var onBehalfOf = userStorage.getUserById(onBehalfOfHeader);
+      if (onBehalfOf != null) {
+        userDetails = makeDetails(onBehalfOf);
+      }
+    }
     UsernamePasswordAuthenticationToken authentication =
         new UsernamePasswordAuthenticationToken(userDetails, null, 
userDetails.getAuthorities());
     authentication.setDetails(new 
WebAuthenticationDetailsSource().buildDetails(request));
@@ -146,6 +154,17 @@ public class TokenAuthenticationFilter extends 
OncePerRequestFilter {
     SecurityContextHolder.getContext().setAuthentication(authentication);
   }
 
+  private PrincipalUserDetails<?> makeDetails(Principal user) {
+    return user instanceof UserAccount ? new UserAccountDetails((UserAccount) 
user) :
+        new ServiceAccountDetails((ServiceAccount) user);
+  }
+
+  private boolean isAdminUser(PrincipalUserDetails<?> userDetails) {
+    return userDetails.getAuthorities().stream()
+        .anyMatch(a ->
+            Objects.equals(a.getAuthority(), 
DefaultRole.Constants.ROLE_ADMIN_VALUE)
+        );
+  }
 
   private String getJwtFromRequest(HttpServletRequest request) {
     String bearerToken = request.getHeader(HttpConstants.AUTHORIZATION);
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
index 1df07fd3df..fecaedcfb0 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
@@ -34,8 +34,10 @@ import 
org.apache.streampipes.service.core.migrations.v099.AddAssetManagementVie
 import 
org.apache.streampipes.service.core.migrations.v099.AddScriptTemplateViewMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.ComputeCertificateThumbprintMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.CreateAssetPermissionMigration;
+import 
org.apache.streampipes.service.core.migrations.v099.CreateDatasetPermissionMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.ModifyAssetLinkIconMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.MoveAssetContentMigration;
+import 
org.apache.streampipes.service.core.migrations.v099.RemoveDuplicatedAssetPermissions;
 import 
org.apache.streampipes.service.core.migrations.v099.RemoveObsoletePrivilegesMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.UniqueDashboardIdMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.connect.MigrateAdaptersToUseScript;
@@ -73,12 +75,14 @@ public class AvailableMigrations {
         new AddAssetManagementViewMigration(),
         new MoveAssetContentMigration(),
         new CreateAssetPermissionMigration(),
+        new CreateDatasetPermissionMigration(),
         new RemoveObsoletePrivilegesMigration(),
         new UniqueDashboardIdMigration(),
         new AddScriptTemplateViewMigration(),
         new ComputeCertificateThumbprintMigration(),
         new MigrateAdaptersToUseScript(),
-        new ModifyAssetLinkIconMigration()
+        new ModifyAssetLinkIconMigration(),
+        new RemoveDuplicatedAssetPermissions()
     );
   }
 }
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateAssetPermissionMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateAssetPermissionMigration.java
index 7fa6ca071f..546de9ac73 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateAssetPermissionMigration.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateAssetPermissionMigration.java
@@ -26,7 +26,6 @@ import org.apache.streampipes.storage.api.IPermissionStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import java.io.IOException;
-import java.util.List;
 
 public class CreateAssetPermissionMigration implements Migration {
 
@@ -49,7 +48,7 @@ public class CreateAssetPermissionMigration implements 
Migration {
   @Override
   public void executeMigration() throws IOException {
     assetStorage.findAll().forEach(assetModel -> {
-      var existingPermission = 
permissionStorage.getObjectPermissions(List.of(assetModel.getElementId()));
+      var existingPermission = 
permissionStorage.getUserPermissionsForObject(assetModel.getElementId());
       if (existingPermission.isEmpty()) {
         permissionResourceManager.createDefault(
             assetModel.getElementId(),
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateDatasetPermissionMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateDatasetPermissionMigration.java
new file mode 100644
index 0000000000..1155f56083
--- /dev/null
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/CreateDatasetPermissionMigration.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v099;
+
+import org.apache.streampipes.model.datalake.DataLakeMeasure;
+import org.apache.streampipes.model.graph.DataSinkInvocation;
+import org.apache.streampipes.model.pipeline.Pipeline;
+import org.apache.streampipes.model.staticproperty.FreeTextStaticProperty;
+import org.apache.streampipes.resource.management.PermissionResourceManager;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.api.IPermissionStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.Optional;
+
+public class CreateDatasetPermissionMigration implements Migration {
+
+  private final CRUDStorage<DataLakeMeasure> dataLakeStorage;
+  private final CRUDStorage<Pipeline> pipelineStorage;
+  private final IPermissionStorage permissionStorage;
+  private final PermissionResourceManager permissionResourceManager;
+
+  private static final String DATALAKE_APP_ID =
+      "org.apache.streampipes.sinks.internal.jvm.datalake";
+
+  private static final String DB_MEASUREMENT = "db_measurement";
+
+
+  public CreateDatasetPermissionMigration() {
+    this.dataLakeStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getDataLakeStorage();
+    this.pipelineStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineStorageAPI();
+    this.permissionStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage();
+    this.permissionResourceManager = new PermissionResourceManager();
+  }
+
+  @Override
+  public boolean shouldExecute() {
+    return true;
+  }
+
+  @Override
+  public void executeMigration() throws IOException {
+    dataLakeStorage.findAll().forEach(measure -> {
+      var existingPermission = 
permissionStorage.getUserPermissionsForObject(measure.getElementId());
+
+      if (existingPermission.isEmpty()) {
+
+        permissionResourceManager.createDefault(
+            measure.getElementId(),
+            DataLakeMeasure.class,
+            findAssociatedPipelineOwner(measure),
+            true
+        );
+      }
+    });
+  }
+
+  private String findAssociatedPipelineOwner(DataLakeMeasure measure) {
+    String measureName = measure.getMeasureName();
+
+    return pipelineStorage.findAll().stream()
+        .filter(pipeline -> pipeline.getActions() != null)
+        .filter(pipeline ->
+            pipeline.getActions().stream()
+                .filter(Objects::nonNull)
+                .filter(action -> DATALAKE_APP_ID.equals(action.getAppId()))
+                .map(this::extractMeasurement)
+                .flatMap(Optional::stream)
+                .anyMatch(measureName::equals)
+        )
+        .map(pipeline -> getPipelineOwner(pipeline.getPipelineId()))
+        .filter(Objects::nonNull)
+        .findFirst()
+        .orElse(null);
+  }
+
+  private String getPipelineOwner(String pipelineId) {
+    var permission = permissionStorage.getUserPermissionsForObject(pipelineId);
+    if (!permission.isEmpty()) {
+      return permission.get(0).getOwnerSid();
+    }
+    return null;
+  }
+
+  private Optional<String> extractMeasurement(DataSinkInvocation datasink) {
+    return datasink.getStaticProperties().stream()
+        .filter(sp -> DB_MEASUREMENT.equals(sp.getInternalName()))
+        .filter(FreeTextStaticProperty.class::isInstance)
+        .map(FreeTextStaticProperty.class::cast)
+        .map(FreeTextStaticProperty::getValue)
+        .filter(value -> !value.isBlank())
+        .findFirst();
+  }
+
+  @Override
+  public String getDescription() {
+    return "Create default permissions for datasets";
+  }
+}
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/RemoveDuplicatedAssetPermissions.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/RemoveDuplicatedAssetPermissions.java
new file mode 100644
index 0000000000..b2918b23a5
--- /dev/null
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/RemoveDuplicatedAssetPermissions.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v099;
+
+import org.apache.streampipes.model.assets.SpAssetModel;
+import org.apache.streampipes.model.client.user.Permission;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.api.IPermissionStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class RemoveDuplicatedAssetPermissions implements Migration {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(RemoveDuplicatedAssetPermissions.class);
+
+  private final IPermissionStorage permissionStorage =
+      StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage();
+
+  private final CRUDStorage<SpAssetModel> assetStorage =
+      StorageDispatcher.INSTANCE.getNoSqlStore().getAssetStorage();
+
+  @Override
+  public boolean shouldExecute() {
+    return true;
+  }
+
+  @Override
+  public void executeMigration() throws IOException {
+    AtomicInteger deletedPermissions = new AtomicInteger();
+    assetStorage.findAll().forEach(asset -> {
+      var permissions = 
permissionStorage.getUserPermissionsForObject(asset.getElementId());
+      if (permissions.size() > 1) {
+
+        // "Bad" permissions = no owner, empty grantedAuthorities, and public
+        var badPermissions = permissions.stream()
+            .filter(this::isAccidentalPermission)
+            .toList();
+
+        // "Good" permissions = everything else
+        var goodPermissions = permissions.stream()
+            .filter(p -> !isAccidentalPermission(p))
+            .toList();
+
+        // Choose what to keep:
+        // - keep first GOOD if exists
+        // - otherwise keep first BAD (meaning all are bad)
+        var keep = !goodPermissions.isEmpty()
+            ? goodPermissions.get(0)
+            : badPermissions.get(0);
+
+        // Delete everything else
+        for (var p : permissions) {
+          if (!(keep.getElementId().equals(p.getElementId()))) {
+            permissionStorage.deleteElementById(p.getElementId());
+            deletedPermissions.getAndIncrement();
+          }
+        }
+      }
+    });
+    LOG.info("Deleted {} permissions", deletedPermissions.get());
+  }
+
+  private boolean isAccidentalPermission(Permission p) {
+    if (p == null) {
+      return false;
+    }
+
+    var owner = p.getOwnerSid();
+    boolean noOwner = (owner == null) || owner.isBlank();
+
+    var granted = p.getGrantedAuthorities();
+    boolean emptyAuthorities = (granted == null) || granted.isEmpty();
+
+    boolean isPublic = p.isPublicElement();
+
+    return noOwner && emptyAuthorities && isPublic;
+  }
+
+
+  @Override
+  public String getDescription() {
+    return "Remove duplicated asset permissions";
+  }
+}
diff --git 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/PrivilegeManager.java
 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/PrivilegeManager.java
index dd850d5de4..4e76d959d9 100644
--- 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/PrivilegeManager.java
+++ 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/PrivilegeManager.java
@@ -37,6 +37,7 @@ public class PrivilegeManager {
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_READ_NOTIFICATIONS_VALUE),
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_VALUE),
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_ELEMENT_VALUE),
+        
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE),
 
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_ADAPTER_VALUE),
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_ASSETS_VALUE),
@@ -45,7 +46,8 @@ public class PrivilegeManager {
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_FILES_VALUE),
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_GENERIC_STORAGE_VALUE),
         
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_PIPELINE_VALUE),
-        
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_PIPELINE_ELEMENT_VALUE)
+        
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_PIPELINE_ELEMENT_VALUE),
+        
Privilege.create(DefaultPrivilege.Constants.PRIVILEGE_WRITE_DATASET_VALUE)
 
 
 
diff --git 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/RoleManager.java
 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/RoleManager.java
index a30f3a682a..5ca24b5223 100644
--- 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/RoleManager.java
+++ 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authorization/RoleManager.java
@@ -44,12 +44,14 @@ public class RoleManager {
             DefaultPrivilege.Constants.PRIVILEGE_READ_DASHBOARD_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_WRITE_DASHBOARD_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_READ_DATA_EXPLORER_VIEW_VALUE,
-            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE
+            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE
         )),
         
Role.createDefaultRole(DefaultRole.Constants.ROLE_DASHBOARD_USER_VALUE, 
"Dashboard User", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_READ_DASHBOARD_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_READ_DATA_EXPLORER_VIEW_VALUE,
-            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE
+            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE
         )),
         
Role.createDefaultRole(DefaultRole.Constants.ROLE_PIPELINE_ADMIN_VALUE, 
"Pipeline Admin", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_VALUE,
@@ -57,13 +59,16 @@ public class RoleManager {
             DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_ELEMENT_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_READ_FILES_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_WRITE_FILES_VALUE,
-            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE
+            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_WRITE_DATASET_VALUE
         )),
         Role.createDefaultRole(DefaultRole.Constants.ROLE_PIPELINE_USER_VALUE, 
"Pipeline User", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_ELEMENT_VALUE,
             DefaultPrivilege.Constants.PRIVILEGE_READ_FILES_VALUE,
-            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE
+            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE
         )),
         Role.createDefaultRole(DefaultRole.Constants.ROLE_ASSET_ADMIN_VALUE, 
"Asset Admin", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
@@ -82,11 +87,13 @@ public class RoleManager {
         
Role.createDefaultRole(DefaultRole.Constants.ROLE_DATA_EXPLORER_ADMIN_VALUE, 
"Data Explorer Admin", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_READ_DATA_EXPLORER_VIEW_VALUE,
             
DefaultPrivilege.Constants.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW_VALUE,
-            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE
+            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE
         )),
         
Role.createDefaultRole(DefaultRole.Constants.ROLE_DATA_EXPLORER_USER_VALUE, 
"Data Explorer User", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_READ_DATA_EXPLORER_VIEW_VALUE,
-            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE
+            DefaultPrivilege.Constants.PRIVILEGE_READ_GENERIC_STORAGE_VALUE,
+            DefaultPrivilege.Constants.PRIVILEGE_READ_DATASET_VALUE
         )),
         Role.createDefaultRole(DefaultRole.Constants.ROLE_CONNECT_ADMIN_VALUE, 
"Connect Admin", List.of(
             DefaultPrivilege.Constants.PRIVILEGE_WRITE_ADAPTER_VALUE,
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts 
b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
index 4af3b4fa53..f644453904 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
@@ -33,6 +33,10 @@ export class DataExplorerBtns {
         return cy.dataCy('save-dashboard-btn');
     }
 
+    public static discardDashboard() {
+        return cy.dataCy('discard-dashboard-btn');
+    }
+
     public static saveChartsToAssetBtn() {
         return cy
             .dataCy('add-to-Asset-data-view-btn', { timeout: 10000 })
@@ -106,6 +110,10 @@ export class DataExplorerBtns {
         return cy.dataCy('edit-' + widgetName);
     }
 
+    public static viewWidget(widgetName: string) {
+        return cy.dataCy('show-data-view-' + widgetName);
+    }
+
     public static moreOptionsBtn(widgetName) {
         return cy.dataCy('more-options-' + widgetName);
     }
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts 
b/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
index d174295010..e918092423 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
@@ -442,6 +442,13 @@ export class DataExplorerUtils {
             .click();
     }
 
+    public static assertSelectDataSet(dataSet: string) {
+        cy.dataCy('data-explorer-select-data-set')
+            .click()
+            .get('mat-option')
+            .should('contain.text', dataSet);
+    }
+
     /**
      * Checks if in the widget configuration the filters are set or not
      * @param amountOfFilter the amount of filters that should be set. 0 if no 
filter should be visible
diff --git a/ui/cypress/support/utils/dataset/DatasetBtns.ts 
b/ui/cypress/support/utils/dataset/DatasetBtns.ts
new file mode 100644
index 0000000000..36985d2541
--- /dev/null
+++ b/ui/cypress/support/utils/dataset/DatasetBtns.ts
@@ -0,0 +1,23 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+export class DatasetBtns {
+    public static datasetTable() {
+        return cy.dataCy('datalake-settings', { timeout: 10000 });
+    }
+}
diff --git a/ui/cypress/support/utils/dataset/DatasetUtils.ts 
b/ui/cypress/support/utils/dataset/DatasetUtils.ts
new file mode 100644
index 0000000000..fbda2e8b20
--- /dev/null
+++ b/ui/cypress/support/utils/dataset/DatasetUtils.ts
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+import { PermissionUtils } from '../user/PermissionUtils';
+import { DatasetBtns } from './DatasetBtns';
+
+export class DatasetUtils {
+    public static goToDatasets() {
+        cy.visit('#/datasets');
+    }
+
+    public static checkAmountOfDatasets(amount: number) {
+        DatasetUtils.goToDatasets();
+
+        if (amount === 0) {
+            // The wait is needed because the default value is the 
no-table-entries element.
+            // It must be waited till the data is loaded. Once a better 
solution is found, this can be removed.
+            cy.wait(1000);
+            cy.dataCy('no-table-entries').should('be.visible');
+        } else {
+            DatasetBtns.datasetTable().should('have.length', amount);
+        }
+    }
+
+    public static authorizeUserOnDataset(datasetname: string, email: string) {
+        DatasetUtils.goToDatasets();
+        PermissionUtils.authorizeUser(datasetname, email);
+    }
+}
diff --git a/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts 
b/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
new file mode 100644
index 0000000000..c855272410
--- /dev/null
+++ b/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { UserRole } from '../../../src/app/_enums/user-role.enum';
+import { UserUtils } from '../../support/utils/UserUtils';
+import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { User } from '../../support/model/User';
+import { DataExplorerUtils } from 
'../../support/utils/dataExplorer/DataExplorerUtils';
+import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
+import { DataExplorerBtns } from 
'../../support/utils/dataExplorer/DataExplorerBtns';
+import { DatasetUtils } from '../../support/utils/dataset/DatasetUtils';
+import { GeneralUtils } from '../../support/utils/GeneralUtils';
+
+describe('Test Dataset Permissions', () => {
+    const datasetName = 'Persist simulator';
+    let datasetUser1: User;
+    let datasetAdmin1: User;
+    let datasetAdmin2: User;
+    let chartAdmin1: User;
+    let chartUser1: User;
+    let dashboardAdmin1: User;
+
+    beforeEach('Setup Test', () => {
+        cy.initStreamPipesTest();
+
+        datasetUser1 = UserUtils.createUser(
+            'datasetUser1',
+            UserRole.ROLE_PIPELINE_USER,
+        );
+
+        datasetAdmin1 = UserUtils.createUser(
+            'datasetAdmin1',
+            UserRole.ROLE_CONNECT_ADMIN,
+            UserRole.ROLE_PIPELINE_ADMIN,
+        );
+
+        datasetAdmin2 = UserUtils.createUser(
+            'datasetAdmin2',
+            UserRole.ROLE_PIPELINE_ADMIN,
+        );
+
+        chartAdmin1 = UserUtils.createUser(
+            'chartAdmin1',
+            UserRole.ROLE_DATA_EXPLORER_ADMIN,
+        );
+
+        chartUser1 = UserUtils.createUser(
+            'chartUser1',
+            UserRole.ROLE_DATA_EXPLORER_USER,
+        );
+
+        dashboardAdmin1 = UserUtils.createUser(
+            'dashboardAdmin1',
+            UserRole.ROLE_DASHBOARD_ADMIN,
+        );
+    });
+
+    it('Dataset is not shared with other users', () => {
+        generateDataset();
+
+        assertDatasetIsVisibleAndEditableCanChangePermissions(
+            UserUtils.adminUser,
+        );
+
+        assertDatasetIsNotVisible(datasetUser1);
+
+        UserUtils.switchUser(datasetUser1);
+
+        assertDatasetIsNotVisible(datasetAdmin2);
+    });
+
+    it('Datasets only usable in charts if permissions were configured', () => {
+        generateDataset();
+
+        UserUtils.switchUser(chartAdmin1);
+
+        assertDatasetAvailabilityInCharts(false);
+
+        authUserOnDataset('[email protected]');
+
+        UserUtils.switchUser(chartAdmin1);
+
+        assertDatasetAvailabilityInCharts(true);
+
+        DataExplorerUtils.goToDatalake();
+
+        PermissionUtils.authorizeUser(
+            'test',
+            '[email protected]',
+        );
+
+        // I am not quite sure why this is needed, but without it the test 
fails
+        cy.dataCy('confirm-delete', { timeout: 10000 }).click();
+
+        UserUtils.switchUser(chartUser1);
+
+        DataExplorerUtils.checkAmountOfCharts(1);
+
+        GeneralUtils.openMenuForRow('test');
+
+        DataExplorerBtns.viewWidget('test').click();
+
+        assertAlertBanner(true);
+
+        assertDatasetIsNotVisible(chartUser1);
+
+        authUserOnDataset('[email protected]');
+
+        UserUtils.switchUser(chartUser1);
+
+        DataExplorerUtils.checkAmountOfCharts(1);
+
+        GeneralUtils.openMenuForRow('test');
+
+        DataExplorerBtns.viewWidget('test').click();
+
+        assertAlertBanner(false);
+    });
+
+    it('Data only shown in dashboard if permissions were configured', () => {
+        generateDataset();
+
+        authUserOnDataset('[email protected]');
+
+        UserUtils.switchUser(chartAdmin1);
+
+        assertDatasetAvailabilityInCharts(true);
+
+        PermissionUtils.authorizeUser(
+            'test',
+            '[email protected]',
+        );
+
+        UserUtils.switchUser(dashboardAdmin1);
+
+        generateDashboard('TestDB');
+
+        assertAlertBanner(true);
+
+        DataExplorerBtns.discardDashboard().click();
+
+        assertDatasetIsNotVisible(dashboardAdmin1);
+
+        authUserOnDataset('[email protected]');
+
+        UserUtils.switchUser(dashboardAdmin1);
+
+        generateDashboard('TestDB2');
+
+        assertAlertBanner(false);
+    });
+
+    function assertDatasetAvailabilityInCharts(available: boolean) {
+        DataExplorerUtils.goToDatalake();
+        DataExplorerBtns.openNewDataViewBtn().click();
+        if (!available) {
+            cy.get('sp-alert-banner').should('be.visible');
+        } else {
+            DataExplorerUtils.assertSelectDataSet('simulator');
+            DataExplorerUtils.addDataViewAndTableWidget(
+                'test',
+                'simulator',
+                true,
+            );
+            DataExplorerUtils.saveDataViewConfiguration();
+        }
+    }
+
+    function generateDataset() {
+        UserUtils.switchUser(datasetAdmin1);
+        ConnectUtils.addMachineDataSimulator('simulator', true);
+    }
+
+    function generateDashboard(name: string) {
+        DataExplorerUtils.goToDashboard();
+        DataExplorerUtils.createNewDashboard(name);
+        DataExplorerUtils.editDashboard(name);
+        DataExplorerUtils.addDataViewToDashboard('test', true);
+    }
+    function assertDatasetIsVisibleAndEditableCanChangePermissions(user: User) 
{
+        UserUtils.switchUser(user);
+        DatasetUtils.goToDatasets();
+        DatasetUtils.checkAmountOfDatasets(1);
+        PermissionUtils.validateUserCanChangePermissions(datasetName);
+    }
+
+    function assertDatasetIsNotVisible(user: User) {
+        UserUtils.switchUser(user);
+        DatasetUtils.goToDatasets();
+        DatasetUtils.checkAmountOfDatasets(0);
+    }
+
+    function assertAlertBanner(exists: boolean) {
+        if (exists) {
+            cy.dataCy('sp-alert-banner-error').should('exist');
+        } else {
+            cy.dataCy('sp-alert-banner-error').should('not.exist');
+        }
+    }
+
+    function authUserOnDataset(email: string) {
+        UserUtils.switchUser(datasetAdmin1);
+
+        DatasetUtils.authorizeUserOnDataset('simulator', email);
+    }
+});
diff --git a/ui/deployment/modules.yml b/ui/deployment/modules.yml
index f43600590b..5eb66406be 100644
--- a/ui/deployment/modules.yml
+++ b/ui/deployment/modules.yml
@@ -37,7 +37,7 @@ spDatasets:
     title: 'Datasets'
     description: 'Manage datasets'
     icon: 'dataset'
-    privileges: '[UserRole.ROLE_ADMIN]'
+    privileges: '[UserPrivilege.PRIVILEGE_READ_DATASET, 
UserPrivilege.PRIVILEGE_WRITE_DATASET]'
     pageNames: 'PageName.DATASETS'
     showStatusBox: false
     category: 'management'
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
index cc55f6fce2..23efa1c963 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
@@ -16,12 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2026-01-13 
08:54:20.
+// Generated using typescript-generator version 3.2.1263 on 2026-01-15 
16:03:19.
 
-import { Storable } from './platform-services';
+import { Storable } from './streampipes-model';
 
 export class Group implements Storable {
     alternateIds: string[];
@@ -309,6 +310,8 @@ export type DefaultPrivilege =
     | 'PRIVILEGE_WRITE_ADAPTER'
     | 'PRIVILEGE_READ_PIPELINE_ELEMENT'
     | 'PRIVILEGE_WRITE_PIPELINE_ELEMENT'
+    | 'PRIVILEGE_READ_DATASET'
+    | 'PRIVILEGE_WRITE_DATASET'
     | 'PRIVILEGE_READ_DASHBOARD'
     | 'PRIVILEGE_WRITE_DASHBOARD'
     | 'PRIVILEGE_READ_DATA_EXPLORER_VIEW'
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index 558d5816a7..a0fced120e 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -16,10 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2026-01-13 
08:54:16.
+// Generated using typescript-generator version 3.2.1263 on 2026-01-15 
16:03:01.
 
 export class NamedStreamPipesEntity implements Storable {
     '@class':
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html
 
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html
index 63615e61a1..543abca116 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-exception-message/sp-exception-message.component.html
@@ -19,6 +19,7 @@
 <div fxFlex="100" fxLayout="row" class="w-100" data-cy="display-exception">
     <sp-alert-banner
         type="error"
+        data-cy="sp-alert-banner-error"
         class="w-100"
         [showDetailsButton]="showDetails"
         (detailsButtonClicked)="openDetailsDialog()"
diff --git a/ui/src/app/_enums/user-privilege.enum.ts 
b/ui/src/app/_enums/user-privilege.enum.ts
index acde97b208..1de793b088 100644
--- a/ui/src/app/_enums/user-privilege.enum.ts
+++ b/ui/src/app/_enums/user-privilege.enum.ts
@@ -45,4 +45,7 @@ export enum UserPrivilege {
 
     PRIVILEGE_READ_LABELS = 'PRIVILEGE_READ_LABELS',
     PRIVILEGE_WRITE_LABELS = 'PRIVILEGE_WRITE_LABELS',
+
+    PRIVILEGE_READ_DATASET = 'PRIVILEGE_READ_DATASET',
+    PRIVILEGE_WRITE_DATASET = 'PRIVILEGE_WRITE_DATASET',
 }
diff --git 
a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
 
b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
index 284bb1261d..fee14dfa0a 100644
--- 
a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
+++ 
b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
@@ -95,7 +95,17 @@
             </ng-container>
 
             <ng-template spTableActions let-element>
-                <button mat-menu-item (click)="openDataView(element, false)">
+                <button
+                    mat-menu-item
+                    [attr.data-cy]="
+                        'show-data-view-' +
+                        element.baseAppearanceConfig.widgetTitle.replaceAll(
+                            ' ',
+                            ''
+                        )
+                    "
+                    (click)="openDataView(element, false)"
+                >
                     <mat-icon>visibility</mat-icon>
                     <span>{{ 'Show' | translate }}</span>
                 </button>
diff --git 
a/ui/src/app/configuration/files/file-overview/file-overview.component.html 
b/ui/src/app/configuration/files/file-overview/file-overview.component.html
index d0b91930ff..a7fa9267f5 100644
--- a/ui/src/app/configuration/files/file-overview/file-overview.component.html
+++ b/ui/src/app/configuration/files/file-overview/file-overview.component.html
@@ -20,25 +20,26 @@
     <ng-container matColumnDef="filename">
         <th mat-header-cell *matHeaderCellDef>{{ 'Filename' | translate }}</th>
         <td mat-cell *matCellDef="let fileMetadata">
-            <h4>{{ fileMetadata.filename }}</h4>
+            <span>{{ fileMetadata.filename }}</span>
         </td>
     </ng-container>
     <ng-container matColumnDef="filetype">
         <th mat-header-cell *matHeaderCellDef>{{ 'Filetype' | translate }}</th>
         <td mat-cell *matCellDef="let fileMetadata">
-            <span
-                class="filetype-container"
-                [style.background-color]="getFileColor(fileMetadata.filetype)"
-                >{{ fileMetadata.filetype }}</span
+            <sp-label
+                [color]="getFileColor(fileMetadata.filetype)"
+                size="medium"
+                [labelText]="fileMetadata.filetype"
             >
+            </sp-label>
         </td>
     </ng-container>
     <ng-container matColumnDef="uploaded">
         <th mat-header-cell *matHeaderCellDef>{{ 'Uploaded' | translate }}</th>
         <td mat-cell *matCellDef="let fileMetadata">
-            <h5>
+            <span>
                 {{ fileMetadata.createdAt | date: 'dd.MM.yyyy HH:mm' }}
-            </h5>
+            </span>
         </td>
     </ng-container>
 
diff --git 
a/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
 
b/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
index 0d85549398..ff335b85ae 100644
--- 
a/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
+++ 
b/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
@@ -39,6 +39,7 @@
                 [matTooltip]="'Discard' | translate"
                 class="mat-basic mr-10 edit-menu-btn"
                 (click)="discardDashboardEmitter.emit()"
+                data-cy="discard-dashboard-btn"
             >
                 <i class="material-icons">undo</i>
                 <span>&nbsp;{{ 'Discard' | translate }}</span>
diff --git 
a/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.html
 
b/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.html
index a6aa18e361..c181b22821 100644
--- 
a/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.html
+++ 
b/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.html
@@ -163,75 +163,114 @@
                         {{ 'Retention' | translate }}
                     </th>
                     <td mat-cell *matCellDef="let configurationEntry">
-                        <button
-                            color="accent"
-                            mat-icon-button
-                            [matTooltip]="'Set retention rate' | translate"
-                            data-cy="datalake-retention-btn"
-                            matTooltipPosition="above"
-                            (click)="
-                                openRetentionDialog(
-                                    configurationEntry.elementId
-                                )
-                            "
-                        >
-                            <i
-                                class="material-icons"
-                                [ngStyle]="{
-                                    color: configurationEntry?.retention
-                                        ? 'var(--color-success)'
-                                        : 'var(--color-neutral',
-                                }"
-                                >history</i
+                        <div fxLayout="row">
+                            <span
+                                fxFlex
+                                fxFlexOrder="3"
+                                fxLayout="row"
+                                fxLayoutAlign="start center"
                             >
-                        </button>
-                        @if (
-                            
configurationEntry?.retention?.retentionExportConfig
-                                ?.retentionLog?.length > 0
-                        ) {
-                            <button
-                                color="accent"
-                                mat-icon-button
-                                [matTooltip]="
-                                    ('Open Retention Log' | translate) +
-                                    (configurationEntry?.retention
-                                        ?.retentionExportConfig?.lastExport
-                                        ? ' • ' +
-                                          (configurationEntry.retention
-                                              .retentionExportConfig.lastExport
-                                              | date: 'yyyy-MM-dd HH:mm:ss')
-                                        : '')
-                                "
-                                data-cy="datalake-retention-log-btn"
-                                matTooltipPosition="above"
-                                (click)="
-                                    openRetentionLog(
-                                        configurationEntry?.retention
-                                            .retentionExportConfig.retentionLog
-                                    )
-                                "
+                                @if (isAdmin) {
+                                    <button
+                                        color="accent"
+                                        mat-icon-button
+                                        [matTooltip]="
+                                            'Set retention rate' | translate
+                                        "
+                                        data-cy="datalake-retention-btn"
+                                        matTooltipPosition="above"
+                                        (click)="
+                                            openRetentionDialog(
+                                                configurationEntry.elementId
+                                            )
+                                        "
+                                    >
+                                        <i
+                                            class="material-icons"
+                                            [ngStyle]="{
+                                                color: 
configurationEntry?.retention
+                                                    ? 'var(--color-success)'
+                                                    : 'var(--color-neutral',
+                                            }"
+                                            >history</i
+                                        >
+                                    </button>
+                                } @else {
+                                    <p>-</p>
+                                }
+                            </span>
+                        </div>
+                    </td>
+                </ng-container>
+
+                <ng-container matColumnDef="retentionlog">
+                    <th mat-header-cell *matHeaderCellDef>
+                        {{ 'Retention Log' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        <div fxLayout="row">
+                            <span
+                                fxFlex
+                                fxFlexOrder="3"
+                                fxLayout="row"
+                                fxLayoutAlign="start center"
                             >
-                                <i
-                                    class="material-icons"
-                                    [ngStyle]="{
-                                        color:
-                                            configurationEntry?.retention
+                                @if (configurationEntry?.retention) {
+                                    <button
+                                        color="accent"
+                                        mat-icon-button
+                                        [matTooltip]="
+                                            ('Open Retention Log' | translate) 
+
+                                            (configurationEntry?.retention
                                                 ?.retentionExportConfig
-                                                ?.retentionLog?.length &&
-                                            configurationEntry.retention
-                                                .retentionExportConfig
-                                                .retentionLog[
-                                                configurationEntry.retention
+                                                ?.lastExport
+                                                ? ' • ' +
+                                                  (configurationEntry.retention
+                                                      .retentionExportConfig
+                                                      .lastExport
+                                                      | date
+                                                          : 'yyyy-MM-dd 
HH:mm:ss')
+                                                : '')
+                                        "
+                                        data-cy="datalake-retention-log-btn"
+                                        matTooltipPosition="above"
+                                        (click)="
+                                            openRetentionLog(
+                                                configurationEntry?.retention
                                                     .retentionExportConfig
-                                                    .retentionLog.length - 1
-                                            ].status
-                                                ? 'var(--color-success)'
-                                                : 'var(--color-error)',
-                                    }"
-                                    >list_alt</i
-                                >
-                            </button>
-                        }
+                                                    .retentionLog
+                                            )
+                                        "
+                                    >
+                                        <i
+                                            class="material-icons"
+                                            [ngStyle]="{
+                                                color:
+                                                    configurationEntry
+                                                        ?.retention
+                                                        ?.retentionExportConfig
+                                                        ?.retentionLog
+                                                        ?.length &&
+                                                    
configurationEntry.retention
+                                                        .retentionExportConfig
+                                                        .retentionLog[
+                                                        configurationEntry
+                                                            .retention
+                                                            
.retentionExportConfig
+                                                            .retentionLog
+                                                            .length - 1
+                                                    ].status
+                                                        ? 'green'
+                                                        : 'red',
+                                            }"
+                                            >list_alt</i
+                                        >
+                                    </button>
+                                } @else {
+                                    <p>-</p>
+                                }
+                            </span>
+                        </div>
                     </td>
                 </ng-container>
 
@@ -246,223 +285,241 @@
                             'Download data from index' | translate
                         }}</span>
                     </button>
-                    <button
-                        mat-menu-item
-                        data-cy="datalake-truncate-btn"
-                        (click)="cleanDatalakeIndex(element.name)"
-                    >
-                        <mat-icon>local_fire_department</mat-icon>
-                        <span>{{
-                            'Truncate all data from index' | translate
-                        }}</span>
-                    </button>
-                    <button
-                        mat-menu-item
-                        data-cy="datalake-delete-btn"
-                        [disabled]="!element.remove"
-                        (click)="deleteDatalakeIndex(element.name)"
-                    >
-                        <mat-icon>delete</mat-icon>
-                        <span>{{
-                            'Remove index from database' | translate
-                        }}</span>
-                    </button>
+                    @if (writeAccess) {
+                        <button
+                            mat-menu-item
+                            data-cy="datalake-truncate-btn"
+                            (click)="cleanDatalakeIndex(element.name)"
+                        >
+                            <mat-icon>local_fire_department</mat-icon>
+                            <span>{{
+                                'Truncate all data from index' | translate
+                            }}</span>
+                        </button>
+                        <button
+                            mat-menu-item
+                            data-cy="datalake-delete-btn"
+                            [disabled]="!element.remove"
+                            (click)="deleteDatalakeIndex(element.name)"
+                        >
+                            <mat-icon>delete</mat-icon>
+                            <span>{{
+                                'Remove index from database' | translate
+                            }}</span>
+                        </button>
+                        <button
+                            mat-menu-item
+                            data-cy="open-manage-permissions"
+                            (click)="showPermissionsDialog(element)"
+                        >
+                            <mat-icon>share</mat-icon>
+                            <span>{{ 'Manage permissions' | translate }}</span>
+                        </button>
+                    }
                 </ng-template>
             </sp-table>
         </div>
-
-        <div
-            fxLayout="row"
-            fxFlex="100"
-            class="mt-10 w-100"
-            fxLayoutAlign="start center"
-        >
-            <sp-basic-header-title-component
-                [title]="'Export Providers' | translate"
-                [description]="
-                    'Add, Edit, and Delete export providers used for backing 
up data lakes.'
-                        | translate
-                "
-            ></sp-basic-header-title-component>
-            <span fxFlex></span>
+        @if (isAdmin) {
             <div
-                fxFlex
                 fxLayout="row"
-                fxLayoutAlign="end center"
-                fxLayoutGap="5px"
+                fxFlex="100"
+                class="mt-10 w-100"
+                fxLayoutAlign="start center"
             >
-                <button
-                    mat-flat-button
-                    matTooltip="{{ 'New' | translate }}"
-                    data-cy="new-export-providers"
-                    (click)="createExportProvider(null)"
+                <sp-basic-header-title-component
+                    [title]="'Export Providers' | translate"
+                    [description]="
+                        'Add, Edit, and Delete export providers used for 
backing up data lakes.'
+                            | translate
+                    "
+                ></sp-basic-header-title-component>
+                <span fxFlex></span>
+                <div
+                    fxFlex
+                    fxLayout="row"
+                    fxLayoutAlign="end center"
+                    fxLayoutGap="5px"
                 >
-                    <mat-icon>add</mat-icon>
-                    <span>{{ 'New' | translate }}</span>
-                </button>
-                <button
-                    mat-icon-button
-                    matTooltip="{{ 'Refresh' | translate }}"
-                    data-cy="refresh-export-providers-measures"
-                    (click)="loadAvailableExportProvider()"
-                >
-                    <mat-icon>refresh</mat-icon>
-                </button>
+                    <button
+                        mat-flat-button
+                        matTooltip="{{ 'New' | translate }}"
+                        data-cy="new-export-providers"
+                        (click)="createExportProvider(null)"
+                    >
+                        <mat-icon>add</mat-icon>
+                        <span>{{ 'New' | translate }}</span>
+                    </button>
+                    <button
+                        mat-icon-button
+                        matTooltip="{{ 'Refresh' | translate }}"
+                        data-cy="refresh-export-providers-measures"
+                        (click)="loadAvailableExportProvider()"
+                    >
+                        <mat-icon>refresh</mat-icon>
+                    </button>
+                </div>
             </div>
-        </div>
-        <table
-            fxFlex="100"
-            mat-table
-            data-cy="exportproviders-settings"
-            [dataSource]="dataSourceExport"
-            style="width: 100%"
-            matSort
-        >
-            <ng-container matColumnDef="providertype">
-                <th mat-header-cell mat-sort-header *matHeaderCellDef>
-                    {{ 'Provider Type' | translate }}
-                </th>
-                <td mat-cell *matCellDef="let configurationEntry">
-                    {{ configurationEntry.providerType }}
-                </td>
-            </ng-container>
 
-            <ng-container matColumnDef="endpoint">
-                <th mat-header-cell mat-sort-header *matHeaderCellDef>
-                    {{ 'Endpoint' | translate }}
-                </th>
-                <td mat-cell *matCellDef="let configurationEntry">
-                    {{ configurationEntry.endPoint }}
-                </td>
-            </ng-container>
+            <table
+                fxFlex="100"
+                mat-table
+                data-cy="exportproviders-settings"
+                [dataSource]="dataSourceExport"
+                style="width: 100%"
+                matSort
+            >
+                <ng-container matColumnDef="providertype">
+                    <th mat-header-cell mat-sort-header *matHeaderCellDef>
+                        {{ 'Provider Type' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        {{ configurationEntry.providerType }}
+                    </td>
+                </ng-container>
 
-            <ng-container matColumnDef="bucket">
-                <th mat-header-cell mat-sort-header *matHeaderCellDef>
-                    {{ 'Bucket' | translate }}
-                </th>
-                <td mat-cell *matCellDef="let configurationEntry">
-                    {{ configurationEntry.bucketName }}
-                </td>
-            </ng-container>
+                <ng-container matColumnDef="endpoint">
+                    <th mat-header-cell mat-sort-header *matHeaderCellDef>
+                        {{ 'Endpoint' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        {{ configurationEntry.endPoint }}
+                    </td>
+                </ng-container>
 
-            <ng-container matColumnDef="editExportProvider">
-                <th mat-header-cell *matHeaderCellDef>
-                    {{ 'Edit' | translate }}
-                </th>
-                <td mat-cell *matCellDef="let configurationEntry">
-                    <div *ngIf="configurationEntry.providerId !== 'FOLDER'">
-                        <div fxLayout="row">
-                            <span
-                                fxFlex
-                                fxFlexOrder="3"
-                                fxLayout="row"
-                                fxLayoutAlign="start center"
-                            >
-                                <button
-                                    color="accent"
-                                    mat-icon-button
-                                    matTooltip="{{
-                                        'Edit Export Provider' | translate
-                                    }}"
-                                    matTooltipPosition="above"
-                                    data-cy="exportProvider-edit-btn"
-                                    (click)="
-                                        
createExportProvider(configurationEntry)
-                                    "
+                <ng-container matColumnDef="bucket">
+                    <th mat-header-cell mat-sort-header *matHeaderCellDef>
+                        {{ 'Bucket' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        {{ configurationEntry.bucketName }}
+                    </td>
+                </ng-container>
+
+                <ng-container matColumnDef="editExportProvider">
+                    <th mat-header-cell *matHeaderCellDef>
+                        {{ 'Edit' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        <div *ngIf="configurationEntry.providerId !== 
'FOLDER'">
+                            <div fxLayout="row">
+                                <span
+                                    fxFlex
+                                    fxFlexOrder="3"
+                                    fxLayout="row"
+                                    fxLayoutAlign="start center"
                                 >
-                                    <i class="material-icons">edit</i>
-                                </button>
-                            </span>
+                                    <button
+                                        color="accent"
+                                        mat-icon-button
+                                        matTooltip="{{
+                                            'Edit Export Provider' | translate
+                                        }}"
+                                        matTooltipPosition="above"
+                                        data-cy="exportProvider-edit-btn"
+                                        (click)="
+                                            createExportProvider(
+                                                configurationEntry
+                                            )
+                                        "
+                                    >
+                                        <i class="material-icons">edit</i>
+                                    </button>
+                                </span>
+                            </div>
                         </div>
-                    </div>
-                </td>
-            </ng-container>
+                    </td>
+                </ng-container>
 
-            <ng-container matColumnDef="delete">
-                <th mat-header-cell *matHeaderCellDef>
-                    {{ 'Remove' | translate }}
-                </th>
-                <td mat-cell *matCellDef="let configurationEntry">
-                    <div *ngIf="configurationEntry.providerId !== 'FOLDER'">
-                        <div fxLayout="row">
-                            <span
-                                fxFlex
-                                fxFlexOrder="3"
-                                fxLayout="row"
-                                fxLayoutAlign="start center"
-                            >
-                                <button
-                                    color="accent"
-                                    mat-icon-button
-                                    matTooltip="{{
-                                        'Remove export provider configuration'
-                                            | translate
-                                    }}"
-                                    data-cy="exportProvider-delete-btn"
-                                    matTooltipPosition="above"
-                                    (click)="
-                                        deleteExportProvider(
-                                            configurationEntry.providerId
-                                        )
-                                    "
+                <ng-container matColumnDef="delete">
+                    <th mat-header-cell *matHeaderCellDef>
+                        {{ 'Remove' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        <div *ngIf="configurationEntry.providerId !== 
'FOLDER'">
+                            <div fxLayout="row">
+                                <span
+                                    fxFlex
+                                    fxFlexOrder="3"
+                                    fxLayout="row"
+                                    fxLayoutAlign="start center"
                                 >
-                                    <i class="material-icons">delete</i>
-                                </button>
-                            </span>
+                                    <button
+                                        color="accent"
+                                        mat-icon-button
+                                        matTooltip="{{
+                                            'Remove export provider 
configuration'
+                                                | translate
+                                        }}"
+                                        data-cy="exportProvider-delete-btn"
+                                        matTooltipPosition="above"
+                                        (click)="
+                                            deleteExportProvider(
+                                                configurationEntry.providerId
+                                            )
+                                        "
+                                    >
+                                        <i class="material-icons">delete</i>
+                                    </button>
+                                </span>
+                            </div>
                         </div>
-                    </div>
-                </td>
-            </ng-container>
+                    </td>
+                </ng-container>
 
-            <ng-container matColumnDef="test">
-                <th mat-header-cell *matHeaderCellDef>
-                    {{ 'Test' | translate }}
-                </th>
-                <td mat-cell *matCellDef="let configurationEntry">
-                    <div>
-                        <div fxLayout="row">
-                            <span
-                                fxFlex
-                                fxFlexOrder="3"
-                                fxLayout="row"
-                                fxLayoutAlign="start center"
-                            >
-                                <button
-                                    color="accent"
-                                    mat-icon-button
-                                    matTooltip="{{
-                                        'Test export provider configuration'
-                                            | translate
-                                    }}"
-                                    data-cy="exportProvider-test-btn"
-                                    matTooltipPosition="above"
-                                    (click)="
-                                        testExportProvider(
-                                            configurationEntry.providerId
-                                        )
-                                    "
+                <ng-container matColumnDef="test">
+                    <th mat-header-cell *matHeaderCellDef>
+                        {{ 'Test' | translate }}
+                    </th>
+                    <td mat-cell *matCellDef="let configurationEntry">
+                        <div>
+                            <div fxLayout="row">
+                                <span
+                                    fxFlex
+                                    fxFlexOrder="3"
+                                    fxLayout="row"
+                                    fxLayoutAlign="start center"
                                 >
-                                    <i class="material-icons">bug_report</i>
-                                </button>
-                            </span>
+                                    <button
+                                        color="accent"
+                                        mat-icon-button
+                                        matTooltip="{{
+                                            'Test export provider 
configuration'
+                                                | translate
+                                        }}"
+                                        data-cy="exportProvider-test-btn"
+                                        matTooltipPosition="above"
+                                        (click)="
+                                            testExportProvider(
+                                                configurationEntry.providerId
+                                            )
+                                        "
+                                    >
+                                        <i 
class="material-icons">bug_report</i>
+                                    </button>
+                                </span>
+                            </div>
                         </div>
-                    </div>
-                </td>
-            </ng-container>
+                    </td>
+                </ng-container>
 
-            <tr mat-header-row *matHeaderRowDef="displayedColumnsExport"></tr>
-            <tr
-                mat-row
-                *matRowDef="let row; columns: displayedColumnsExport"
-            ></tr>
-        </table>
+                <tr
+                    mat-header-row
+                    *matHeaderRowDef="displayedColumnsExport"
+                ></tr>
+                <tr
+                    mat-row
+                    *matRowDef="let row; columns: displayedColumnsExport"
+                ></tr>
+            </table>
 
-        <div
-            *ngIf="
-                !availableExportProvider || availableExportProvider.length === 0
-            "
-        >
-            <h5>{{ 'no stored export providers' | translate }}</h5>
-        </div>
+            <div
+                *ngIf="
+                    !availableExportProvider ||
+                    availableExportProvider.length === 0
+                "
+            >
+                <h5>{{ 'no stored export providers' | translate }}</h5>
+            </div>
+        }
     </div>
 </sp-basic-view>
diff --git 
a/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.ts
 
b/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.ts
index dd3677e50f..4c45f739cc 100644
--- 
a/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.ts
+++ 
b/ui/src/app/dataset/components/datalake-configuration/datalake-configuration.component.ts
@@ -31,13 +31,17 @@ import {
     ExportProviderSettings,
     ExportProviderService,
     RetentionLog,
+    UserService,
+    DataLakeMeasure,
 } from '@streampipes/platform-services';
 import { MatPaginator } from '@angular/material/paginator';
 import { MatSort } from '@angular/material/sort';
 import {
+    CurrentUserService,
     DataDownloadDialogComponent,
     DialogRef,
     DialogService,
+    ObjectPermissionDialogComponent,
     LocalStorageService,
     PanelType,
     SpBreadcrumbService,
@@ -51,6 +55,8 @@ import { DeleteExportProviderComponent } from 
'../../dialog/delete-export-provid
 import { TranslateService } from '@ngx-translate/core';
 import { ExportProviderConnectionTestComponent } from 
'../../dialog/export-provider-connection-test/export-provider-connection-test.component';
 import { DataRetentionLogDialogComponent } from 
'../../dialog/data-retention-log-dialog/data-retention-log-dialog.component';
+import { UserRole } from 'src/app/_enums/user-role.enum';
+import { UserPrivilege } from 'src/app/_enums/user-privilege.enum';
 
 @Component({
     selector: 'sp-datalake-configuration',
@@ -70,6 +76,7 @@ export class DatalakeConfigurationComponent implements 
OnInit, AfterViewInit {
     private breadcrumbService = inject(SpBreadcrumbService);
     private exportProviderRestService = inject(ExportProviderService);
     private translateService = inject(TranslateService);
+    private currentUserService = inject(CurrentUserService);
 
     dataSource: MatTableDataSource<DataLakeConfigurationEntry> =
         new MatTableDataSource([]);
@@ -101,6 +108,8 @@ export class DatalakeConfigurationComponent implements 
OnInit, AfterViewInit {
 
     pageSize = this.localStorageService.get('paginator-page-size', 10);
     pageIndex = 0;
+    isAdmin = false;
+    writeAccess = false;
 
     ngOnInit(): void {
         this.breadcrumbService.updateBreadcrumb([
@@ -109,6 +118,11 @@ export class DatalakeConfigurationComponent implements 
OnInit, AfterViewInit {
         ]);
         this.loadAvailableMeasurements();
         this.loadAvailableExportProvider();
+        const currentUser = this.currentUserService.getCurrentUser();
+        this.isAdmin = currentUser.roles.indexOf(UserRole.ROLE_ADMIN) > -1;
+        this.writeAccess =
+            currentUser.roles.indexOf(UserPrivilege.PRIVILEGE_WRITE_DATASET) >
+                -1 || this.isAdmin;
     }
 
     ngAfterViewInit() {
@@ -348,6 +362,20 @@ export class DatalakeConfigurationComponent implements 
OnInit, AfterViewInit {
             this.queryEntryCounts(measurements, 'eventsLatest', 7);
         }
     }
+    showPermissionsDialog(element: DataLakeMeasure) {
+        this.dialogService.open(ObjectPermissionDialogComponent, {
+            panelType: PanelType.SLIDE_IN_PANEL,
+            title: this.translateService.instant('Manage permissions'),
+            width: '50vw',
+            data: {
+                objectInstanceId: element.elementId,
+                headerTitle:
+                    this.translateService.instant(
+                        'Manage permissions for dataset ',
+                    ) + element.measureName,
+            },
+        });
+    }
 
     queryEntryCounts(
         measurements: string[],

Reply via email to