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

riemer pushed a commit to branch STREAMPIPES-426
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit c4e23c37265abcfd7321792ea17d51d79724fefa
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Oct 20 17:00:09 2021 +0200

    [STREAMPIPES-426] Improve permission management
---
 pom.xml                                            |   6 +
 .../backend/StreamPipesResourceConfig.java         |   2 +
 .../streampipes/model/client/user/Permission.java  |  27 +++-
 .../model/client/user/PermissionBuilder.java       |   4 +-
 .../model/client/user/PermissionEntry.java         |  63 ++++++++
 .../model/client/user/PrincipalType.java           |   3 +-
 .../manager/pipeline/PipelineManager.java          |   3 +-
 .../manager/setup/CouchDbInstallationStep.java     |   6 +-
 .../manager/pipeline/TestPipelineManager.java      |  99 +++++--------
 .../management/PermissionResourceManager.java      |  26 ++--
 .../resource/management/SpResourceManager.java     |   4 +
 .../rest/core/base/impl/AbstractRestResource.java  |   5 +
 streampipes-rest/pom.xml                           |   6 +
 .../streampipes/rest/impl/PipelineResource.java    |   6 -
 .../apache/streampipes/rest/impl/UserResource.java |   6 +-
 .../rest/impl/admin/PermissionResource.java        |  14 +-
 .../storage/api/IPermissionStorage.java            |   2 +
 .../couchdb/impl/PermissionStorageImpl.java        |  10 ++
 .../StreamPipesCredentialsMatcher.java             |  20 +--
 .../authentication/StreamPipesRealm.java           | 109 --------------
 ui/angular.json                                    |   2 +
 ui/src/app/app-overview/app-overview.component.ts  |   2 +-
 .../app/core-model/gen/streampipes-model-client.ts |  64 +++++---
 ui/src/app/core-ui/core-ui.module.ts               |   2 +
 .../object-permission-dialog.component.html        |  83 +++++++++++
 .../object-permission-dialog.component.scss        |  10 +-
 .../object-permission-dialog.component.ts          | 165 +++++++++++++++++++++
 .../pipeline-overview.component.html               |  44 ++++--
 .../pipeline-overview.component.ts                 |  10 +-
 .../services/pipeline-operations.service.ts        |  98 +++++++-----
 .../platform-services/apis/permissions.service.ts  |  48 ++++++
 ui/src/app/platform-services/apis/user.service.ts  |  17 ++-
 ui/src/app/platform-services/platform.module.ts    |   5 +-
 ui/src/app/services/auth.service.ts                |   3 +-
 34 files changed, 665 insertions(+), 309 deletions(-)

diff --git a/pom.xml b/pom.xml
index 625b419..a045909 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,7 @@
         <influxdb.version>2.21</influxdb.version>
         <jackson.version>2.12.3</jackson.version>
         <jakarta-annotation.version>1.3.5</jakarta-annotation.version>
+        <jakarta-mail.version>1.6.7</jakarta-mail.version>
         <jakarta-validation.version>2.0.2</jakarta-validation.version>
         <javapoet.version>1.6.1</javapoet.version>
         <javax-annotation.version>1.3.2</javax-annotation.version>
@@ -229,6 +230,11 @@
                 <version>${retrofit.version}</version>
             </dependency>
             <dependency>
