Repository: apache/streampipes
Pull request #3373 (2024-12-05 13:43:10)

fix(#3372): Fix time range selector test
<!--
  ~ 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.
  ~
  -->

  <!--
Thanks for contributing! Here are some tips you can follow to help us incorporate your contribution quickly and easily:
1. If this is your first time, please read our contributor guidelines:
    - https://streampipes.apache.org/community/get-involved/
    - https://cwiki.apache.org/confluence/display/STREAMPIPES/Getting+Started
2. Make sure the PR title is formatted like: `[#<GitHub issue id>] PR title ...`
3. If the PR is unfinished, add '[WIP]' in your PR title, e.g., `[WIP][#<GitHub issue id>] PR title ...`.
4. Please write your PR title to summarize what this PR proposes/fixes.
5. Link the PR to the corresponding GitHub issue (if present) in the `Development` section in the right menu bar.
6. Be sure to keep the PR description updated to reflect all changes.
7. If possible, provide a concise example to reproduce the issue for a faster review.
8. Make sure tests pass via `mvn clean install`.
9. (Optional) If the contribution is large, please file an Apache ICLA
    - http://apache.org/licenses/icla.pdf
-->

### Purpose
<!--
Please clarify what changes you are proposing and describe how those changes will address the issue.
Furthermore, describe potential consequences the changes might have.
-->

### Remarks
<!--
Is there anything left we need to pay attention on?
Are there some references that might be important? E.g. links to Confluence, or discussions
on the mailing list or GitHub.
-->
PR introduces (a) breaking change(s): <yes/no>

PR introduces (a) deprecation(s): <yes/no>



Patch:
diff --git a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
index 5c42478431..79310b96b1 100644
--- a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
+++ b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java
@@ -97,7 +97,7 @@ public class AdapterMasterManagement {
     var storedDescription = new SourcesManagement()
         .createAdapterDataStream(adapterDescription, streamId);
     storedDescription.setCorrespondingAdapterId(adapterId);
-    installDataSource(storedDescription, principalSid, true);
+    installDataSource(storedDescription, principalSid);
     LOG.info("Install source (source URL: {} in backend", adapterDescription.getElementId());
   }
 
@@ -141,15 +141,8 @@ public class AdapterMasterManagement {
     LOG.info("Successfully deleted data stream: " + adapter.getCorrespondingDataStreamElementId());
   }
 
-  public List<AdapterDescription> getAllAdapterInstances() throws AdapterException {
-
-    List<AdapterDescription> allAdapters = adapterInstanceStorage.findAll();
-
-    if (allAdapters == null) {
-      throw new AdapterException("Could not get all adapters");
-    }
-
-    return allAdapters;
+  public List<AdapterDescription> getAllAdapterInstances() {
+    return adapterInstanceStorage.findAll();
   }
 
   public void stopStreamAdapter(String elementId) throws AdapterException {
@@ -198,11 +191,10 @@ public class AdapterMasterManagement {
 
   private void installDataSource(
       SpDataStream stream,
-      String principalSid,
-      boolean publicElement
+      String principalSid
   ) throws AdapterException {
     try {
-      new DataStreamVerifier(stream).verifyAndAdd(principalSid, publicElement);
+      new DataStreamVerifier(stream).verifyAndAdd(principalSid, false);
     } catch (SepaParseException e) {
       LOG.error("Error while installing data source: {}", stream.getElementId(), e);
       throw new AdapterException();
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
index caca0dac6c..0c88e461b6 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
@@ -108,24 +108,20 @@ public class ResetManagement {
   private static void stopAndDeleteAllAdapters() {
     AdapterMasterManagement adapterMasterManagement = new AdapterMasterManagement(
         StorageDispatcher.INSTANCE.getNoSqlStore()
-            .getAdapterInstanceStorage(),
+                                  .getAdapterInstanceStorage(),
         new SpResourceManager().manageAdapters(),
         new SpResourceManager().manageDataStreams(),
         AdapterMetricsManager.INSTANCE.getAdapterMetrics()
     );
 
-    try {
-      List<AdapterDescription> allAdapters = adapterMasterManagement.getAllAdapterInstances();
-      allAdapters.forEach(adapterDescription -> {
-        try {
-          adapterMasterManagement.deleteAdapter(adapterDescription.getElementId());
-        } catch (AdapterException e) {
-          logger.error("Failed to delete adapter with id: " + adapterDescription.getElementId(), e);
-        }
-      });
-    } catch (AdapterException e) {
-      logger.error("Failed to load all adapter descriptions", e);
-    }
+    List<AdapterDescription> allAdapters = adapterMasterManagement.getAllAdapterInstances();
+    allAdapters.forEach(adapterDescription -> {
+      try {
+        adapterMasterManagement.deleteAdapter(adapterDescription.getElementId());
+      } catch (AdapterException e) {
+        logger.error("Failed to delete adapter with id: " + adapterDescription.getElementId(), e);
+      }
+    });
   }
 
   private static void deleteAllFiles() {
@@ -154,37 +150,37 @@ public class ResetManagement {
   private static void removeAllDataViewWidgets() {
     var widgetStorage =
         StorageDispatcher.INSTANCE.getNoSqlStore()
-            .getDataExplorerWidgetStorage();
+                                  .getDataExplorerWidgetStorage();
     widgetStorage.findAll()
-        .forEach(widget -> widgetStorage.deleteElementById(widget.getElementId()));
+                 .forEach(widget -> widgetStorage.deleteElementById(widget.getElementId()));
   }
 
   private static void removeAllDataViews() {
     var dataLakeDashboardStorage =
         StorageDispatcher.INSTANCE.getNoSqlStore()
-            .getDataExplorerDashboardStorage();
+                                  .getDataExplorerDashboardStorage();
     dataLakeDashboardStorage.findAll()
-        .forEach(dashboard -> dataLakeDashboardStorage.deleteElementById(dashboard.getElementId()));
+                            .forEach(dashboard -> dataLakeDashboardStorage.deleteElementById(dashboard.getElementId()));
   }
 
   private static void removeAllDashboardWidgets() {
     var dashboardWidgetStorage =
         StorageDispatcher.INSTANCE.getNoSqlStore()
-            .getDashboardWidgetStorage();
+                                  .getDashboardWidgetStorage();
     dashboardWidgetStorage.findAll()
-        .forEach(widget -> dashboardWidgetStorage.deleteElementById(widget.getElementId()));
+                          .forEach(widget -> dashboardWidgetStorage.deleteElementById(widget.getElementId()));
   }
 
   private static void removeAllDashboards() {
     var dashboardStorage = StorageDispatcher.INSTANCE.getNoSqlStore()
-        .getDashboardStorage();
+                                                     .getDashboardStorage();
     dashboardStorage.findAll()
-        .forEach(dashboard -> dashboardStorage.deleteElementById(dashboard.getElementId()));
+                    .forEach(dashboard -> dashboardStorage.deleteElementById(dashboard.getElementId()));
   }
 
   private static void removeAllAssets(String username) {
     IGenericStorage genericStorage = StorageDispatcher.INSTANCE.getNoSqlStore()
-        .getGenericStorage();
+                                                               .getGenericStorage();
     try {
       for (Map<String, Object> asset : genericStorage.findAll("asset-management")) {
         genericStorage.delete((String) asset.get("_id"), (String) asset.get("_rev"));
@@ -211,13 +207,19 @@ public class ResetManagement {
         "asset-management",
         "asset-sites"
     );
-    var genericStorage = StorageDispatcher.INSTANCE.getNoSqlStore().getGenericStorage();
+    var genericStorage = StorageDispatcher.INSTANCE.getNoSqlStore()
+                                                   .getGenericStorage();
 
     appDocTypesToDelete.forEach(docType -> {
       try {
         var allDocs = genericStorage.findAll(docType);
         for (var doc : allDocs) {
-          genericStorage.delete(doc.get("_id").toString(), doc.get("_rev").toString());
+          genericStorage.delete(
+              doc.get("_id")
+                 .toString(),
+              doc.get("_rev")
+                 .toString()
+          );
         }
       } catch (IOException e) {
         throw new RuntimeException(e);
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AdapterMonitoringResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AdapterMonitoringResource.java
index 8260c4c9c6..0d7363f781 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AdapterMonitoringResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AdapterMonitoringResource.java
@@ -21,11 +21,13 @@ package org.apache.streampipes.rest.impl;
 
 import org.apache.streampipes.manager.monitoring.pipeline.ExtensionsLogProvider;
 import org.apache.streampipes.manager.monitoring.pipeline.ExtensionsServiceLogExecutor;
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
 import org.apache.streampipes.model.monitoring.SpLogEntry;
 import org.apache.streampipes.model.monitoring.SpMetricsEntry;
 
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -39,21 +41,51 @@ import java.util.Map;
 @RequestMapping("/api/v2/adapter-monitoring")
 public class AdapterMonitoringResource extends AbstractMonitoringResource {
 
-  @GetMapping(path = "adapter/{elementId}/logs", produces = MediaType.APPLICATION_JSON_VALUE)
-  public ResponseEntity<List<SpLogEntry>> getLogInfoForAdapter(@PathVariable("elementId") String elementId) {
+  @GetMapping(
+      path = "adapter/{elementId}/logs",
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @PreAuthorize("this.hasReadAuthority() and hasPermission('#elementId', 'READ')")
+  public ResponseEntity<List<SpLogEntry>> getLogInfoForAdapter(
+      @PathVariable("elementId") String elementId
+  ) {
     return ok(ExtensionsLogProvider.INSTANCE.getLogInfosForResource(elementId));
   }
 
-  @GetMapping(path = "adapter/{elementId}/metrics", produces = MediaType.APPLICATION_JSON_VALUE)
-  public ResponseEntity<SpMetricsEntry> getMetricsInfoForAdapter(@PathVariable("elementId") String elementId) {
+  @GetMapping(
+      path = "adapter/{elementId}/metrics",
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @PreAuthorize("this.hasReadAuthority() and hasPermission('#elementId', 'READ')")
+  public ResponseEntity<SpMetricsEntry> getMetricsInfoForAdapter(
+      @PathVariable("elementId") String elementId
+  ) {
     return ok(ExtensionsLogProvider.INSTANCE.getMetricInfosForResource(elementId));
   }
 
-  @GetMapping(path = "metrics", produces = MediaType.APPLICATION_JSON_VALUE)
+  @GetMapping(
+      path = "metrics",
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @PreAuthorize("this.hasReadAuthority() and hasPermission('#elementId', 'READ')")
   public ResponseEntity<Map<String, SpMetricsEntry>> getMetricsInfos(
       @RequestParam(value = "filter") List<String> elementIds
   ) {
     new ExtensionsServiceLogExecutor().triggerUpdate();
     return ok(ExtensionsLogProvider.INSTANCE.getMetricsInfoForResources(elementIds));
   }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasReadAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_READ_ADAPTER_VALUE);
+  }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasWriteAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_ADAPTER_VALUE);
+  }
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineMonitoring.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineMonitoring.java
index f772754391..135bf5f938 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineMonitoring.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineMonitoring.java
@@ -19,11 +19,13 @@ package org.apache.streampipes.rest.impl;
 
 import org.apache.streampipes.manager.monitoring.pipeline.ExtensionsLogProvider;
 import org.apache.streampipes.manager.monitoring.pipeline.ExtensionsServiceLogExecutor;
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
 import org.apache.streampipes.model.monitoring.SpLogEntry;
 import org.apache.streampipes.model.monitoring.SpMetricsEntry;
 
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -37,20 +39,43 @@ import java.util.Map;
 @RequestMapping("/api/v2/pipeline-monitoring")
 public class PipelineMonitoring extends AbstractMonitoringResource {
 
-  @GetMapping(value = "/pipeline/{pipelineId}/logs", produces = MediaType.APPLICATION_JSON_VALUE)
+  @GetMapping(
+      value = "/pipeline/{pipelineId}/logs",
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @PreAuthorize("this.hasReadAuthority() and hasPermission('#pipelineId', 'READ')")
   public ResponseEntity<Map<String, List<SpLogEntry>>> getLogInfoForPipeline(
-      @PathVariable("pipelineId") String pipelineId) {
+      @PathVariable("pipelineId") String pipelineId
+  ) {
     return ok(ExtensionsLogProvider.INSTANCE.getLogInfosForPipeline(pipelineId));
   }
 
-  @GetMapping(value = "/pipeline/{pipelineId}/metrics", produces = MediaType.APPLICATION_JSON_VALUE)
+  @GetMapping(
+      value = "/pipeline/{pipelineId}/metrics",
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  @PreAuthorize("this.hasReadAuthority() and hasPermission('#pipelineId', 'READ')")
   public ResponseEntity<Map<String, SpMetricsEntry>> getMetricsInfoForPipeline(
       @PathVariable("pipelineId") String pipelineId,
-      @RequestParam(value = "forceUpdate", required = false, defaultValue = "false") boolean forceUpdate) {
+      @RequestParam(value = "forceUpdate", required = false, defaultValue = "false") boolean forceUpdate
+  ) {
     if (forceUpdate) {
       new ExtensionsServiceLogExecutor().triggerUpdate();
     }
     return ok(ExtensionsLogProvider.INSTANCE.getMetricInfosForPipeline(pipelineId));
   }
 
+  /**
+   * required by Spring expression
+   */
+  public boolean hasReadAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_VALUE);
+  }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasWriteAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_PIPELINE_VALUE);
+  }
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
index d2dbdf5676..b897a9d599 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
@@ -25,6 +25,7 @@ import org.apache.streampipes.manager.pipeline.PipelineManager;
 import org.apache.streampipes.manager.pipeline.compact.CompactPipelineManagement;
 import org.apache.streampipes.manager.recommender.ElementRecommender;
 import org.apache.streampipes.manager.storage.PipelineStorageService;
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
 import org.apache.streampipes.model.message.ErrorMessage;
 import org.apache.streampipes.model.message.Message;
 import org.apache.streampipes.model.message.Notification;
@@ -37,7 +38,6 @@ import org.apache.streampipes.model.pipeline.PipelineElementRecommendationMessag
 import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
 import org.apache.streampipes.model.pipeline.compact.CompactPipeline;
 import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
-import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.rest.shared.exception.SpMessageException;
 import org.apache.streampipes.rest.shared.exception.SpNotificationException;
 import org.apache.streampipes.storage.management.StorageDispatcher;
@@ -95,7 +95,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
               mediaType = "application/json",
               array = @ArraySchema(schema = @Schema(implementation = Pipeline.class))
           )})})
-  @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasReadAuthority()")
   @PostFilter("hasPermission(filterObject.pipelineId, 'READ')")
   public List<Pipeline> get() {
     return PipelineManager.getAllPipelines();
@@ -105,7 +105,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       path = "{pipelineId}/status",
       produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Get the pipeline status of a given pipeline", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasReadAuthority()")
   public List<PipelineStatusMessage> getPipelineStatus(@PathVariable("pipelineId") String pipelineId) {
     return PipelineStatusManager.getPipelineStatus(pipelineId, 5);
   }
@@ -114,15 +114,15 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       path = "/{pipelineId}",
       produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Delete a pipeline with a given id", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
-  public Message removeOwn(@PathVariable("pipelineId") String pipelineId) {
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#pipelineId', 'WRITE')")
+  public Message delete(@PathVariable("pipelineId") String pipelineId) {
     PipelineManager.deletePipeline(pipelineId);
     return Notifications.success("Pipeline deleted");
   }
 
   @GetMapping(path = "/{pipelineId}", produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Get a specific pipeline with the given id", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasReadAuthority() and hasPermission('#pipelineId', 'READ')")
   public ResponseEntity<Pipeline> getElement(@PathVariable("pipelineId") String pipelineId) {
     Pipeline foundPipeline = PipelineManager.getPipeline(pipelineId);
 
@@ -135,7 +135,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
 
   @GetMapping(path = "/{pipelineId}/start", produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Start the pipeline with the given id", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#pipelineId', 'WRITE')")
   public ResponseEntity<?> start(@PathVariable("pipelineId") String pipelineId) {
     try {
       PipelineOperationStatus status = PipelineManager.startPipeline(pipelineId);
@@ -149,7 +149,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
 
   @GetMapping(path = "/{pipelineId}/stop", produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Stop the pipeline with the given id", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#pipelineId', 'WRITE')")
   public ResponseEntity<?> stop(@PathVariable("pipelineId") String pipelineId,
                                 @RequestParam(value = "forceStop", defaultValue = "false") boolean forceStop) {
     try {
@@ -166,7 +166,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Store a new pipeline", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<SuccessMessage> addPipeline(@RequestBody Pipeline pipeline) {
 
     String pipelineId = PipelineManager.addPipeline(getAuthenticatedUserSid(), pipeline);
@@ -180,7 +180,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Convert a pipeline to the compact model", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<CompactPipeline> convertToCompactPipeline(@RequestBody Pipeline pipeline) {
     return ok(compactPipelineManagement.convertPipeline(pipeline));
   }
@@ -190,7 +190,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
   @Hidden
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   @PostAuthorize("hasPermission(returnObject, 'READ')")
   public PipelineElementRecommendationMessage recommend(@RequestBody Pipeline pipeline,
                                                         @PathVariable("recId") String baseRecElement) {
@@ -221,7 +221,7 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
   @Hidden
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> validatePipeline(@RequestBody Pipeline pipeline) {
     try {
       return ok(new PipelineVerificationHandlerV2(pipeline).verifyPipeline());
@@ -238,8 +238,8 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       produces = MediaType.APPLICATION_JSON_VALUE,
       consumes = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Update an existing pipeline", tags = {"Pipeline"})
-  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
-  public ResponseEntity<SuccessMessage> overwritePipeline(@PathVariable("pipelineId") String pipelineId,
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#pipelineId', 'WRITE')")
+  public ResponseEntity<SuccessMessage> updatePipeline(@PathVariable("pipelineId") String pipelineId,
                                                           @RequestBody Pipeline pipeline) {
     Pipeline storedPipeline = getPipelineStorage().getElementById(pipelineId);
     if (!storedPipeline.isRunning()) {
@@ -264,10 +264,23 @@ public class PipelineResource extends AbstractAuthGuardedRestResource {
       "Pipeline"}, responses = {@ApiResponse(content = {
         @Content(mediaType = "application/json",
           array = @ArraySchema(schema = @Schema(implementation = Pipeline.class)))})})
-  @PreAuthorize(AuthConstants.HAS_READ_PIPELINE_PRIVILEGE)
+  @PreAuthorize("this.hasReadAuthority()")
   @PostFilter("hasPermission(filterObject.pipelineId, 'READ')")
   public List<Pipeline> getPipelinesContainingElement(@PathVariable("elementId") String elementId) {
     return PipelineManager.getPipelinesContainingElements(elementId);
   }
 
+  /**
+   * required by Spring expression
+   */
+  public boolean hasReadAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_READ_PIPELINE_VALUE);
+  }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasWriteAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_PIPELINE_VALUE);
+  }
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AbstractAdapterResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AbstractAdapterResource.java
index c9623a4df1..dcdd800b6a 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AbstractAdapterResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AbstractAdapterResource.java
@@ -17,6 +17,7 @@
  */
 package org.apache.streampipes.rest.impl.connect;
 
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
 import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 
 import java.util.function.Supplier;
@@ -28,4 +29,18 @@ public class AbstractAdapterResource<T> extends AbstractAuthGuardedRestResource
   public AbstractAdapterResource(Supplier<T> managementServiceSupplier) {
     this.managementService = managementServiceSupplier.get();
   }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasReadAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_READ_ADAPTER_VALUE);
+  }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasWriteAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_ADAPTER_VALUE);
+  }
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java
index 372769a792..b0a7b2c8e6 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java
@@ -36,6 +36,7 @@ import org.apache.streampipes.model.util.ElementIdGenerator;
 import org.apache.streampipes.resource.management.PermissionResourceManager;
 import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.rest.security.AuthConstants;
+import org.apache.streampipes.rest.security.SpPermissionEvaluator;
 import org.apache.streampipes.rest.shared.constants.SpMediaType;
 import org.apache.streampipes.storage.api.IPipelineStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
@@ -46,6 +47,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 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.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -70,7 +72,7 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
   public AdapterResource() {
     super(() -> new AdapterMasterManagement(
         StorageDispatcher.INSTANCE.getNoSqlStore()
-            .getAdapterInstanceStorage(),
+                                  .getAdapterInstanceStorage(),
         new SpResourceManager().manageAdapters(),
         new SpResourceManager().manageDataStreams(),
         AdapterMetricsManager.INSTANCE.getAdapterMetrics()
@@ -78,7 +80,7 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
   }
 
   @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<? extends Message> addAdapter(@RequestBody AdapterDescription adapterDescription) {
     var principalSid = getAuthenticatedUserSid();
     var username = getAuthenticatedUsername();
@@ -102,14 +104,14 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
       SpMediaType.YAML,
       SpMediaType.YML
   })
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> convertToCompactAdapter(@RequestBody AdapterDescription adapterDescription)
       throws Exception {
     return ok(new CompactAdapterManagement(List.of()).convertToCompactAdapter(adapterDescription));
   }
 
   @PutMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#adapterDescription.elementId', 'WRITE')")
   public ResponseEntity<? extends Message> updateAdapter(@RequestBody AdapterDescription adapterDescription) {
     var updateManager = new AdapterUpdateManagement(managementService);
     try {
@@ -123,7 +125,7 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
   }
 
   @PutMapping(path = "pipeline-migration-preflight", consumes = MediaType.APPLICATION_JSON_VALUE,
-      produces = MediaType.APPLICATION_JSON_VALUE)
+              produces = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
   public ResponseEntity<List<PipelineUpdateInfo>> performPipelineMigrationPreflight(
       @RequestBody AdapterDescription adapterDescription
@@ -135,60 +137,87 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
   }
 
   @GetMapping(path = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, SpMediaType.YAML, SpMediaType.YML})
-  @PreAuthorize(AuthConstants.HAS_READ_ADAPTER_PRIVILEGE)
-  public ResponseEntity<?> getAdapter(@PathVariable("id") String adapterId,
-                                      @RequestParam(value = "output",
-                                          defaultValue = "full",
-                                          required = false) String outputMode) {
+  @PreAuthorize("this.hasReadAuthority()")
+  public ResponseEntity<?> getAdapter(
+      @PathVariable("id") String elementId,
+      @RequestParam(value = "output",
+                    defaultValue = "full",
+                    required = false) String outputMode
+  ) {
 
     try {
-      AdapterDescription adapterDescription = getAdapterDescription(adapterId);
+      var adapterDescription = getAdapterDescription(elementId);
+
+      // This check is done here because the adapter permission is checked based on the corresponding data stream
+      // and not based on the element id
+      if (!checkAdapterReadPermission(adapterDescription)) {
+        LOG.error("User is not allowed to read adapter {}", elementId);
+        return ResponseEntity.status(HttpStatus.SC_UNAUTHORIZED)
+                             .build();
+      }
+
       if (outputMode.equalsIgnoreCase("compact")) {
         return ok(toCompactAdapterDescription(adapterDescription));
       } else {
         return ok(adapterDescription);
       }
     } catch (AdapterException e) {
-      LOG.error("Error while getting adapter with id {}", adapterId, e);
+      LOG.error("Error while getting adapter with id {}", elementId, e);
       return fail();
     } catch (Exception e) {
-      LOG.error("Error while transforming adapter {}", adapterId, e);
+      LOG.error("Error while transforming adapter {}", elementId, e);
       return fail();
     }
   }
 
+  /**
+   * Checks if the current user has the permission to read the adapter
+   */
+  private boolean checkAdapterReadPermission(AdapterDescription adapterDescription) {
+    var spPermissionEvaluator = new SpPermissionEvaluator();
+    var authentication = SecurityContextHolder.getContext()
+                                              .getAuthentication();
+    return spPermissionEvaluator.hasPermission(
+        authentication,
+        adapterDescription.getCorrespondingDataStreamElementId(),
+        "READ"
+    );
+  }
+
   @PostMapping(path = "/{id}/stop", produces = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
-  public ResponseEntity<?> stopAdapter(@PathVariable("id") String adapterId) {
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#elementId', 'WRITE')")
+  public ResponseEntity<?> stopAdapter(@PathVariable("id") String elementId) {
     try {
-      managementService.stopStreamAdapter(adapterId);
+      managementService.stopStreamAdapter(elementId);
       return ok(Notifications.success("Adapter started"));
     } catch (AdapterException e) {
-      LOG.error("Could not stop adapter with id {}", adapterId, e);
+      LOG.error("Could not stop adapter with id {}", elementId, e);
       return serverError(SpLogMessage.from(e));
     }
   }
 
   @PostMapping(path = "/{id}/start", produces = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
-  public ResponseEntity<?> startAdapter(@PathVariable("id") String adapterId) {
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#elementId', 'WRITE')")
+  public ResponseEntity<?> startAdapter(@PathVariable("id") String elementId) {
     try {
-      managementService.startStreamAdapter(adapterId);
+      managementService.startStreamAdapter(elementId);
       return ok(Notifications.success("Adapter stopped"));
     } catch (AdapterException e) {
-      LOG.error("Could not start adapter with id {}", adapterId, e);
+      LOG.error("Could not start adapter with id {}", elementId, e);
       return serverError(SpLogMessage.from(e));
     }
   }
 
   @DeleteMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
-  public ResponseEntity<?> deleteAdapter(@PathVariable("id") String elementId,
-                                         @RequestParam(value = "deleteAssociatedPipelines", defaultValue = "false")
-                                         boolean deleteAssociatedPipelines) {
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#elementId', 'WRITE')")
+  public ResponseEntity<?> deleteAdapter(
+      @PathVariable("id") String elementId,
+      @RequestParam(value = "deleteAssociatedPipelines", defaultValue = "false")
+      boolean deleteAssociatedPipelines
+  ) {
     List<String> pipelinesUsingAdapter = getPipelinesUsingAdapter(elementId);
     IPipelineStorage pipelineStorageAPI = StorageDispatcher.INSTANCE.getNoSqlStore()
-        .getPipelineStorageAPI();
+                                                                    .getPipelineStorageAPI();
 
     if (pipelinesUsingAdapter.isEmpty()) {
       try {
@@ -202,22 +231,40 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
       List<String> namesOfPipelinesUsingAdapter = pipelinesUsingAdapter
           .stream()
           .map(pipelineId -> pipelineStorageAPI.getElementById(
-                  pipelineId)
-              .getName())
+                                                   pipelineId)
+                                               .getName())
           .collect(Collectors.toList());
       return ResponseEntity.status(HttpStatus.SC_CONFLICT)
-          .body(String.join(", ", namesOfPipelinesUsingAdapter));
+                           .body(String.join(", ", namesOfPipelinesUsingAdapter));
     } else {
       PermissionResourceManager permissionResourceManager = new PermissionResourceManager();
       // find out the names of pipelines that have an owner and the owner is not the current user
-      List<String> namesOfPipelinesNotOwnedByUser = pipelinesUsingAdapter.stream().filter(pipelineId ->
-              !permissionResourceManager.findForObjectId(pipelineId).stream().findFirst().map(Permission::getOwnerSid)
-                  // if a pipeline has no owner, pretend the owner is the user so the user can delete it
-                  .orElse(this.getAuthenticatedUserSid()).equals(this.getAuthenticatedUserSid()))
-          .map(pipelineId -> pipelineStorageAPI.getElementById(pipelineId).getName()).collect(Collectors.toList());
-      boolean isAdmin = SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream()
-          .anyMatch(r -> r.getAuthority().equals(
-              DefaultRole.ROLE_ADMIN.name()));
+      List<String> namesOfPipelinesNotOwnedByUser = pipelinesUsingAdapter
+          .stream()
+          .filter(pipelineId ->
+                      !permissionResourceManager.findForObjectId(
+                                                    pipelineId)
+                                                .stream()
+                                                .findFirst()
+                                                .map(
+                                                    Permission::getOwnerSid)
+                                                // if a pipeline has no owner, pretend the owner
+                                                // is the user so the user can delete it
+                                                .orElse(
+                                                    this.getAuthenticatedUserSid())
+                                                .equals(
+                                                    this.getAuthenticatedUserSid()))
+          .map(pipelineId -> pipelineStorageAPI.getElementById(
+                                                   pipelineId)
+                                               .getName())
+          .collect(Collectors.toList());
+      boolean isAdmin = SecurityContextHolder.getContext()
+                                             .getAuthentication()
+                                             .getAuthorities()
+                                             .stream()
+                                             .anyMatch(r -> r.getAuthority()
+                                                             .equals(
+                                                                 DefaultRole.ROLE_ADMIN.name()));
       // if the user is admin or owns all pipelines using this adapter,
       // the user can delete all associated pipelines and this adapter
       if (isAdmin || namesOfPipelinesNotOwnedByUser.isEmpty()) {
@@ -228,34 +275,31 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
           }
           managementService.deleteAdapter(elementId);
           return ok(Notifications.success("Adapter with id: " + elementId
-              + " and all pipelines using the adapter are deleted."));
+                                              + " and all pipelines using the adapter are deleted."));
         } catch (Exception e) {
-          LOG.error("Error while deleting adapter with id "
-              + elementId + " and all pipelines using the adapter", e);
+          LOG.error(
+              "Error while deleting adapter with id "
+                  + elementId + " and all pipelines using the adapter", e
+          );
           return ok(Notifications.error(e.getMessage()));
         }
       } else {
         // otherwise, hint the user the names of pipelines using the adapter but not owned by the user
         return ResponseEntity.status(HttpStatus.SC_CONFLICT)
-            .body(String.join(", ", namesOfPipelinesNotOwnedByUser));
+                             .body(String.join(", ", namesOfPipelinesNotOwnedByUser));
       }
     }
   }
 
   @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
-  @PreAuthorize(AuthConstants.HAS_READ_ADAPTER_PRIVILEGE)
-  public ResponseEntity<?> getAllAdapters() {
-    try {
-      return ok(managementService.getAllAdapterInstances());
-    } catch (AdapterException e) {
-      LOG.error("Error while getting all adapters", e);
-      return ResponseEntity.status(500)
-          .build();
-    }
+  @PreAuthorize("this.hasReadAuthority()")
+  @PostFilter("hasPermission(filterObject.correspondingDataStreamElementId, 'READ')")
+  public List<AdapterDescription> getAllAdapters() {
+    return managementService.getAllAdapterInstances();
   }
 
-  private AdapterDescription getAdapterDescription(String adapterId) throws AdapterException {
-    return managementService.getAdapter(adapterId);
+  private AdapterDescription getAdapterDescription(String elementId) throws AdapterException {
+    return managementService.getAdapter(elementId);
   }
 
   private CompactAdapter toCompactAdapterDescription(AdapterDescription adapterDescription) throws Exception {
@@ -264,8 +308,8 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage
 
   private List<String> getPipelinesUsingAdapter(String adapterId) {
     return StorageDispatcher.INSTANCE.getNoSqlStore()
-        .getPipelineStorageAPI()
-        .getPipelinesUsingAdapter(adapterId);
+                                     .getPipelineStorageAPI()
+                                     .getPipelinesUsingAdapter(adapterId);
   }
 
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
index 0e51f00927..89912db3d8 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/CompactAdapterResource.java
@@ -30,7 +30,6 @@ import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.connect.adapter.compact.CompactAdapter;
 import org.apache.streampipes.model.message.Notifications;
 import org.apache.streampipes.resource.management.SpResourceManager;
-import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.rest.shared.constants.SpMediaType;
 import org.apache.streampipes.rest.shared.exception.BadRequestException;
 import org.apache.streampipes.storage.management.StorageDispatcher;
@@ -75,7 +74,7 @@ public class CompactAdapterResource extends AbstractAdapterResource<AdapterMaste
           SpMediaType.YAML
       }
   )
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> addAdapterCompact(
       @RequestBody CompactAdapter compactAdapter
   ) throws Exception {
@@ -129,7 +128,7 @@ public class CompactAdapterResource extends AbstractAdapterResource<AdapterMaste
           "application/yml"
       }
   )
-  @PreAuthorize(AuthConstants.HAS_WRITE_ADAPTER_PRIVILEGE)
+  @PreAuthorize("this.hasWriteAuthority() and hasPermission('#elementId', 'WRITE')")
   public ResponseEntity<?> updateAdapterCompact(
       @PathVariable("id") String elementId,
       @RequestBody CompactAdapter compactAdapter
@@ -164,4 +163,5 @@ public class CompactAdapterResource extends AbstractAdapterResource<AdapterMaste
     var generators = adapterGenerationSteps.getGenerators();
     return new CompactAdapterManagement(generators).convertToAdapterDescription(compactAdapter, existingAdapter);
   }
+
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/DescriptionResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/DescriptionResource.java
index 7d8f562034..d26eaf951a 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/DescriptionResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/DescriptionResource.java
@@ -31,6 +31,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+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;
@@ -53,6 +54,7 @@ public class DescriptionResource extends AbstractAdapterResource<DescriptionMana
   }
 
   @GetMapping(path = "/adapters", produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasReadAuthority()")
   public ResponseEntity<List<AdapterDescription>> getAdapters() {
     List<AdapterDescription> result = managementService.getAdapters();
 
@@ -60,6 +62,7 @@ public class DescriptionResource extends AbstractAdapterResource<DescriptionMana
   }
 
   @GetMapping(path = "/{id}/assets", produces = "application/zip")
+  @PreAuthorize("this.hasReadAuthority()")
   public ResponseEntity<?> getAdapterAssets(@PathVariable("id") String id) {
     try {
       String result = null;
@@ -87,6 +90,7 @@ public class DescriptionResource extends AbstractAdapterResource<DescriptionMana
   }
 
   @GetMapping(path = "/{id}/assets/icon", produces = "image/png")
+  @PreAuthorize("this.hasReadAuthority()")
   public ResponseEntity<?> getAdapterIconAsset(@PathVariable("id") String id) {
     try {
 
@@ -115,6 +119,7 @@ public class DescriptionResource extends AbstractAdapterResource<DescriptionMana
   }
 
   @GetMapping(path = "/{id}/assets/documentation", produces = MediaType.TEXT_PLAIN_VALUE)
+  @PreAuthorize("this.hasReadAuthority()")
   public ResponseEntity<?> getAdapterDocumentationAsset(@PathVariable("id") String id) {
     try {
       String result = null;
@@ -142,6 +147,7 @@ public class DescriptionResource extends AbstractAdapterResource<DescriptionMana
   }
 
   @DeleteMapping(path = "{adapterId}")
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> deleteAdapter(@PathVariable("adapterId") String adapterId) {
     try {
       this.managementService.deleteAdapterDescription(adapterId);
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
index ddb51a9a92..f49dfaa7c9 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
@@ -22,6 +22,7 @@ import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableExce
 import org.apache.streampipes.commons.exceptions.connect.ParseException;
 import org.apache.streampipes.connect.management.management.GuessManagement;
 import org.apache.streampipes.extensions.api.connect.exception.WorkerAdapterException;
+import org.apache.streampipes.model.client.user.DefaultPrivilege;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
 import org.apache.streampipes.model.connect.guess.AdapterEventPreview;
 import org.apache.streampipes.model.connect.guess.GuessSchema;
@@ -32,6 +33,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -53,6 +55,7 @@ public class GuessResource extends AbstractAdapterResource<GuessManagement> {
       path = "/schema",
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> guessSchema(@RequestBody AdapterDescription adapterDescription) {
 
     try {
@@ -74,6 +77,7 @@ public class GuessResource extends AbstractAdapterResource<GuessManagement> {
       path = "/schema/preview",
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> getAdapterEventPreview(@RequestBody AdapterEventPreview previewRequest) {
     try {
       return ok(managementService.performAdapterEventPreview(previewRequest));
@@ -81,5 +85,13 @@ public class GuessResource extends AbstractAdapterResource<GuessManagement> {
       return badRequest();
     }
   }
+
+  /**
+   * required by Spring expression
+   */
+  public boolean hasWriteAuthority() {
+    return isAdminOrHasAnyAuthority(DefaultPrivilege.Constants.PRIVILEGE_WRITE_ADAPTER_VALUE);
+  }
+
 }
 
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java
index ce66dbeed8..cf30a41758 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/RuntimeResolvableResource.java
@@ -38,6 +38,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -67,6 +68,7 @@ public class RuntimeResolvableResource extends AbstractAdapterResource<WorkerAdm
       path = "{id}/configurations",
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasWriteAuthority()")
   public ResponseEntity<?> fetchConfigurations(@PathVariable("id") String appId,
                                                @RequestBody RuntimeOptionsRequest runtimeOptionsRequest) {
 
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/UnitResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/UnitResource.java
index 1efb585e82..db2edcdd6c 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/UnitResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/UnitResource.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -48,6 +49,7 @@ public class UnitResource extends AbstractAdapterResource<UnitMasterManagement>
       consumes = MediaType.APPLICATION_JSON_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE
   )
+  @PreAuthorize("this.hasReadAuthority()")
   public ResponseEntity<?> getFittingUnits(@RequestBody UnitDescription unitDescription) {
     try {
       String resultingJson = managementService.getFittingUnits(unitDescription);
@@ -59,8 +61,9 @@ public class UnitResource extends AbstractAdapterResource<UnitMasterManagement>
   }
 
   @GetMapping(path = "/units",
-      produces = MediaType.APPLICATION_JSON_VALUE)
-  public ResponseEntity<List<UnitDescription>> getAllUnits(){
+              produces = MediaType.APPLICATION_JSON_VALUE)
+  @PreAuthorize("this.hasReadAuthority()")
+  public ResponseEntity<List<UnitDescription>> getAllUnits() {
     List<UnitDescription> unitDescriptions = managementService.getAllUnitDescriptions();
     return ok(unitDescriptions);
   }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
index 08ee86cc46..86c0a74d80 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/security/SpPermissionEvaluator.java
@@ -35,39 +35,71 @@ import java.util.function.Predicate;
 @Configuration
 public class SpPermissionEvaluator implements PermissionEvaluator {
 
+  /**
+   * Evaluates whether the user has the necessary permissions for a given resource.
+   *
+   * @param authentication     The authentication object containing the user's credentials.
+   * @param targetDomainObject The resource being accessed, which can be an instance of
+   *                           PipelineElementRecommendationMessage or a String representing the resource ID.
+   * @param permission         Is not used in this implementation.
+   * @return true if the user has the necessary permissions, false otherwise.
+   */
   @Override
-  public boolean hasPermission(Authentication auth, Object o, Object permission) {
-    PrincipalUserDetails<?> userDetails = getUserDetails(auth);
-    if (o instanceof PipelineElementRecommendationMessage) {
-      return isAdmin(userDetails) || filterRecommendation(auth, (PipelineElementRecommendationMessage) o);
+  public boolean hasPermission(
+      Authentication authentication,
+      Object targetDomainObject,
+      Object permission
+  ) {
+    PrincipalUserDetails<?> userDetails = getUserDetails(authentication);
+    if (targetDomainObject instanceof PipelineElementRecommendationMessage) {
+      return isAdmin(userDetails) || filterRecommendation(
+          authentication,
+          (PipelineElementRecommendationMessage) targetDomainObject
+      );
     } else {
-      String objectInstanceId = (String) o;
+      String objectInstanceId = (String) targetDomainObject;
       if (isAdmin(userDetails)) {
         return true;
       }
-      return hasPermission(auth, objectInstanceId);
+      return hasPermission(authentication, objectInstanceId);
     }
   }
 
-  private boolean filterRecommendation(Authentication auth, PipelineElementRecommendationMessage message) {
-    Predicate<PipelineElementRecommendation> isForbidden = r -> !hasPermission(auth, r.getElementId());
-    message.getPossibleElements().removeIf(isForbidden);
-
-    return true;
-  }
-
+  /**
+   * Evaluates whether the user has the necessary permissions for a given resource.
+   *
+   * @param authentication The authentication object containing the user's credentials.
+   * @param targetId       The ID of the resource being accessed.
+   * @param targetType     Is not used in this implementation.
+   * @param permission     Is not used in this implementation.
+   * @return true if the user has the necessary permissions, false otherwise.
+   */
   @Override
-  public boolean hasPermission(Authentication auth, Serializable serializable, String s, Object permission) {
-    PrincipalUserDetails<?> userDetails = getUserDetails(auth);
+  public boolean hasPermission(
+      Authentication authentication,
+      Serializable targetId,
+      String targetType,
+      Object permission
+  ) {
+    PrincipalUserDetails<?> userDetails = getUserDetails(authentication);
     if (isAdmin(userDetails)) {
       return true;
     }
-    return hasPermission(auth, serializable.toString());
+    return hasPermission(authentication, targetId.toString());
+  }
+
+  private boolean filterRecommendation(Authentication auth, PipelineElementRecommendationMessage message) {
+    Predicate<PipelineElementRecommendation> isForbidden = r -> !hasPermission(auth, r.getElementId());
+    message.getPossibleElements()
+           .removeIf(isForbidden);
+
+    return true;
   }
 
   private boolean hasPermission(Authentication auth, String objectInstanceId) {
     return isPublicElement(objectInstanceId)
-        || getUserDetails(auth).getAllObjectPermissions().contains(objectInstanceId);
+        || getUserDetails(auth).getAllObjectPermissions()
+                               .contains(objectInstanceId);
   }
 
   private PrincipalUserDetails<?> getUserDetails(Authentication authentication) {
@@ -76,14 +108,18 @@ public class SpPermissionEvaluator implements PermissionEvaluator {
 
   private boolean isPublicElement(String objectInstanceId) {
     List<Permission> permissions =
-        StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage().getUserPermissionsForObject(objectInstanceId);
-    return permissions.size() > 0 && permissions.get(0).isPublicElement();
+        StorageDispatcher.INSTANCE.getNoSqlStore()
+                                  .getPermissionStorage()
+                                  .getUserPermissionsForObject(objectInstanceId);
+    return permissions.size() > 0 && permissions.get(0)
+                                                .isPublicElement();
   }
 
   private boolean isAdmin(PrincipalUserDetails<?> userDetails) {
     return userDetails
         .getAuthorities()
         .stream()
-        .anyMatch(a -> a.getAuthority().equals(DefaultRole.Constants.ROLE_ADMIN_VALUE));
+        .anyMatch(a -> a.getAuthority()
+                        .equals(DefaultRole.Constants.ROLE_ADMIN_VALUE));
   }
 }
diff --git a/ui/cypress/support/utils/GeneralUtils.ts b/ui/cypress/support/utils/GeneralUtils.ts
index 8db505e32b..9b0186b1f6 100644
--- a/ui/cypress/support/utils/GeneralUtils.ts
+++ b/ui/cypress/support/utils/GeneralUtils.ts
@@ -20,4 +20,11 @@ export class GeneralUtils {
     public static tab(identifier: string) {
         return cy.dataCy(`tab-${identifier}`).click();
     }
+
+    public static validateAmountOfNavigationIcons(expected: number) {
+        cy.dataCy('navigation-icon', { timeout: 10000 }).should(
+            'have.length',
+            expected,
+        );
+    }
 }
diff --git a/ui/cypress/support/utils/UserUtils.ts b/ui/cypress/support/utils/UserUtils.ts
index b91f046f07..3f966450db 100644
--- a/ui/cypress/support/utils/UserUtils.ts
+++ b/ui/cypress/support/utils/UserUtils.ts
@@ -62,6 +62,25 @@ export class UserUtils {
         cy.dataCy('sp-element-edit-user-save').click();
     }
 
+    /**
+     * Create a new user with the specified roles and a default password to the system.
+     *
+     * @param name - The name of the user to be added.
+     * @param roles - The roles to be assigned to the new user.
+     */
+    public static createUser(name: string, ...roles: UserRole[]): User {
+        const userBuilder = UserBuilder.create(`${name}@streampipes.apache.org`)
+            .setName(name)
+            .setPassword('default');
+
+        roles.forEach(role => userBuilder.addRole(role));
+
+        const user = userBuilder.build();
+
+        this.addUser(user);
+        return user;
+    }
+
     public static switchUser(user: User) {
         cy.logout();
         cy.visit('#/login');
diff --git a/ui/cypress/support/utils/connect/ConnectUtils.ts b/ui/cypress/support/utils/connect/ConnectUtils.ts
index 0a0eae22c1..52d2ec6c88 100644
--- a/ui/cypress/support/utils/connect/ConnectUtils.ts
+++ b/ui/cypress/support/utils/connect/ConnectUtils.ts
@@ -440,6 +440,13 @@ export class ConnectUtils {
 
     public static checkAmountOfAdapters(amount: number) {
         ConnectUtils.goToConnect();
-        ConnectBtns.deleteAdapter().should('have.length', amount);
+        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 {
+            ConnectBtns.deleteAdapter().should('have.length', amount);
+        }
     }
 }
diff --git a/ui/cypress/support/utils/pipeline/PipelineBtns.ts b/ui/cypress/support/utils/pipeline/PipelineBtns.ts
index 6ace82220f..faece471a5 100644
--- a/ui/cypress/support/utils/pipeline/PipelineBtns.ts
+++ b/ui/cypress/support/utils/pipeline/PipelineBtns.ts
@@ -17,6 +17,14 @@
  */
 
 export class PipelineBtns {
+    public static statusPipeline() {
+        return cy.dataCy('status-pipeline-green', { timeout: 10000 });
+    }
+
+    public static stopPipeline() {
+        return cy.dataCy('stop-pipeline-button', { timeout: 10000 });
+    }
+
     public static deletePipeline() {
         return cy.dataCy('delete-pipeline', { timeout: 10000 });
     }
diff --git a/ui/cypress/support/utils/pipeline/PipelineUtils.ts b/ui/cypress/support/utils/pipeline/PipelineUtils.ts
index 0fa9ce4443..2b50f512c0 100644
--- a/ui/cypress/support/utils/pipeline/PipelineUtils.ts
+++ b/ui/cypress/support/utils/pipeline/PipelineUtils.ts
@@ -21,6 +21,9 @@ import { StaticPropertyUtils } from '../userInput/StaticPropertyUtils';
 import { OutputStrategyUtils } from '../OutputStrategyUtils';
 import { PipelineElementInput } from '../../model/PipelineElementInput';
 import { PipelineBtns } from './PipelineBtns';
+import { ConnectUtils } from '../connect/ConnectUtils';
+import { PipelineBuilder } from '../../builder/PipelineBuilder';
+import { PipelineElementBuilder } from '../../builder/PipelineElementBuilder';
 
 export class PipelineUtils {
     public static addPipeline(pipelineInput: PipelineInput) {
@@ -33,6 +36,32 @@ export class PipelineUtils {
         PipelineUtils.startPipeline(pipelineInput);
     }
 
+    /**
+     * This method adds a sample adapter and pipeline
+     */
+    public static addSampleAdapterAndPipeline() {
+        const adapterName = 'simulator';
+
+        ConnectUtils.addMachineDataSimulator(adapterName);
+
+        const pipelineInput = PipelineBuilder.create('Pipeline Test')
+            .addSource(adapterName)
+            .addProcessingElement(
+                PipelineElementBuilder.create('field_renamer')
+                    .addInput('drop-down', 'convert-property', 'timestamp')
+                    .addInput('input', 'field-name', 't')
+                    .build(),
+            )
+            .addSink(
+                PipelineElementBuilder.create('data_lake')
+                    .addInput('input', 'db_measurement', 'demo')
+                    .build(),
+            )
+            .build();
+
+        PipelineUtils.addPipeline(pipelineInput);
+    }
+
     public static editPipeline() {
         cy.dataCy('modify-pipeline-btn').first().click();
     }
@@ -133,7 +162,15 @@ export class PipelineUtils {
 
     public static checkAmountOfPipelinesPipeline(amount: number) {
         PipelineUtils.goToPipelines();
-        PipelineBtns.deletePipeline().should('have.length', amount);
+
+        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 {
+            PipelineBtns.statusPipeline().should('have.length', amount);
+        }
     }
 
     public static deletePipeline() {
diff --git a/ui/cypress/support/utils/user/PermissionUtils.ts b/ui/cypress/support/utils/user/PermissionUtils.ts
new file mode 100644
index 0000000000..9907c56acc
--- /dev/null
+++ b/ui/cypress/support/utils/user/PermissionUtils.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 { StaticPropertyUtils } from '../userInput/StaticPropertyUtils';
+
+export class PermissionUtils {
+    public static openManagePermissions() {
+        cy.dataCy('open-manage-permissions').click();
+    }
+
+    public static markElementAsPublic() {
+        PermissionUtils.openManagePermissions();
+        StaticPropertyUtils.clickCheckbox('permission-public-element');
+        PermissionUtils.save();
+    }
+
+    public static authorizeUser(email: string) {
+        PermissionUtils.openManagePermissions();
+
+        cy.dataCy('authorized-user').type(email);
+        cy.get(`[data-cy="user-option-${email}"]`).click();
+
+        PermissionUtils.save();
+    }
+
+    public static save() {
+        cy.dataCy('sp-manage-permissions-save').click();
+    }
+}
diff --git a/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
index 34b84673f3..acc1a5eaf4 100644
--- a/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
+++ b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts
@@ -24,7 +24,7 @@ export class StaticPropertyUtils {
         // Configure Properties
         configs.forEach(config => {
             if (config.type === 'checkbox') {
-                this.clickCheckbox(config);
+                this.clickCheckbox(config.selector);
             } else if (config.type === 'button') {
                 cy.dataCy(config.selector, { timeout: 2000 }).click();
             } else if (config.type === 'drop-down') {
@@ -65,8 +65,12 @@ export class StaticPropertyUtils {
         });
     }
 
-    private static clickCheckbox(input: UserInput) {
-        this.clickSelectionInput(input.selector, '.mdc-checkbox');
+    /**
+     * This method can be used to check a mat checkbox
+     * @param selector
+     */
+    public static clickCheckbox(selector: string) {
+        this.clickSelectionInput(selector, '.mdc-checkbox');
     }
 
     private static clickRadio(input: UserInput) {
diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.html
index 5e8302acb3..52024357ff 100644
--- a/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.html
+++ b/ui/projects/streampipes/shared-ui/src/lib/components/sp-table/sp-table.component.html
@@ -24,7 +24,11 @@
         <tr mat-row *matRowDef="let row; columns: columns"></tr>
 
         <tr class="mat-row" *matNoDataRow>
-            <td class="mat-cell" [colSpan]="columns.length">
+            <td
+                data-cy="no-table-entries"
+                class="mat-cell"
+                [colSpan]="columns.length"
+            >
                 No entries available.
             </td>
         </tr>
diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
index 5c1b11994c..6147d2cf9c 100644
--- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
+++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
@@ -258,6 +258,7 @@
                                     mat-icon-button
                                     matTooltip="Manage permissions"
                                     matTooltipPosition="above"
+                                    data-cy="open-manage-permissions"
                                     *ngIf="isAdmin"
                                     (click)="showPermissionsDialog(adapter)"
                                 >
diff --git a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
index cf16da3273..ba806e1b7b 100644
--- a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
+++ b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
@@ -33,7 +33,10 @@
                             >
                         </mat-select>
                     </mat-form-field>
-                    <mat-checkbox formControlName="publicElement">
+                    <mat-checkbox
+                        data-cy="permission-public-element"
+                        formControlName="publicElement"
+                    >
                         Public Element
                     </mat-checkbox>
                 </div>
@@ -66,6 +69,7 @@
                                 [matChipInputSeparatorKeyCodes]="
                                     separatorKeysCodes
                                 "
+                                data-cy="authorized-user"
                                 (matChipInputTokenEnd)="addUser($event)"
                             />
                         </mat-chip-grid>
@@ -76,6 +80,7 @@
                             <mat-option
                                 *ngFor="let user of filteredUsers | async"
                                 [value]="user"
+                                [attr.data-cy]="'user-option-' + user.username"
                             >
                                 {{ user.username }}
                             </mat-option>
@@ -140,7 +145,7 @@
                 (click)="save()"
                 style="margin-right: 10px"
                 [disabled]="!parentForm.valid"
-                data-cy="sp-element-edit-user-save"
+                data-cy="sp-manage-permissions-save"
             >
                 <i class="material-icons">save</i><span>&nbsp;Save</span>
             </button>
diff --git a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
index 1f21a19d03..dc9e66c336 100644
--- a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
+++ b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
@@ -231,7 +231,7 @@ export class ObjectPermissionDialogComponent implements OnInit {
 
     private addUserToSelection(authority: PermissionEntry) {
         const user = this.allUsers.find(u => u.principalId === authority.sid);
-        this.grantedUserAuthorities.push(user);
+        user && this.grantedUserAuthorities.push(user);
     }
 
     private addGroupToSelection(authority: PermissionEntry) {
diff --git a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/data-explorer-widget-data-settings.component.ts b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/data-explorer-widget-data-settings.component.ts
index c2de37bfd2..3a6c2e3f2b 100644
--- a/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/data-explorer-widget-data-settings.component.ts
+++ b/ui/src/app/data-explorer/components/data-view/data-view-designer-panel/data-settings/data-explorer-widget-data-settings.component.ts
@@ -239,7 +239,7 @@ export class DataExplorerWidgetDataSettingsComponent implements OnInit {
     }
 
     createDefaultWidget(): void {
-        if (this.dataConfig.sourceConfigs.length === 1) {
+        if (this.checkIfDefaultTableShouldBeShown()) {
             const fields = this.fieldProviderService.generateFieldLists(
                 this.dataConfig.sourceConfigs,
             );
@@ -253,6 +253,16 @@ export class DataExplorerWidgetDataSettingsComponent implements OnInit {
         }
     }
 
+    /**
+     * This method checks if there is at least one data source and that no widget type is already configured.
+     */
+    checkIfDefaultTableShouldBeShown(): boolean {
+        return (
+            this.dataConfig.sourceConfigs.length === 1 &&
+            !this.currentlyConfiguredWidget.widgetType
+        );
+    }
+
     removeSourceConfig(index: number) {
         this.dataConfig.sourceConfigs.splice(index, 1);
     }
diff --git a/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.html b/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.html
index f9918daaa0..1c1bf59463 100644
--- a/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.html
+++ b/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.html
@@ -28,6 +28,7 @@
             <div>
                 <div fxLayout="row" fxLayoutAlign="start center" fxFlex="150px">
                     <div
+                        data-cy="status-pipeline-green"
                         *ngIf="
                             pipeline.running && pipeline.healthStatus === 'OK'
                         "
@@ -116,6 +117,7 @@
                 mat-icon-button
                 matTooltip="Stop pipeline"
                 matTooltipPosition="above"
+                data-cy="stop-pipeline-button"
                 (click)="
                     pipelineOperationsService.stopPipeline(
                         pipeline._id,
@@ -192,7 +194,7 @@
                             refreshPipelinesEmitter
                         )
                     "
-                    data-cy="share"
+                    data-cy="open-manage-permissions"
                 >
                     <i class="material-icons">share</i>
                 </button>


Test patch:
diff --git a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
index e1e18324ce..e8095649b8 100644
--- a/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
+++ b/streampipes-connect-management/src/test/java/org/apache/streampipes/connect/management/management/AdapterMasterManagementTest.java
@@ -71,7 +71,7 @@ public class AdapterMasterManagementTest {
   }
 
   @Test
-  public void getAllAdapters_Success() throws AdapterException {
+  public void getAllAdapters_Success() {
     var adapterDescriptions = List.of(new AdapterDescription());
     var adapterStorage = mock(AdapterInstanceStorageImpl.class);
     var resourceManager = mock(AdapterResourceManager.class);
@@ -90,21 +90,4 @@ public class AdapterMasterManagementTest {
     Assertions.assertEquals(1, result.size());
   }
 
-  @Test
-  public void getAllAdapters_Fail() {
-    var adapterStorage = mock(AdapterInstanceStorageImpl.class);
-    var resourceManager = mock(AdapterResourceManager.class);
-    when(adapterStorage.findAll()).thenReturn(null);
-
-    var adapterMasterManagement =
-        new AdapterMasterManagement(
-            adapterStorage,
-            resourceManager,
-            null,
-            AdapterMetricsManager.INSTANCE.getAdapterMetrics()
-        );
-
-    assertThrows(AdapterException.class, adapterMasterManagement::getAllAdapterInstances);
-
-  }
 }
diff --git a/ui/cypress/tests/connect/deleteAdapterWithMultipleUsers.smoke.spec.ts b/ui/cypress/tests/connect/deleteAdapterWithMultipleUsers.smoke.spec.ts
deleted file mode 100644
index a11332ac9e..0000000000
--- a/ui/cypress/tests/connect/deleteAdapterWithMultipleUsers.smoke.spec.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
-import { PipelineBuilder } from '../../support/builder/PipelineBuilder';
-import { PipelineElementBuilder } from '../../support/builder/PipelineElementBuilder';
-import { UserUtils } from '../../support/utils/UserUtils';
-import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
-
-const adapterName = 'simulator';
-
-const pipelineInput = PipelineBuilder.create('Pipeline Test')
-    .addSource(adapterName)
-    .addProcessingElement(
-        PipelineElementBuilder.create('field_renamer')
-            .addInput('drop-down', 'convert-property', 'timestamp')
-            .addInput('input', 'field-name', 't')
-            .build(),
-    )
-    .addSink(
-        PipelineElementBuilder.create('data_lake')
-            .addInput('input', 'db_measurement', 'demo')
-            .build(),
-    )
-    .build();
-
-describe('Test Enhanced Adapter Deletion', () => {
-    beforeEach('Setup Test', () => {
-        cy.initStreamPipesTest();
-        UserUtils.addUser(UserUtils.userWithAdapterAndPipelineAdminRights);
-    });
-
-    it('Test Delete Adapter and Associated Pipelines', () => {
-        ConnectUtils.addMachineDataSimulator(adapterName, false);
-        PipelineUtils.addPipeline(pipelineInput);
-
-        ConnectUtils.deleteAdapterAndAssociatedPipelines();
-    });
-
-    it('Test Admin Should Be Able to Delete Adapter and Not Owned Associated Pipelines', () => {
-        // Let the user create the adapter and the pipeline
-        ConnectUtils.addMachineDataSimulator(adapterName, false);
-        PipelineUtils.addPipeline(pipelineInput);
-
-        // Then let the admin delete them
-        UserUtils.switchUser(UserUtils.adminUser);
-        ConnectUtils.deleteAdapterAndAssociatedPipelines(true);
-    });
-
-    it('Test Delete Adapter and Associated Pipelines Permission Denied', () => {
-        // Let the admin create the adapter and the pipeline
-        UserUtils.switchUser(UserUtils.adminUser);
-        ConnectUtils.addMachineDataSimulator(adapterName, false);
-        PipelineUtils.addPipeline(pipelineInput);
-
-        // Then the user shouldn't be able to delete them
-        UserUtils.switchUser(UserUtils.userWithAdapterAndPipelineAdminRights);
-        ConnectUtils.deleteAdapterAndAssociatedPipelinesPermissionDenied();
-    });
-});
diff --git a/ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts b/ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts
new file mode 100644
index 0000000000..e10fd81873
--- /dev/null
+++ b/ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts
@@ -0,0 +1,43 @@
+/*
+ *  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 { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { UserUtils } from '../../support/utils/UserUtils';
+import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
+
+describe('Test Enhanced Adapter Deletion', () => {
+    beforeEach('Setup Test', () => {
+        cy.initStreamPipesTest();
+        UserUtils.addUser(UserUtils.userWithAdapterAndPipelineAdminRights);
+    });
+
+    it('Test Delete Adapter and Associated Pipelines', () => {
+        PipelineUtils.addSampleAdapterAndPipeline();
+
+        ConnectUtils.deleteAdapterAndAssociatedPipelines();
+    });
+
+    it('Test Admin Should Be Able to Delete Adapter and Not Owned Associated Pipelines', () => {
+        // Let the user create the adapter and the pipeline
+        PipelineUtils.addSampleAdapterAndPipeline();
+
+        // Then let the admin delete them
+        UserUtils.switchUser(UserUtils.adminUser);
+        ConnectUtils.deleteAdapterAndAssociatedPipelines(true);
+    });
+});
diff --git a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
index 21a1604fb3..71f2356ed9 100644
--- a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
+++ b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts
@@ -123,7 +123,10 @@ function getLocalizedDateString(date: Date) {
 }
 
 function getLocalizedTimeString(date: Date) {
-    return date.toLocaleTimeString().slice(0, 8);
+    const hours = String(date.getHours()).padStart(2, '0');
+    const minutes = String(date.getMinutes()).padStart(2, '0');
+    const seconds = String(date.getSeconds()).padStart(2, '0');
+    return `${hours}:${minutes}:${seconds}`;
 }
 
 function parseTimeStringToSeconds(timeString: string) {
diff --git a/ui/cypress/tests/pipeline/multiUser/pipelineMultiUserSupport.ts b/ui/cypress/tests/pipeline/multiUser/pipelineMultiUserSupport.ts
new file mode 100644
index 0000000000..31f61716e4
--- /dev/null
+++ b/ui/cypress/tests/pipeline/multiUser/pipelineMultiUserSupport.ts
@@ -0,0 +1,49 @@
+/*
+ * 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 { UserUtils } from '../../../support/utils/UserUtils';
+import { UserRole } from '../../../../src/app/_enums/user-role.enum';
+import { PipelineUtils } from '../../../support/utils/pipeline/PipelineUtils';
+
+describe('Test Pipeline Multi User support', () => {
+    beforeEach('Setup Test', () => {
+        cy.initStreamPipesTest();
+    });
+
+    it('Test with pipeline admin account', () => {
+        testPipelineNotShownForOtherUsers(UserRole.ROLE_PIPELINE_ADMIN);
+    });
+
+    it('Test with pipeline user account', () => {
+        testPipelineNotShownForOtherUsers(UserRole.ROLE_PIPELINE_USER);
+    });
+
+    /**
+     * This function validates that the pipeline is only shown to
+     * the user who created it
+     */
+    function testPipelineNotShownForOtherUsers(userRole: UserRole) {
+        const pipelineAdminUser = UserUtils.createUser('user1', userRole);
+
+        PipelineUtils.addSampleAdapterAndPipeline();
+
+        UserUtils.switchUser(pipelineAdminUser);
+
+        PipelineUtils.checkAmountOfPipelinesPipeline(0);
+    }
+});
diff --git a/ui/cypress/tests/pipeline/pipelineTest.smoke.spec.ts b/ui/cypress/tests/pipeline/pipelineTest.smoke.spec.ts
index 010f7695f9..f17be7abc5 100644
--- a/ui/cypress/tests/pipeline/pipelineTest.smoke.spec.ts
+++ b/ui/cypress/tests/pipeline/pipelineTest.smoke.spec.ts
@@ -16,36 +16,15 @@
  *
  */
 
-import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
 import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
-import { PipelineElementBuilder } from '../../support/builder/PipelineElementBuilder';
-import { PipelineBuilder } from '../../support/builder/PipelineBuilder';
-
-const adapterName = 'simulator';
 
 describe('Test Random Data Simulator Stream Adapter', () => {
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
-        ConnectUtils.addMachineDataSimulator(adapterName);
     });
 
     it('Perform Test', () => {
-        const pipelineInput = PipelineBuilder.create('Pipeline Test')
-            .addSource(adapterName)
-            .addProcessingElement(
-                PipelineElementBuilder.create('field_renamer')
-                    .addInput('drop-down', 'convert-property', 'timestamp')
-                    .addInput('input', 'field-name', 't')
-                    .build(),
-            )
-            .addSink(
-                PipelineElementBuilder.create('data_lake')
-                    .addInput('input', 'db_measurement', 'demo')
-                    .build(),
-            )
-            .build();
-
-        PipelineUtils.addPipeline(pipelineInput);
+        PipelineUtils.addSampleAdapterAndPipeline();
         PipelineUtils.deletePipeline();
     });
 });
diff --git a/ui/cypress/tests/pipeline/updatePipelineTest.smoke.spec.ts b/ui/cypress/tests/pipeline/updatePipelineTest.smoke.spec.ts
index 5de2359906..f1ff02d0c9 100644
--- a/ui/cypress/tests/pipeline/updatePipelineTest.smoke.spec.ts
+++ b/ui/cypress/tests/pipeline/updatePipelineTest.smoke.spec.ts
@@ -16,36 +16,15 @@
  *
  */
 
-import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
 import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
-import { PipelineElementBuilder } from '../../support/builder/PipelineElementBuilder';
-import { PipelineBuilder } from '../../support/builder/PipelineBuilder';
-
-const adapterName = 'simulator';
 
 describe('Test update of running pipeline', () => {
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
-        ConnectUtils.addMachineDataSimulator(adapterName);
     });
 
     it('Perform Test', () => {
-        const pipelineInput = PipelineBuilder.create('Pipeline Test')
-            .addSource(adapterName)
-            .addProcessingElement(
-                PipelineElementBuilder.create('field_renamer')
-                    .addInput('drop-down', 'convert-property', 'timestamp')
-                    .addInput('input', 'field-name', 't')
-                    .build(),
-            )
-            .addSink(
-                PipelineElementBuilder.create('data_lake')
-                    .addInput('input', 'db_measurement', 'demo')
-                    .build(),
-            )
-            .build();
-
-        PipelineUtils.addPipeline(pipelineInput);
+        PipelineUtils.addSampleAdapterAndPipeline();
         PipelineUtils.editPipeline();
         cy.wait(1000);
         PipelineUtils.startPipeline();
diff --git a/ui/cypress/tests/userManagement/testGroupManagement.spec.ts b/ui/cypress/tests/userManagement/testGroupManagement.spec.ts
index 03d7159568..4c62669b2b 100644
--- a/ui/cypress/tests/userManagement/testGroupManagement.spec.ts
+++ b/ui/cypress/tests/userManagement/testGroupManagement.spec.ts
@@ -23,6 +23,8 @@ import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
 import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
 import { PipelineElementBuilder } from '../../support/builder/PipelineElementBuilder';
 import { PipelineBuilder } from '../../support/builder/PipelineBuilder';
+import { GeneralUtils } from '../../support/utils/GeneralUtils';
+import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
 
 describe('Test Group Management for Pipelines', () => {
     beforeEach('Setup Test', () => {
@@ -94,18 +96,15 @@ describe('Test Group Management for Pipelines', () => {
 
         // Add user group to pipeline
         PipelineUtils.goToPipelines();
-        cy.dataCy('share').click();
+        PermissionUtils.openManagePermissions();
         cy.get('label').contains('Authorized Groups').click();
         cy.get('mat-option').contains('User_Group').click();
-        cy.dataCy('sp-element-edit-user-save').click();
+        PermissionUtils.save();
 
         // Login as first user which belongs to user group with pipeline admin role
         UserUtils.switchUser(user);
 
-        cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-            'have.length',
-            4,
-        );
+        GeneralUtils.validateAmountOfNavigationIcons(4);
 
         // Check if pipeline is visible
         PipelineUtils.goToPipelines();
@@ -121,10 +120,7 @@ describe('Test Group Management for Pipelines', () => {
         // Login as user2
         UserUtils.switchUser(user2);
 
-        cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-            'have.length',
-            3,
-        );
+        GeneralUtils.validateAmountOfNavigationIcons(3);
 
         // Check if pipeline is invisible to user2
         PipelineUtils.goToPipelines();
diff --git a/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts b/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts
index 5b0dace368..04290d02ca 100644
--- a/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts
@@ -15,48 +15,53 @@
  *  limitations under the License.
  *
  */
-import { UserBuilder } from '../../support/builder/UserBuilder';
 import { UserRole } from '../../../src/app/_enums/user-role.enum';
 import { UserUtils } from '../../support/utils/UserUtils';
 import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
-import { ConnectBtns } from '../../support/utils/connect/ConnectBtns';
+import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
+import { GeneralUtils } from '../../support/utils/GeneralUtils';
 
 describe('Test User Roles for Connect', () => {
+    var connectAdminUser;
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
+        connectAdminUser = UserUtils.createUser(
+            'user',
+            UserRole.ROLE_CONNECT_ADMIN,
+        );
         ConnectUtils.addMachineDataSimulator('simulator');
     });
 
-    it('Perform Test', () => {
-        // Add connect admin user
-        const connect_admin = UserBuilder.create('user@streampipes.apache.org')
-            .setName('connect_admin')
-            .setPassword('password')
-            .addRole(UserRole.ROLE_CONNECT_ADMIN)
-            .build();
-        UserUtils.addUser(connect_admin);
-
-        // Login as user and check if connect is visible to user
-        UserUtils.switchUser(connect_admin);
-
-        cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-            'have.length',
-            3,
-        );
+    it('Connect admin should not see adapters of other users', () => {
+        UserUtils.switchUser(connectAdminUser);
 
-        ConnectUtils.goToConnect();
-        cy.dataCy('all-adapters-table', { timeout: 10000 }).should(
-            'have.length',
-            1,
-        );
-        cy.dataCy('all-adapters-table', { timeout: 10000 }).should(
-            'contain',
-            'simulator',
-        );
+        GeneralUtils.validateAmountOfNavigationIcons(3);
+
+        // Validate that no adapter is visible
+        ConnectUtils.checkAmountOfAdapters(0);
+    });
+
+    it('Connect admin should see public adapters of other users', () => {
+        // Set adapter to public
+        PermissionUtils.markElementAsPublic();
+
+        UserUtils.switchUser(connectAdminUser);
+
+        GeneralUtils.validateAmountOfNavigationIcons(3);
+
+        // Validate that adapter is visible
+        ConnectUtils.checkAmountOfAdapters(1);
+    });
+
+    it('Connect admin should see shared adapters of other users', () => {
+        // Share adapter with user
+        PermissionUtils.authorizeUser(connectAdminUser.email);
+
+        UserUtils.switchUser(connectAdminUser);
+
+        GeneralUtils.validateAmountOfNavigationIcons(3);
 
-        // validate that adapter can be stopped and edited
-        ConnectBtns.stopAdapter().click();
-        ConnectBtns.editAdapter().should('not.be.disabled');
-        ConnectBtns.editAdapter().click();
+        // Validate that adapter is visible
+        ConnectUtils.checkAmountOfAdapters(1);
     });
 });
diff --git a/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts b/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts
index 94d83418ff..3c690ffe92 100644
--- a/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts
@@ -16,88 +16,90 @@
  *
  */
 
-import { UserBuilder } from '../../support/builder/UserBuilder';
 import { UserRole } from '../../../src/app/_enums/user-role.enum';
 import { UserUtils } from '../../support/utils/UserUtils';
 import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
 import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
-import { PipelineElementBuilder } from '../../support/builder/PipelineElementBuilder';
-import { PipelineBuilder } from '../../support/builder/PipelineBuilder';
+import { GeneralUtils } from '../../support/utils/GeneralUtils';
+import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
+import { PipelineBtns } from '../../support/utils/pipeline/PipelineBtns';
 
 describe('Test User Roles for Pipelines', () => {
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
-        ConnectUtils.addMachineDataSimulator('simulator');
-        const pipelineInput = PipelineBuilder.create('Pipeline Test')
-            .addSource('simulator')
-            .addProcessingElement(
-                PipelineElementBuilder.create('field_renamer')
-                    .addInput('drop-down', 'convert-property', 'timestamp')
-                    .addInput('input', 'field-name', 't')
-                    .build(),
-            )
-            .addSink(
-                PipelineElementBuilder.create('data_lake')
-                    .addInput('input', 'db_measurement', 'demo')
-                    .build(),
-            )
-            .build();
-
-        PipelineUtils.addPipeline(pipelineInput);
+        // Create a machine data simulator with a sample pipeline for the tests
+        ConnectUtils.addMachineDataSimulator('simulator', true);
     });
 
-    it('Perform Test', () => {
-        // Add new user
-        UserUtils.goToUserConfiguration();
-
-        cy.dataCy('user-accounts-table-row', { timeout: 10000 }).should(
-            'have.length',
-            1,
+    it('Pipeline admin should not see pipelines of other users', () => {
+        const newUser = UserUtils.createUser(
+            'user',
+            UserRole.ROLE_PIPELINE_ADMIN,
         );
 
-        const email = 'user@streampipes.apache.org';
-        const name = 'test_user';
-        const user = UserBuilder.create(email)
-            .setName(name)
-            .setPassword(name)
-            .addRole(UserRole.ROLE_PIPELINE_USER)
-            .build();
+        // Login as user and check if pipeline is visible to user
+        UserUtils.switchUser(newUser);
+
+        GeneralUtils.validateAmountOfNavigationIcons(4);
 
-        UserUtils.addUser(user);
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(0);
+    });
 
-        // Check if user is added successfully
-        cy.dataCy('user-accounts-table-row', { timeout: 10000 }).should(
-            'have.length',
-            2,
+    it('Pipeline admin should see public pipelines of other users', () => {
+        const newUser = UserUtils.createUser(
+            'user',
+            UserRole.ROLE_PIPELINE_ADMIN,
         );
 
         // Add new authorized user to pipeline
         PipelineUtils.goToPipelines();
-        cy.dataCy('share').click();
-        cy.get('label').contains('Authorized Users').click();
-        cy.get('mat-option').contains(email).click();
-        cy.dataCy('sp-element-edit-user-save').click();
+        PermissionUtils.markElementAsPublic();
 
         // Login as user and check if pipeline is visible to user
-        UserUtils.switchUser(user);
+        UserUtils.switchUser(newUser);
+
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(1);
+    });
 
-        cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-            'have.length',
-            3,
+    it(' Pipeline admin should see shared pipelines of other users', () => {
+        const newUser = UserUtils.createUser(
+            'user',
+            UserRole.ROLE_PIPELINE_ADMIN,
         );
 
+        // Add new authorized user to pipeline
         PipelineUtils.goToPipelines();
-        cy.dataCy('all-pipelines-table', { timeout: 10000 }).should(
-            'have.length',
-            1,
-        );
-        cy.dataCy('all-pipelines-table', { timeout: 10000 }).should(
-            'contain',
-            'Pipeline Test',
+        PermissionUtils.markElementAsPublic();
+        PermissionUtils.authorizeUser(newUser.email);
+
+        // Login as user and check if pipeline is visible to user
+        UserUtils.switchUser(newUser);
+
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(1);
+    });
+
+    it(' Pipeline user should see shared pipelines of other users but not be able to edit them', () => {
+        const newUser = UserUtils.createUser(
+            'user',
+            UserRole.ROLE_PIPELINE_USER,
         );
 
-        // Delete user
-        UserUtils.switchUser(UserUtils.adminUser);
-        UserUtils.deleteUser(user);
+        // Add new authorized user to pipeline
+        PipelineUtils.goToPipelines();
+        // PermissionUtils.markElementAsPublic();
+        PermissionUtils.authorizeUser(newUser.email);
+
+        // Login as user and check if pipeline is visible to user
+        UserUtils.switchUser(newUser);
+
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(1);
+
+        // A pipeline user should not be able to stop the pipeline or delete it
+        PipelineBtns.deletePipeline().should('not.exist');
+        PipelineBtns.stopPipeline().should('be.disabled');
     });
 });
diff --git a/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts b/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts
index 34e4624d63..4c00e01667 100644
--- a/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts
+++ b/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts
@@ -19,6 +19,7 @@
 import { UserBuilder } from '../../support/builder/UserBuilder';
 import { UserRole } from '../../../src/app/_enums/user-role.enum';
 import { UserUtils } from '../../support/utils/UserUtils';
+import { GeneralUtils } from '../../support/utils/GeneralUtils';
 
 const testedRoles = [
     UserRole.ROLE_PIPELINE_ADMIN,
@@ -38,10 +39,7 @@ for (var i = 0; i < testedRoles.length; i++) {
         it('Perform Test', () => {
             // Add new user
             UserUtils.goToUserConfiguration();
-            cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-                'have.length',
-                8,
-            );
+            GeneralUtils.validateAmountOfNavigationIcons(8);
 
             cy.dataCy('user-accounts-table-row', { timeout: 10000 }).should(
                 'have.length',
@@ -68,30 +66,15 @@ for (var i = 0; i < testedRoles.length; i++) {
 
             // Check if every role displays correct navigation menu
             if (testRole == UserRole.ROLE_PIPELINE_ADMIN) {
-                cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-                    'have.length',
-                    4,
-                );
+                GeneralUtils.validateAmountOfNavigationIcons(4);
             } else if (testRole == UserRole.ROLE_DASHBOARD_ADMIN) {
-                cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-                    'have.length',
-                    4,
-                );
+                GeneralUtils.validateAmountOfNavigationIcons(4);
             } else if (testRole == UserRole.ROLE_DATA_EXPLORER_ADMIN) {
-                cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-                    'have.length',
-                    4,
-                );
+                GeneralUtils.validateAmountOfNavigationIcons(4);
             } else if (testRole == UserRole.ROLE_CONNECT_ADMIN) {
-                cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-                    'have.length',
-                    3,
-                );
+                GeneralUtils.validateAmountOfNavigationIcons(3);
             } else if (testRole == UserRole.ROLE_ASSET_ADMIN) {
-                cy.dataCy('navigation-icon', { timeout: 10000 }).should(
-                    'have.length',
-                    3,
-                );
+                GeneralUtils.validateAmountOfNavigationIcons(3);
             }
 
             // Login as admin and delete user