+                <groupId>com.sun.mail</groupId>
+                <artifactId>jakarta.mail</artifactId>
+                <version>${jakarta-mail.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>commons-beanutils</groupId>
                 <artifactId>commons-beanutils</artifactId>
                 <version>${commons-beanutils.version}</version>
diff --git 
a/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
 
b/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
index 7ae8e19..de5ab6f 100644
--- 
a/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
+++ 
b/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
@@ -25,6 +25,7 @@ import 
org.apache.streampipes.ps.PipelineElementTemplateResource;
 import org.apache.streampipes.rest.impl.*;
 import org.apache.streampipes.rest.impl.admin.ConsulConfig;
 import org.apache.streampipes.rest.impl.admin.EmailConfigurationResource;
+import org.apache.streampipes.rest.impl.admin.PermissionResource;
 import org.apache.streampipes.rest.impl.admin.UserGroupResource;
 import org.apache.streampipes.rest.impl.connect.*;
 import org.apache.streampipes.rest.impl.dashboard.Dashboard;
@@ -73,6 +74,7 @@ public class StreamPipesResourceConfig extends ResourceConfig 
{
         register(MeasurementUnitResource.class);
         register(Notification.class);
         register(OntologyMeasurementUnit.class);
+        register(PermissionResource.class);
         register(PersistedDataStreamResource.class);
         register(PipelineCanvasMetadataCache.class);
         register(PipelineCanvasMetadataResource.class);
diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
index 2c0f2cb..d1ed397 100644
--- 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/Permission.java
@@ -19,10 +19,12 @@ package org.apache.streampipes.model.client.user;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.google.gson.annotations.SerializedName;
+import org.apache.streampipes.model.shared.annotation.TsModel;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@TsModel
 public class Permission {
 
   protected @SerializedName("_id") String permissionId;
@@ -33,13 +35,14 @@ public class Permission {
 
   private String objectInstanceId;
   private String objectClassName;
+  private boolean publicElement;
 
   private String ownerSid;
 
-  private List<String> allowedSids;
+  private List<PermissionEntry> grantedAuthorities;
 
   public Permission() {
-    this.allowedSids = new ArrayList<>();
+    this.grantedAuthorities = new ArrayList<>();
   }
 
   public String getPermissionId() {
@@ -82,16 +85,16 @@ public class Permission {
     this.ownerSid = ownerSid;
   }
 
-  public void addAllowedSid(String sid) {
-    this.allowedSids.add(sid);
+  public void addPermissionEntry(PermissionEntry permissionEntry) {
+    this.grantedAuthorities.add(permissionEntry);
   }
 
-  public List<String> getAllowedSids() {
-    return allowedSids;
+  public List<PermissionEntry> getGrantedAuthorities() {
+    return grantedAuthorities;
   }
 
-  public void setAllowedSids(List<String> allowedSids) {
-    this.allowedSids = allowedSids;
+  public void setGrantedAuthorities(List<PermissionEntry> grantedAuthorities) {
+    this.grantedAuthorities = grantedAuthorities;
   }
 
   public String get$type() {
@@ -101,4 +104,12 @@ public class Permission {
   public void set$type(String $type) {
     this.$type = $type;
   }
+
+  public boolean isPublicElement() {
+    return publicElement;
+  }
+
+  public void setPublicElement(boolean publicElement) {
+    this.publicElement = publicElement;
+  }
 }
diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionBuilder.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionBuilder.java
index 70da030..f739f29 100644
--- 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionBuilder.java
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionBuilder.java
@@ -40,8 +40,8 @@ public class PermissionBuilder {
     this.permission.setOwnerSid(ownerSid);
   }
 
-  public PermissionBuilder with(String sid) {
-    this.permission.addAllowedSid(sid);
+  public PermissionBuilder with(PermissionEntry permissionEntry) {
+    this.permission.addPermissionEntry(permissionEntry);
     return this;
   }
 
diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionEntry.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionEntry.java
new file mode 100644
index 0000000..f9dd25d
--- /dev/null
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PermissionEntry.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.streampipes.model.client.user;
+
+import java.util.Objects;
+
+public class PermissionEntry {
+
+  private String sid;
+  private PrincipalType principalType;
+
+  public PermissionEntry() {
+  }
+
+  public PermissionEntry(String sid, PrincipalType principalType) {
+    this.sid = sid;
+    this.principalType = principalType;
+  }
+
+  public String getSid() {
+    return sid;
+  }
+
+  public void setSid(String sid) {
+    this.sid = sid;
+  }
+
+  public PrincipalType getPrincipalType() {
+    return principalType;
+  }
+
+  public void setPrincipalType(PrincipalType principalType) {
+    this.principalType = principalType;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    PermissionEntry that = (PermissionEntry) o;
+    return sid.equals(that.sid) && principalType == that.principalType;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(sid, principalType);
+  }
+}
diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PrincipalType.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PrincipalType.java
index be84b20..620c9a4 100644
--- 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PrincipalType.java
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/PrincipalType.java
@@ -20,5 +20,6 @@ package org.apache.streampipes.model.client.user;
 public enum PrincipalType {
 
   USER_ACCOUNT,
-  SERVICE_ACCOUNT
+  SERVICE_ACCOUNT,
+  GROUP
 }
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/PipelineManager.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/PipelineManager.java
index c1a0680..6a077a0 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/PipelineManager.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/PipelineManager.java
@@ -66,7 +66,8 @@ public class PipelineManager {
      * @param pipeline
      * @return the pipelineId
      */
-    public static String addPipeline(String principalSid, Pipeline pipeline) {
+    public static String addPipeline(String principalSid,
+                                     Pipeline pipeline) {
 
         // call by reference bad smell
         String pipelineId = UUIDGenerator.generateUuid();
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
index c1a05f8..76f0160 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/setup/CouchDbInstallationStep.java
@@ -157,7 +157,10 @@ public class CouchDbInstallationStep extends 
InstallationStep {
       tokenFunction.setMap("function(doc) { if (doc.properties.userApiTokens) 
{ doc.properties.userApiTokens.forEach(function(token) { 
emit(token.properties.hashedToken, doc.properties.email); });}}");
 
       MapReduce userPermissionFunction = new MapReduce();
-      userPermissionFunction.setMap("function(doc) { if (doc.$type === 
'permission') {emit(doc.ownerSid, doc); for(var i = 0; i < 
doc.allowedSids.length; i++) {emit(doc.allowedSids[i],doc)}}}");
+      userPermissionFunction.setMap("function(doc) { if (doc.$type === 
'permission') {emit(doc.ownerSid, doc); for(var i = 0; i < 
doc.grantedAuthorities.length; i++) 
{emit(doc.grantedAuthorities[i].sid,doc)}}}");
+
+      MapReduce objectPermissionFunction = new MapReduce();
+      objectPermissionFunction.setMap("function(doc) { if (doc.$type === 
'permission') {emit(doc.objectInstanceId, doc);}}");
 
       views.put("password", passwordFunction);
       views.put("username", usernameFunction);
@@ -165,6 +168,7 @@ public class CouchDbInstallationStep extends 
InstallationStep {
       views.put("permissions", permissionFunction);
       views.put("token", tokenFunction);
       views.put("userpermissions", userPermissionFunction);
+      views.put("objectpermissions", objectPermissionFunction);
 
       userDocument.setViews(views);
       Response resp = 
Utils.getCouchDbUserClient().design().synchronizeWithDb(userDocument);
diff --git 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/pipeline/TestPipelineManager.java
 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/pipeline/TestPipelineManager.java
index b5a33e0..88bc61b 100644
--- 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/pipeline/TestPipelineManager.java
+++ 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/pipeline/TestPipelineManager.java
@@ -41,62 +41,43 @@ import static junit.framework.TestCase.assertNotNull;
         Operations.class})
 public class TestPipelineManager {
 
-    @Before
-    public  void before() {
-        PowerMockito.mockStatic(
-                UserManagementService.class);
-    }
-
-    @Test
-    public void testStartPipeline() {
-        // Prepare
-        PipelineOperationStatus expectedPipelineOperationStatus = 
getPipelineOperationStatus();
-        PowerMockito.stub(PowerMockito.method(PipelineManager.class, 
"getPipeline", 
String.class)).toReturn(DummyPipelineGenerator.makePipelineWithPipelineName());
-        PowerMockito.stub(PowerMockito.method(Operations.class, 
"startPipeline", Pipeline.class)).toReturn(expectedPipelineOperationStatus);
-
-        // Test
-        PipelineOperationStatus result = 
PipelineManager.startPipeline("pipelineId");
-
-        // Assertions
-        assertNotNull(result);
-        assertEquals(expectedPipelineOperationStatus.getPipelineName(), 
result.getPipelineName());
-    }
-
-    @Test
-    public void testStopPipeline() {
-        // Prepare
-        PipelineOperationStatus expectedPipelineOperationStatus = 
getPipelineOperationStatus();
-        PowerMockito.stub(PowerMockito.method(PipelineManager.class, 
"getPipeline", 
String.class)).toReturn(DummyPipelineGenerator.makePipelineWithPipelineName());
-        PowerMockito.stub(PowerMockito.method(Operations.class, 
"stopPipeline", Pipeline.class, 
boolean.class)).toReturn(expectedPipelineOperationStatus);
-
-        // Test
-        PipelineOperationStatus result = 
PipelineManager.stopPipeline("pipelineId", true);
-
-        // Assertions
-        assertNotNull(result);
-        assertEquals(expectedPipelineOperationStatus.getPipelineName(), 
result.getPipelineName());
-    }
-
-    @Test
-    public void testAddPipeline() {
-        // Prepare
-        PowerMockito.mockStatic(Operations.class);
-        Pipeline pipeline = 
DummyPipelineGenerator.makePipelineWithProcessorAndSink();
-
-        String username = "[email protected]";
-
-        // Test
-        String result = PipelineManager.addPipeline(username, pipeline);
-
-        // Assertions
-        assertNotNull(result);
-        assertNotNull(pipeline.getPipelineId());
-        assertEquals(username, pipeline.getCreatedByUser());
-        pipeline.getSepas().forEach(processor -> assertEquals(username, 
processor.getCorrespondingUser()));
-        pipeline.getActions().forEach(sink -> assertEquals(username, 
sink.getCorrespondingUser()));
-    }
-
-    private PipelineOperationStatus getPipelineOperationStatus() {
-        return new PipelineOperationStatus("", 
DummyPipelineGenerator.PIPELINE_NAME,"", new ArrayList<>());
-    }
-}
\ No newline at end of file
+  @Before
+  public void before() {
+    PowerMockito.mockStatic(
+            UserManagementService.class);
+  }
+
+  @Test
+  public void testStartPipeline() {
+    // Prepare
+    PipelineOperationStatus expectedPipelineOperationStatus = 
getPipelineOperationStatus();
+    PowerMockito.stub(PowerMockito.method(PipelineManager.class, 
"getPipeline", 
String.class)).toReturn(DummyPipelineGenerator.makePipelineWithPipelineName());
+    PowerMockito.stub(PowerMockito.method(Operations.class, "startPipeline", 
Pipeline.class)).toReturn(expectedPipelineOperationStatus);
+
+    // Test
+    PipelineOperationStatus result = 
PipelineManager.startPipeline("pipelineId");
+
+    // Assertions
+    assertNotNull(result);
+    assertEquals(expectedPipelineOperationStatus.getPipelineName(), 
result.getPipelineName());
+  }
+
+  @Test
+  public void testStopPipeline() {
+    // Prepare
+    PipelineOperationStatus expectedPipelineOperationStatus = 
getPipelineOperationStatus();
+    PowerMockito.stub(PowerMockito.method(PipelineManager.class, 
"getPipeline", 
String.class)).toReturn(DummyPipelineGenerator.makePipelineWithPipelineName());
+    PowerMockito.stub(PowerMockito.method(Operations.class, "stopPipeline", 
Pipeline.class, boolean.class)).toReturn(expectedPipelineOperationStatus);
+
+    // Test
+    PipelineOperationStatus result = 
PipelineManager.stopPipeline("pipelineId", true);
+
+    // Assertions
+    assertNotNull(result);
+    assertEquals(expectedPipelineOperationStatus.getPipelineName(), 
result.getPipelineName());
+  }
+
+  private PipelineOperationStatus getPipelineOperationStatus() {
+    return new PipelineOperationStatus("", 
DummyPipelineGenerator.PIPELINE_NAME, "", new ArrayList<>());
+  }
+}
diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
similarity index 60%
copy from 
streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
copy to 
streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
index 65ac07c..f19a634 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/PermissionResourceManager.java
@@ -15,25 +15,27 @@
  * limitations under the License.
  *
  */
-package org.apache.streampipes.storage.api;
+package org.apache.streampipes.resource.management;
 
 import org.apache.streampipes.model.client.user.Permission;
+import org.apache.streampipes.storage.api.IPermissionStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import java.util.List;
-import java.util.Set;
 
-public interface IPermissionStorage {
+public class PermissionResourceManager {
 
-  List<Permission> getAllPermissions();
+  private final IPermissionStorage db;
 
-  Permission getPermissionById(String permissionId);
+  public PermissionResourceManager() {
+    this.db = 
StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage();
+  }
 
-  void addPermission(Permission permission);
-
-  void updatePermission(Permission permission);
-
-  void deletePermission(String permissionId);
-
-  Set<String> getObjectPermissions(List<String> sids);
+  public List<Permission> findAll() {
+    return db.getAllPermissions();
+  }
 
+  public List<Permission> findForObjectId(String objectInstanceId) {
+    return db.getUserPermissionsForObject(objectInstanceId);
+  }
 }
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java
index 88e56f2..e5304b0 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java
@@ -38,4 +38,8 @@ public class SpResourceManager {
   public PipelineResourceManager managePipelines() {
     return new PipelineResourceManager();
   }
+
+  public PermissionResourceManager managePermissions() {
+    return new PermissionResourceManager();
+  }
 }
diff --git 
a/streampipes-rest-core-base/src/main/java/org/apache/streampipes/rest/core/base/impl/AbstractRestResource.java
 
b/streampipes-rest-core-base/src/main/java/org/apache/streampipes/rest/core/base/impl/AbstractRestResource.java
index 92796fc..43fd507 100644
--- 
a/streampipes-rest-core-base/src/main/java/org/apache/streampipes/rest/core/base/impl/AbstractRestResource.java
+++ 
b/streampipes-rest-core-base/src/main/java/org/apache/streampipes/rest/core/base/impl/AbstractRestResource.java
@@ -26,6 +26,7 @@ import org.apache.streampipes.model.message.ErrorMessage;
 import org.apache.streampipes.model.message.Message;
 import org.apache.streampipes.model.message.Notification;
 import org.apache.streampipes.model.message.SuccessMessage;
+import org.apache.streampipes.resource.management.SpResourceManager;
 import org.apache.streampipes.rest.shared.impl.AbstractSharedRestInterface;
 import org.apache.streampipes.storage.api.*;
 import org.apache.streampipes.storage.management.StorageDispatcher;
@@ -148,4 +149,8 @@ public abstract class AbstractRestResource extends 
AbstractSharedRestInterface {
     return SpServiceDiscovery.getServiceDiscovery();
   }
 
+  protected SpResourceManager getSpResourceManager() {
+    return new SpResourceManager();
+  }
+
 }
diff --git a/streampipes-rest/pom.xml b/streampipes-rest/pom.xml
index 332189d..3f73aa3 100644
--- a/streampipes-rest/pom.xml
+++ b/streampipes-rest/pom.xml
@@ -120,6 +120,12 @@
             <artifactId>influxdb-java</artifactId>
         </dependency>
 
+        <!-- dependency convergence -->
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>jakarta.mail</artifactId>
+        </dependency>
+
         <!-- Test dependencies -->
         <dependency>
             <groupId>junit</groupId>
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 2f252b2..b155dfc 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
@@ -40,11 +40,8 @@ import 
org.apache.streampipes.model.pipeline.PipelineOperationStatus;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.security.access.prepost.PostFilter;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.security.access.prepost.PreFilter;
 import org.springframework.stereotype.Component;
 
 import javax.ws.rs.*;
@@ -56,8 +53,6 @@ import java.util.List;
 @Path("/v2/pipelines")
 public class PipelineResource extends AbstractAuthGuardedRestResource {
 
-  private static final Logger logger = 
LoggerFactory.getLogger(PipelineResource.class);
-
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @Path("/own")
@@ -146,7 +141,6 @@ public class PipelineResource extends 
AbstractAuthGuardedRestResource {
   @Operation(summary = "Stop the pipeline with the given id",
           tags = {"Pipeline"})
   @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
-  @PreFilter("hasPermission(pipelineId, 'WRITE')")
   public Response stop(@PathParam("pipelineId") String pipelineId,
                        @QueryParam("forceStop") @DefaultValue("false") boolean 
forceStop) {
     try {
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/UserResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/UserResource.java
index c34b0ae..ce108f4 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/UserResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/UserResource.java
@@ -56,10 +56,10 @@ public class UserResource extends 
AbstractAuthGuardedRestResource {
 
   @GET
   @JacksonSerialized
-  @Path("{username}")
+  @Path("{principalId}")
   @Produces(MediaType.APPLICATION_JSON)
-  public Response getUserDetails(@PathParam("username") String username) {
-    Principal principal = getPrincipal(username);
+  public Response getUserDetails(@PathParam("principalId") String principalId) 
{
+    Principal principal = getPrincipalById(principalId);
     removeCredentials(principal);
 
     if (principal != null) {
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
index 3c8cfd7..310b847 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
@@ -17,10 +17,22 @@
  */
 package org.apache.streampipes.rest.impl.admin;
 
+import org.apache.streampipes.model.client.user.Permission;
+import org.apache.streampipes.rest.core.base.impl.AbstractRestResource;
+
+import javax.ws.rs.GET;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import java.util.List;
 
 @Path("/v2/admin/permissions")
-public class PermissionResource {
+public class PermissionResource extends AbstractRestResource {
+
+  @GET
+  @Path("{objectInstanceId}")
+  public List<Permission> 
getPermissionForObject(@PathParam("objectInstanceId") String objectInstanceId) {
+    return 
getSpResourceManager().managePermissions().findForObjectId(objectInstanceId);
+  }
 
 
 }
diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
index 65ac07c..fadcc46 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
+++ 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/IPermissionStorage.java
@@ -36,4 +36,6 @@ public interface IPermissionStorage {
 
   Set<String> getObjectPermissions(List<String> sids);
 
+  List<Permission> getUserPermissionsForObject(String objectInstanceId);
+
 }
diff --git 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/PermissionStorageImpl.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/PermissionStorageImpl.java
index b518c4a..fef5df1 100644
--- 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/PermissionStorageImpl.java
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/PermissionStorageImpl.java
@@ -70,6 +70,16 @@ public class PermissionStorageImpl extends CrudDao 
implements IPermissionStorage
     return toPermissionSet(objectInstanceSids);
   }
 
+  public List<Permission> getUserPermissionsForObject(String objectInstanceId) 
{
+
+    return couchDbClientSupplier
+            .get()
+            .view("users/objectpermissions")
+            .key(objectInstanceId)
+            .includeDocs(true)
+            .query(Permission.class);
+  }
+
   private Set<String> toPermissionSet(List<Permission> permissions) {
     return 
permissions.stream().map(Permission::getObjectInstanceId).collect(Collectors.toSet());
   }
diff --git 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesCredentialsMatcher.java
 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesCredentialsMatcher.java
index e4cbea6..cc0c94f 100644
--- 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesCredentialsMatcher.java
+++ 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesCredentialsMatcher.java
@@ -26,25 +26,11 @@ import java.security.spec.InvalidKeySpecException;
 
 public class StreamPipesCredentialsMatcher implements PasswordEncoder {
 
-//  @Override
-//  public boolean doCredentialsMatch(AuthenticationToken authenticationToken, 
AuthenticationInfo authenticationInfo) {
-//    try {
-//      return PasswordUtil.validatePassword(new String((char[]) 
authenticationToken
-//                      .getCredentials()),
-//              authenticationInfo
-//              .getCredentials().toString());
-//    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
-//      return false;
-//    }
-//  }
-
   @Override
   public String encode(CharSequence charSequence) {
     try {
       return PasswordUtil.encryptPassword(charSequence.toString());
-    } catch (NoSuchAlgorithmException e) {
-      e.printStackTrace();
-    } catch (InvalidKeySpecException e) {
+    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
       e.printStackTrace();
     }
     return null;
@@ -54,9 +40,7 @@ public class StreamPipesCredentialsMatcher implements 
PasswordEncoder {
   public boolean matches(CharSequence charSequence, String s) {
     try {
       return PasswordUtil.validatePassword(charSequence.toString(), s);
-    } catch (NoSuchAlgorithmException e) {
-      e.printStackTrace();
-    } catch (InvalidKeySpecException e) {
+    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
       e.printStackTrace();
     }
     return false;
diff --git 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesRealm.java
 
b/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesRealm.java
deleted file mode 100644
index c13552d..0000000
--- 
a/streampipes-user-management/src/main/java/org/apache/streampipes/user/management/authentication/StreamPipesRealm.java
+++ /dev/null
@@ -1,109 +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.
-// *
-// */
-//
-//package org.apache.streampipes.user.management.authentication;
-//
-//
-//import org.apache.shiro.authc.*;
-//import org.apache.shiro.realm.Realm;
-//import org.apache.shiro.subject.SimplePrincipalCollection;
-//import org.apache.streampipes.model.client.user.User;
-//import org.apache.streampipes.user.management.service.TokenService;
-//import org.apache.streampipes.user.management.service.UserService;
-//import org.apache.streampipes.user.management.util.TokenUtil;
-//import org.lightcouch.CouchDbException;
-//import org.slf4j.Logger;
-//import org.slf4j.LoggerFactory;
-//
-//
-///**
-// * Principals are a subjects identifying attributes  such as first name, 
last name or user name.
-// * Credentials are usually secret values known only by the subject
-// */
-//public class StreamPipesRealm implements Realm {
-//
-//  private static final Logger LOG = 
LoggerFactory.getLogger(StreamPipesRealm.class);
-//
-//  private StreamPipesCredentialsMatcher credentialsMatcher;
-//
-//  public StreamPipesRealm() {
-//    this.credentialsMatcher = new StreamPipesCredentialsMatcher();
-//  }
-//
-//  @Override
-//  public String getName() {
-//    return "StreamPipeRealm";
-//  }
-//
-//  @Override
-//  /**
-//   * Check if type of AuthenticationToken is supported.
-//   * So far we only support UsernamePasswordToken.
-//   */
-//  public boolean supports(AuthenticationToken authenticationToken) {
-//    return authenticationToken instanceof UsernamePasswordToken || 
authenticationToken instanceof BearerToken;
-//  }
-//
-//  @Override
-//  /**
-//   * Checks if token is correct. See class JavaDoc for creating the password 
view.
-//   */
-//  public AuthenticationInfo getAuthenticationInfo(AuthenticationToken 
authenticationToken) throws AuthenticationException {
-//
-//    if (authenticationToken instanceof UsernamePasswordToken) {
-//
-//      try {
-//        String email = ((UsernamePasswordToken) 
authenticationToken).getUsername();
-//        UserService userService = new UserService(email);
-//
-//        SimpleAuthenticationInfo info = makeInfo(email);
-//        info.setCredentials(userService.getPassword());
-//
-//        if (credentialsMatcher.doCredentialsMatch(authenticationToken, 
info)) {
-//          LOG.info("User successfully authenticated");
-//        } else {
-//          throw new AuthenticationException("Could not authenticate");
-//        }
-//        return info;
-//
-//      } catch (CouchDbException | NullPointerException e) {
-//        e.printStackTrace();
-//      }
-//    } else if (authenticationToken instanceof BearerToken) {
-//      BearerToken token = (BearerToken) authenticationToken;
-//      String hashedToken = TokenUtil.hashToken(token.getToken());
-//      User user = new TokenService().findUserForToken(hashedToken);
-//      SimpleAuthenticationInfo info = makeInfo(user.getEmail());
-//
-//      return info;
-//    }
-//
-//    return null;
-//  }
-//
-//  private SimpleAuthenticationInfo makeInfo(String email) {
-//    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo();
-//    SimplePrincipalCollection principals = new SimplePrincipalCollection();
-//    principals.add(email, this.getName());
-//
-//    LOG.info(principals.toString());
-//    info.setPrincipals(principals);
-//
-//    return info;
-//  }
-//}
diff --git a/ui/angular.json b/ui/angular.json
index 7b233ae..bb40fdd 100644
--- a/ui/angular.json
+++ b/ui/angular.json
@@ -69,6 +69,8 @@
               }
             },
             "development": {
+              "sourceMap": true,
+              "optimization": false,
               "customWebpackConfig": {
                 "path": "./webpack.partial.dev.js"
               }
diff --git a/ui/src/app/app-overview/app-overview.component.ts 
b/ui/src/app/app-overview/app-overview.component.ts
index 733bcfb..30ce1a2 100644
--- a/ui/src/app/app-overview/app-overview.component.ts
+++ b/ui/src/app/app-overview/app-overview.component.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import { Component, OnInit } from "@angular/core";
+import { Component, OnInit } from '@angular/core';
 
 @Component({
     templateUrl: './app-overview.component.html',
diff --git a/ui/src/app/core-model/gen/streampipes-model-client.ts 
b/ui/src/app/core-model/gen/streampipes-model-client.ts
index 93841c1..9f197fb 100644
--- a/ui/src/app/core-model/gen/streampipes-model-client.ts
+++ b/ui/src/app/core-model/gen/streampipes-model-client.ts
@@ -1,25 +1,7 @@
-/*
- * 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.
- *
- */
-
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 2.27.744 on 2021-10-12 
09:29:49.
+// Generated using typescript-generator version 2.27.744 on 2021-10-19 
16:24:30.
 
 export class Element {
     elementId: string;
@@ -140,6 +122,46 @@ export class MatchingResultMessage {
     }
 }
 
+export class Permission {
+    grantedAuthorities: PermissionEntry[];
+    objectClassName: string;
+    objectInstanceId: string;
+    ownerSid: string;
+    permissionId: string;
+    publicElement: boolean;
+    rev: string;
+
+    static fromData(data: Permission, target?: Permission): Permission {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new Permission();
+        instance.permissionId = data.permissionId;
+        instance.rev = data.rev;
+        instance.objectInstanceId = data.objectInstanceId;
+        instance.objectClassName = data.objectClassName;
+        instance.publicElement = data.publicElement;
+        instance.ownerSid = data.ownerSid;
+        instance.grantedAuthorities = 
__getCopyArrayFn(PermissionEntry.fromData)(data.grantedAuthorities);
+        return instance;
+    }
+}
+
+export class PermissionEntry {
+    principalType: PrincipalType;
+    sid: string;
+
+    static fromData(data: PermissionEntry, target?: PermissionEntry): 
PermissionEntry {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new PermissionEntry();
+        instance.sid = data.sid;
+        instance.principalType = data.principalType;
+        return instance;
+    }
+}
+
 export class Principal {
     accountEnabled: boolean;
     accountExpired: boolean;
@@ -278,9 +300,9 @@ export class UserInfo {
     }
 }
 
-export type PrincipalType = "USER_ACCOUNT" | "SERVICE_ACCOUNT";
+export type PrincipalType = "USER_ACCOUNT" | "SERVICE_ACCOUNT" | "GROUP";
 
-export type Privilege = "PRIVILEGE_CREATE_PIPELINE" | 
"PRIVILEGE_READ_PIPELINE" | "PRIVILEGE_UPDATE_PIPELINE" | 
"PRIVILEGE_DELETE_PIPELINE" | "PRIVILEGE_CREATE_ADAPTER" | 
"PRIVILEGE_READ_ADAPTER" | "PRIVILEGE_UPDATE_ADAPTER" | 
"PRIVILEGE_DELETE_ADAPTER" | "PRIVILEGE_CREATE_PIPELINE_ELEMENT" | 
"PRIVILEGE_READ_PIPELINE_ELEMENT" | "PRIVILEGE_UPDATE_PIPELINE_ELEMENT" | 
"PRIVILEGE_DELETE_PIPELINE_ELEMENT" | "PRIVILEGE_CREATE_DASHBOARD" | 
"PRIVILEGE_READ_DASHBOARD" | "PRIVILEGE_UPDATE_DASHBOAR [...]
+export type Privilege = "PRIVILEGE_READ_PIPELINE" | "PRIVILEGE_WRITE_PIPELINE" 
| "PRIVILEGE_DELETE_PIPELINE" | "PRIVILEGE_READ_ADAPTER" | 
"PRIVILEGE_WRITE_ADAPTER" | "PRIVILEGE_DELETE_ADAPTER" | 
"PRIVILEGE_READ_PIPELINE_ELEMENT" | "PRIVILEGE_WRITE_PIPELINE_ELEMENT" | 
"PRIVILEGE_DELETE_PIPELINE_ELEMENT" | "PRIVILEGE_READ_DASHBOARD" | 
"PRIVILEGE_WRITE_DASHBOARD" | "PRIVILEGE_DELETE_DASHBOARD" | 
"PRIVILEGE_READ_DASHBOARD_WIDGET" | "PRIVILEGE_WRITE_DASHBOARD_WIDGET" | 
"PRIVILEGE_DELETE_DASHB [...]
 
 export type Role = "ROLE_ADMIN" | "ROLE_PIPELINE_ADMIN" | 
"ROLE_DASHBOARD_ADMIN" | "ROLE_DATA_EXPLORER_ADMIN" | "ROLE_CONNECT_ADMIN" | 
"ROLE_DASHBOARD_USER" | "ROLE_DATA_EXPLORER_USER" | "ROLE_PIPELINE_USER" | 
"ROLE_APP_USER";
 
diff --git a/ui/src/app/core-ui/core-ui.module.ts 
b/ui/src/app/core-ui/core-ui.module.ts
index 9c9a578..82fe6c7 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -80,6 +80,7 @@ import { ErrorHintComponent } from 
'./error-hint/error-hint.component';
 import { AddToCollectionComponent } from 
'./static-properties/static-collection/add-to-collection/add-to-collection.component';
 import { PipelineStartedStatusComponent } from 
'./pipeline/pipeline-started-status/pipeline-started-status.component';
 import { SplitSectionComponent } from 
'./split-section/split-section.component';
+import { ObjectPermissionDialogComponent } from 
'./object-permission-dialog/object-permission-dialog.component';
 
 @NgModule({
   imports: [
@@ -116,6 +117,7 @@ import { SplitSectionComponent } from 
'./split-section/split-section.component';
     ImageBarComponent,
     ImageAnnotationsComponent,
     ImageViewerComponent,
+    ObjectPermissionDialogComponent,
     StandardDialogComponent,
     PanelDialogComponent,
     SplitSectionComponent,
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
new file mode 100644
index 0000000..d81393d
--- /dev/null
+++ 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html
@@ -0,0 +1,83 @@
+<!--
+  ~ 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.
+  ~
+  -->
+
+<div class="sp-dialog-container">
+    <div class="sp-dialog-content" *ngIf="usersLoaded">
+        <div fxFlex="100" fxLayout="column" class="p-15">
+            <h4>{{headerTitle}}</h4>
+            <form [formGroup]="parentForm" fxFlex="100" fxLayout="column">
+                <div class="general-options-panel" fxLayout="column">
+                    <span class="general-options-header">Basics</span>
+                    <mat-form-field color="accent">
+                        <mat-label>Owner</mat-label>
+                        <input formControlName="owner" fxFlex
+                               matInput
+                               required>
+                    </mat-form-field>
+                    <mat-checkbox formControlName="publicElement">
+                        Public Element
+                    </mat-checkbox>
+                </div>
+                <div fxLayout="column" class="general-options-panel">
+                    <span class="general-options-header">Users</span>
+                    <mat-form-field class="example-chip-list" 
appearance="fill">
+                        <mat-label>Authorized Users</mat-label>
+                        <mat-chip-list #chipList aria-label="User selection">
+                            <mat-chip
+                                    *ngFor="let user of grantedUserAuthorities"
+                                    selectable="true"
+                                    removable="true"
+                                    (removed)="removeUser(user)">
+                                {{user.username}}
+                                <button matChipRemove>
+                                    <mat-icon>cancel</mat-icon>
+                                </button>
+                            </mat-chip>
+                            <input
+                                    placeholder="User Selection"
+                                    #userInput
+                                    [formControl]="userCtrl"
+                                    [matAutocomplete]="auto"
+                                    [matChipInputFor]="chipList"
+                                    
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
+                                    (matChipInputTokenEnd)="addUser($event)">
+                        </mat-chip-list>
+                        <mat-autocomplete #auto="matAutocomplete" 
(optionSelected)="userSelected($event)">
+                            <mat-option *ngFor="let user of filteredUsers | 
async" [value]="user">
+                                {{user.username}}
+                            </mat-option>
+                        </mat-autocomplete>
+                    </mat-form-field>
+                </div>
+            </form>
+        </div>
+    </div>
+    <mat-divider></mat-divider>
+    <div class="sp-dialog-actions">
+        <div fxLayout="row">
+            <button mat-button mat-raised-button color="accent" 
(click)="save()" style="margin-right:10px;"
+                    [disabled]="!parentForm.valid"
+                    data-cy="sp-element-edit-user-save">
+                <i class="material-icons">save</i><span>&nbsp;Save</span>
+            </button>
+            <button mat-button mat-raised-button class="mat-basic" 
(click)="close(false)">
+                Cancel
+            </button>
+        </div>
+    </div>
+</div>
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
similarity index 83%
copy from 
streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
copy to 
ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
index 3c8cfd7..6629d35 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/PermissionResource.java
+++ 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.scss
@@ -15,12 +15,14 @@
  * limitations under the License.
  *
  */
-package org.apache.streampipes.rest.impl.admin;
 
-import javax.ws.rs.Path;
+@import '../../../scss/sp/sp-dialog.scss';
 
-@Path("/v2/admin/permissions")
-public class PermissionResource {
+.form-field .mat-form-field-wrapper {
+  margin-bottom: -1.25em;
+}
 
 
+.form-field .mat-form-field-infix {
+  border-top: 0;
 }
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
new file mode 100644
index 0000000..a9c8961
--- /dev/null
+++ 
b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.ts
@@ -0,0 +1,165 @@
+/*
+ * 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 { COMMA, ENTER } from '@angular/cdk/keycodes';
+import { Component, ElementRef, Input, OnInit, ViewChild } from 
'@angular/core';
+import { DialogRef } from '../dialog/base-dialog/dialog-ref';
+import { FormBuilder, FormControl, FormGroup, Validators } from 
'@angular/forms';
+import { PermissionsService } from 
'../../platform-services/apis/permissions.service';
+import {
+  Group,
+  Permission,
+  PermissionEntry,
+  ServiceAccount,
+  UserAccount
+} from '../../core-model/gen/streampipes-model-client';
+import { UserService } from '../../platform-services/apis/user.service';
+import { MatChipInputEvent } from '@angular/material/chips';
+import { Observable, zip } from 'rxjs';
+import { UserGroupService } from 
'../../platform-services/apis/user-group.service';
+import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
+import { map, startWith } from 'rxjs/operators';
+
+@Component({
+  selector: 'sp-object-permission-dialog',
+  templateUrl: './object-permission-dialog.component.html',
+  styleUrls: ['./object-permission-dialog.component.scss'],
+})
+export class ObjectPermissionDialogComponent implements OnInit {
+
+  separatorKeysCodes: number[] = [ENTER, COMMA];
+
+  @Input()
+  objectInstanceId: string;
+
+  @Input()
+  headerTitle: string;
+
+  parentForm: FormGroup;
+
+  permission: Permission;
+
+  owner: UserAccount | ServiceAccount;
+  grantedUserAuthorities: (UserAccount | ServiceAccount)[];
+  grantedGroupAuthorities: Group[];
+
+  allUsers: (UserAccount | ServiceAccount)[];
+  allGroups: Group[];
+
+  filteredUsers: Observable<(UserAccount | ServiceAccount)[]>;
+
+  usersLoaded = false;
+
+  @ViewChild('userInput') fruitInput: ElementRef<HTMLInputElement>;
+  userCtrl = new FormControl();
+
+
+  constructor(
+      private fb: FormBuilder,
+      private dialogRef: DialogRef<ObjectPermissionDialogComponent>,
+      private permissionsService: PermissionsService,
+      private userService: UserService,
+      private groupService: UserGroupService
+  ) {
+    this.grantedGroupAuthorities = [];
+    this.grantedUserAuthorities = [];
+  }
+
+  ngOnInit(): void {
+    this.loadUsersAndGroups();
+    this.parentForm = this.fb.group({});
+
+    this.parentForm.valueChanges.subscribe(v => {
+      this.permission.publicElement = v.publicElement;
+      //this.permission.ownerSid = v.owner;
+    });
+  }
+
+  loadUsersAndGroups() {
+    zip(this.userService.getAllUserAccounts(),
+        this.userService.getAllServiceAccounts(),
+        this.groupService.getAllUserGroups(),
+        this.permissionsService.getPermissionsForObject(this.objectInstanceId))
+        .subscribe(results => {
+          this.allUsers = results[0];
+          this.allUsers.concat(results[1]);
+          this.allGroups = results[2];
+          this.processPermissions(results[3]);
+          this.usersLoaded = true;
+          console.log(this.allUsers);
+        });
+  }
+
+  processPermissions(permissions: Permission[]) {
+    if (permissions.length > 0) {
+      this.permission = permissions[0];
+      this.parentForm.addControl('publicElement', new 
FormControl(this.permission.publicElement, Validators.required));
+      this.parentForm.addControl('owner', new 
FormControl(this.permission.ownerSid, Validators.required));
+      this.parentForm.addControl('userSelection', new 
FormControl(this.permission.ownerSid));
+      this.filteredUsers = this.userCtrl.valueChanges.pipe(
+          startWith(null),
+          map((fruit: UserAccount | ServiceAccount | null) => fruit ? 
this._filter(fruit) : this.allUsers.slice()));
+
+      this.permission.grantedAuthorities.forEach(authority => {
+        if (authority.principalType === 'GROUP') {
+          this.addGroupToSelection(authority);
+        } else {
+          this.addUserToSelection(authority);
+        }
+      });
+
+    } else {
+      console.log('No permission entry found for item');
+    }
+  }
+
+  save() {
+
+  }
+
+  close(refresh: boolean) {
+    this.dialogRef.close(refresh);
+  }
+
+  removeUser(user: UserAccount | ServiceAccount) {
+    const currentIndex = this.grantedUserAuthorities.findIndex(u => 
u.principalId === user.principalId);
+    this.grantedUserAuthorities.splice(currentIndex, 1);
+  }
+
+  addUser(event: MatChipInputEvent) {
+    event.chipInput.clear();
+  }
+
+  userSelected(event: MatAutocompleteSelectedEvent) {
+    this.grantedUserAuthorities.push(event.option.value);
+  }
+
+  private addUserToSelection(authority: PermissionEntry) {
+    const user = this.allUsers.find(u => u.principalId === authority.sid);
+    this.grantedUserAuthorities.push(user);
+  }
+
+  private addGroupToSelection(authority: PermissionEntry) {
+
+  }
+
+  private _filter(value: any): (UserAccount | ServiceAccount)[] {
+    const filterValue = value.toLowerCase();
+    return this.allUsers.filter(u => 
u.username.toLowerCase().startsWith(filterValue));
+  }
+}
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 d243744..429e5a8 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
@@ -22,18 +22,19 @@
             <th mat-header-cell mat-sort-header *matHeaderCellDef> Status</th>
             <td mat-cell *matCellDef="let pipeline">
                 <div fxLayout="row" fxLayoutAlign="start center">
-                <div *ngIf="pipeline.running && pipeline.healthStatus == 'OK'" 
class="light light-green"></div>
-                <div *ngIf="pipeline.running && pipeline.healthStatus == 
'REQUIRES_ATTENTION'" class="light light-yellow"></div>
-                <div *ngIf="pipeline.running && pipeline.healthStatus == 
'FAILURE'" class="light light-red"></div>
-                <div *ngIf="!pipeline.running" class="light light-neutral" 
[matTooltip]="'Stopped'"></div>
-                <button class="ml-10"
-                        mat-button
-                        mat-icon-button
-                        (click)="openPipelineNotificationsDialog(pipeline)"
-                        *ngIf="pipeline.pipelineNotifications.length > 0"
-                        matTooltip="{{pipeline.pipelineNotifications.length}} 
notification{{pipeline.pipelineNotifications.length > 1 ? 's' : ''}}">
-                    <mat-icon color="warn">warning</mat-icon>
-                </button>
+                    <div *ngIf="pipeline.running && pipeline.healthStatus == 
'OK'" class="light light-green"></div>
+                    <div *ngIf="pipeline.running && pipeline.healthStatus == 
'REQUIRES_ATTENTION'"
+                         class="light light-yellow"></div>
+                    <div *ngIf="pipeline.running && pipeline.healthStatus == 
'FAILURE'" class="light light-red"></div>
+                    <div *ngIf="!pipeline.running" class="light light-neutral" 
[matTooltip]="'Stopped'"></div>
+                    <button class="ml-10"
+                            mat-button
+                            mat-icon-button
+                            (click)="openPipelineNotificationsDialog(pipeline)"
+                            *ngIf="pipeline.pipelineNotifications.length > 0"
+                            
matTooltip="{{pipeline.pipelineNotifications.length}} 
notification{{pipeline.pipelineNotifications.length > 1 ? 's' : ''}}">
+                        <mat-icon color="warn">warning</mat-icon>
+                    </button>
                 </div>
             </td>
         </ng-container>
@@ -73,25 +74,36 @@
             <td mat-cell *matCellDef="let pipeline">
                 <div fxLayout="row">
                 <span fxFlex fxFlexOrder="1" fxLayout="row" 
fxLayoutAlign="center center">
-                    <button  color="accent" mat-button mat-icon-button 
matTooltip="Show pipeline" matTooltipPosition="above"
+                    <button color="accent" mat-button mat-icon-button 
matTooltip="Show pipeline"
+                            matTooltipPosition="above"
                             
(click)="pipelineOperationsService.showPipelineDetails(pipeline._id)"><i
                             class="material-icons">search</i>
                     </button>
                 </span>
                     <span fxFlex fxFlexOrder="2" fxLayout="row" 
fxLayoutAlign="center center">
-                    <button color="accent" mat-button mat-icon-button 
matTooltip="Modify pipeline" matTooltipPosition="above"
+                    <button color="accent" mat-button mat-icon-button 
matTooltip="Modify pipeline"
+                            matTooltipPosition="above"
                             [disabled]="pipeline.running"
                             
(click)="pipelineOperationsService.modifyPipeline(pipeline._id)">
                         <i class="material-icons">mode_edit</i>
                     </button>
                 </span>
                     <span fxFlex fxFlexOrder="3" fxLayout="row" 
fxLayoutAlign="center center">
-                    <button color="accent" mat-button mat-icon-button 
matTooltip="Delete pipeline" matTooltipPosition="above"
+                    <button color="accent" mat-button mat-icon-button 
matTooltip="Delete pipeline"
+                            matTooltipPosition="above"
                             
(click)="pipelineOperationsService.showDeleteDialog(pipeline, 
refreshPipelinesEmitter)"
-                            data-cy="delete" >
+                            data-cy="delete">
                         <i class="material-icons">delete</i>
                     </button>
                 </span>
+                    <span fxFlex fxFlexOrder="4" fxLayout="row" 
fxLayoutAlign="center center" *ngIf="isAdmin">
+                    <button color="accent" mat-button mat-icon-button 
matTooltip="Permissions"
+                            matTooltipPosition="above"
+                            
(click)="pipelineOperationsService.showPermissionsDialog(pipeline, 
refreshPipelinesEmitter)"
+                            data-cy="delete">
+                        <i class="material-icons">share</i>
+                    </button>
+                </span>
                 </div>
             </td>
         </ng-container>
diff --git 
a/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.ts
 
b/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.ts
index a63da8f..bcaeb24 100644
--- 
a/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.ts
+++ 
b/ui/src/app/pipelines/components/pipeline-overview/pipeline-overview.component.ts
@@ -22,6 +22,8 @@ import { PipelineOperationsService } from 
'../../services/pipeline-operations.se
 import { MatTableDataSource } from '@angular/material/table';
 import { MatPaginator } from '@angular/material/paginator';
 import { MatSort } from '@angular/material/sort';
+import { AuthService } from '../../../services/auth.service';
+import { UserRole } from '../../../_enums/user-role.enum';
 
 
 @Component({
@@ -54,12 +56,18 @@ export class PipelineOverviewComponent implements OnInit {
   starting: any;
   stopping: any;
 
-  constructor(public pipelineOperationsService: PipelineOperationsService) {
+  isAdmin = false;
+
+  constructor(public pipelineOperationsService: PipelineOperationsService,
+              private authService: AuthService) {
     this.starting = false;
     this.stopping = false;
   }
 
   ngOnInit() {
+    this.authService.user$.subscribe(user => {
+      this.isAdmin = user.roles.indexOf(UserRole.ROLE_ADMIN) > -1;
+    });
     this.toggleRunningOperation = this.toggleRunningOperation.bind(this);
 
     if (this.pipelineToStart) {
diff --git a/ui/src/app/pipelines/services/pipeline-operations.service.ts 
b/ui/src/app/pipelines/services/pipeline-operations.service.ts
index 5fce7f3..41da3f8 100644
--- a/ui/src/app/pipelines/services/pipeline-operations.service.ts
+++ b/ui/src/app/pipelines/services/pipeline-operations.service.ts
@@ -16,18 +16,19 @@
  *
  */
 
-import {ShepherdService} from "../../services/tour/shepherd.service";
-import {EventEmitter, Injectable} from "@angular/core";
-import {PipelineService} from "../../platform-services/apis/pipeline.service";
-import {PanelType} from "../../core-ui/dialog/base-dialog/base-dialog.model";
-import {DialogService} from 
"../../core-ui/dialog/base-dialog/base-dialog.service";
-import {PipelineStatusDialogComponent} from 
"../dialog/pipeline-status/pipeline-status-dialog.component";
-import {Pipeline} from "../../core-model/gen/streampipes-model";
-import {DeletePipelineDialogComponent} from 
"../dialog/delete-pipeline/delete-pipeline-dialog.component";
-import {DialogRef} from "../../core-ui/dialog/base-dialog/dialog-ref";
-import {Router} from "@angular/router";
-import {PipelineAction} from "../model/pipeline-model";
-import {PipelineNotificationsComponent} from 
"../dialog/pipeline-notifications/pipeline-notifications.component";
+import { ShepherdService } from '../../services/tour/shepherd.service';
+import { EventEmitter, Injectable } from '@angular/core';
+import { PipelineService } from 
'../../platform-services/apis/pipeline.service';
+import { PanelType } from '../../core-ui/dialog/base-dialog/base-dialog.model';
+import { DialogService } from 
'../../core-ui/dialog/base-dialog/base-dialog.service';
+import { PipelineStatusDialogComponent } from 
'../dialog/pipeline-status/pipeline-status-dialog.component';
+import { Pipeline } from '../../core-model/gen/streampipes-model';
+import { DeletePipelineDialogComponent } from 
'../dialog/delete-pipeline/delete-pipeline-dialog.component';
+import { DialogRef } from '../../core-ui/dialog/base-dialog/dialog-ref';
+import { Router } from '@angular/router';
+import { PipelineAction } from '../model/pipeline-model';
+import { PipelineNotificationsComponent } from 
'../dialog/pipeline-notifications/pipeline-notifications.component';
+import { ObjectPermissionDialogComponent } from 
'../../core-ui/object-permission-dialog/object-permission-dialog.component';
 
 @Injectable()
 export class PipelineOperationsService {
@@ -36,20 +37,20 @@ export class PipelineOperationsService {
   stopping: any;
 
   constructor(
-      private ShepherdService: ShepherdService,
-      private PipelineService: PipelineService,
-      private DialogService: DialogService,
-      private Router: Router) {
+      private shepherdService: ShepherdService,
+      private pipelineService: PipelineService,
+      private dialogService: DialogService,
+      private router: Router) {
   }
 
   startPipeline(pipelineId: string,
                 refreshPipelinesEmitter: EventEmitter<boolean>,
-                toggleRunningOperation?,) {
+                toggleRunningOperation?, ) {
     if (toggleRunningOperation) {
       toggleRunningOperation('starting');
     }
-    let dialogRef = this.showPipelineOperationsDialog(pipelineId, 
PipelineAction.Start);
-    this.afterPipelineOperationsDialogClosed(dialogRef, 
refreshPipelinesEmitter, "starting", toggleRunningOperation);
+    const dialogRef = this.showPipelineOperationsDialog(pipelineId, 
PipelineAction.Start);
+    this.afterPipelineOperationsDialogClosed(dialogRef, 
refreshPipelinesEmitter, 'starting', toggleRunningOperation);
   }
 
   stopPipeline(pipelineId: string,
@@ -58,8 +59,8 @@ export class PipelineOperationsService {
     if (toggleRunningOperation) {
       toggleRunningOperation('stopping');
     }
-    let dialogRef = this.showPipelineOperationsDialog(pipelineId, 
PipelineAction.Stop);
-    this.afterPipelineOperationsDialogClosed(dialogRef, 
refreshPipelinesEmitter, "stopping", toggleRunningOperation);
+    const dialogRef = this.showPipelineOperationsDialog(pipelineId, 
PipelineAction.Stop);
+    this.afterPipelineOperationsDialogClosed(dialogRef, 
refreshPipelinesEmitter, 'stopping', toggleRunningOperation);
   }
 
   afterPipelineOperationsDialogClosed(dialogRef: 
DialogRef<PipelineStatusDialogComponent>,
@@ -77,12 +78,12 @@ export class PipelineOperationsService {
   showDeleteDialog(pipeline: Pipeline,
                    refreshPipelinesEmitter: EventEmitter<boolean>,
                    switchToPipelineView?: any) {
-    let dialogRef: DialogRef<DeletePipelineDialogComponent> = 
this.DialogService.open(DeletePipelineDialogComponent, {
+    const dialogRef: DialogRef<DeletePipelineDialogComponent> = 
this.dialogService.open(DeletePipelineDialogComponent, {
       panelType: PanelType.STANDARD_PANEL,
-      title: "Delete Pipeline",
-      width: "70vw",
+      title: 'Delete Pipeline',
+      width: '70vw',
       data: {
-        "pipeline": pipeline,
+        'pipeline': pipeline,
       }
     });
 
@@ -94,30 +95,30 @@ export class PipelineOperationsService {
           switchToPipelineView();
         }
       }
-    })
-  };
+    });
+  }
 
   showPipelineOperationsDialog(pipelineId: string,
                                action: PipelineAction): 
DialogRef<PipelineStatusDialogComponent> {
-    return this.DialogService.open(PipelineStatusDialogComponent, {
+    return this.dialogService.open(PipelineStatusDialogComponent, {
       panelType: PanelType.STANDARD_PANEL,
-      title: "Pipeline Status",
-      width: "70vw",
+      title: 'Pipeline Status',
+      width: '70vw',
       data: {
-        "pipelineId": pipelineId,
-        "action": action
+        'pipelineId': pipelineId,
+        'action': action
       }
     });
-  };
+  }
 
   showPipelineNotificationsDialog(pipeline: Pipeline,
                                   refreshPipelinesEmitter: 
EventEmitter<boolean>) {
-    let dialogRef: DialogRef<PipelineNotificationsComponent> = 
this.DialogService.open(PipelineNotificationsComponent, {
+    const dialogRef: DialogRef<PipelineNotificationsComponent> = 
this.dialogService.open(PipelineNotificationsComponent, {
       panelType: PanelType.STANDARD_PANEL,
-      title: "Pipeline Notifications",
-      width: "70vw",
+      title: 'Pipeline Notifications',
+      width: '70vw',
       data: {
-        "pipeline": pipeline,
+        'pipeline': pipeline,
       }
     });
 
@@ -126,12 +127,29 @@ export class PipelineOperationsService {
     });
   }
 
+  showPermissionsDialog(pipeline: Pipeline,
+                        refreshPipelinesEmitter: EventEmitter<boolean>) {
+    const dialogRef = this.dialogService.open(ObjectPermissionDialogComponent, 
{
+      panelType: PanelType.SLIDE_IN_PANEL,
+      title: 'Manage permissions',
+      width: '70vw',
+      data: {
+        'objectInstanceId': pipeline._id,
+        'headerTitle': 'Manage permissions for pipeline ' + pipeline.name
+      }
+    });
+
+    dialogRef.afterClosed().subscribe(refresh => {
+      refreshPipelinesEmitter.emit(refresh);
+    });
+  }
+
   showPipelineInEditor(id) {
-    this.Router.navigate(["editor"], { queryParams: { pipeline: id }});
+    this.router.navigate(['editor'], { queryParams: { pipeline: id }});
   }
 
   showPipelineDetails(id) {
-    this.Router.navigate(["pipeline-details"], { queryParams: { pipeline: id 
}});
+    this.router.navigate(['pipeline-details'], { queryParams: { pipeline: id 
}});
   }
 
   modifyPipeline(pipeline) {
@@ -139,6 +157,6 @@ export class PipelineOperationsService {
   }
 
   showLogs(id) {
-    //this.$state.go("streampipes.pipelinelogs", {pipeline: id});
+    // this.$state.go("streampipes.pipelinelogs", {pipeline: id});
   }
 }
diff --git a/ui/src/app/platform-services/apis/permissions.service.ts 
b/ui/src/app/platform-services/apis/permissions.service.ts
new file mode 100644
index 0000000..4c87050
--- /dev/null
+++ b/ui/src/app/platform-services/apis/permissions.service.ts
@@ -0,0 +1,48 @@
+/*
+ * 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 { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { PlatformServicesCommons } from './commons.service';
+import { Observable } from 'rxjs';
+import {
+  Message,
+  Pipeline,
+  PipelineCategory,
+  PipelineOperationStatus,
+  PipelineStatusMessage
+} from '../../core-model/gen/streampipes-model';
+import { map } from 'rxjs/operators';
+import { Permission } from '../../core-model/gen/streampipes-model-client';
+
+@Injectable()
+export class PermissionsService {
+
+  constructor(private http: HttpClient,
+              private platformServicesCommons: PlatformServicesCommons) { }
+
+
+  public getPermissionsForObject(objectInstanceId: string): 
Observable<Permission[]> {
+      return 
this.http.get(`${this.permissionsBasePath}/${objectInstanceId}`).pipe(map(response
 => response as Permission[]));
+  }
+
+
+  get permissionsBasePath() {
+    return `${this.platformServicesCommons.apiBasePath}/admin/permissions`;
+  }
+}
diff --git a/ui/src/app/platform-services/apis/user.service.ts 
b/ui/src/app/platform-services/apis/user.service.ts
index 56e9979..1eabf19 100644
--- a/ui/src/app/platform-services/apis/user.service.ts
+++ b/ui/src/app/platform-services/apis/user.service.ts
@@ -21,7 +21,11 @@ import { HttpClient } from '@angular/common/http';
 import { map } from 'rxjs/operators';
 import { Observable } from 'rxjs';
 import { PlatformServicesCommons } from './commons.service';
-import { ServiceAccount, UserAccount } from 
'../../core-model/gen/streampipes-model-client';
+import {
+  Principal,
+  ServiceAccount,
+  UserAccount
+} from '../../core-model/gen/streampipes-model-client';
 
 @Injectable()
 export class UserService {
@@ -64,6 +68,17 @@ export class UserService {
     return this.http.delete(`${this.usersPath}/${principalId}`);
   }
 
+  public getUserById(principalId: string): Observable<UserAccount | 
ServiceAccount> {
+    return this.http.get(`${this.usersPath}/${principalId}`)
+        .pipe(map(response => {
+          if ((response as any).principalType === 'USER_ACCOUNT') {
+            return UserAccount.fromData(response as any);
+          } else {
+            return ServiceAccount.fromData(response as any);
+          }
+        }));
+  }
+
   private get usersPath() {
     return this.platformServicesCommons.apiBasePath + '/users';
   }
diff --git a/ui/src/app/platform-services/platform.module.ts 
b/ui/src/app/platform-services/platform.module.ts
index cb55ffb..59c23b2 100644
--- a/ui/src/app/platform-services/platform.module.ts
+++ b/ui/src/app/platform-services/platform.module.ts
@@ -31,15 +31,16 @@ import { PipelineTemplateService } from 
'./apis/pipeline-template.service';
 import { UserService } from './apis/user.service';
 import { UserGroupService } from './apis/user-group.service';
 import { MailConfigService } from './apis/mail-config.service';
+import { PermissionsService } from './apis/permissions.service';
 
 @NgModule({
-  imports: [
-  ],
+  imports: [],
   declarations: [],
   providers: [
     FilesService,
     MailConfigService,
     MeasurementUnitsService,
+    PermissionsService,
     PlatformServicesCommons,
     PipelineCanvasMetadataService,
     PipelineElementEndpointService,
diff --git a/ui/src/app/services/auth.service.ts 
b/ui/src/app/services/auth.service.ts
index 42fe5e3..dbc116f 100644
--- a/ui/src/app/services/auth.service.ts
+++ b/ui/src/app/services/auth.service.ts
@@ -32,7 +32,7 @@ import { RoleModel } from '../_models/auth.model';
 export class AuthService {
 
     public authToken$: BehaviorSubject<string> = new 
BehaviorSubject<string>(undefined);
-    public user$: BehaviorSubject<any> = new BehaviorSubject<any>(undefined);
+    public user$: BehaviorSubject<UserInfo> = new 
BehaviorSubject<UserInfo>(undefined);
     public isLoggedIn$: BehaviorSubject<boolean> = new 
BehaviorSubject<boolean>(false);
     public darkMode$: BehaviorSubject<boolean> = new 
BehaviorSubject<boolean>(false);
 
@@ -56,7 +56,6 @@ export class AuthService {
         const jwtHelper: JwtHelperService = new JwtHelperService({});
         const decodedToken = jwtHelper.decodeToken(data.accessToken);
         this.tokenStorage.saveToken(data.accessToken);
-        console.log(decodedToken.user);
         this.tokenStorage.saveUser(decodedToken.user);
         this.authToken$.next(data.accessToken);
         this.user$.next(decodedToken.user);

Reply via email to