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

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


The following commit(s) were added to refs/heads/dev by this push:
     new afc72791d7 feat(#3944): Improve permission handling (#3946)
afc72791d7 is described below

commit afc72791d729687a0f429ca5845af491ae4ba6e5
Author: Philipp Zehnder <[email protected]>
AuthorDate: Thu Nov 20 16:43:41 2025 +0100

    feat(#3944): Improve permission handling (#3946)
---
 .../manager/pipeline/PipelineManager.java          |  68 ++++++---
 .../streampipes/rest/impl/ResetResource.java       |   9 ++
 ui/cypress/support/utils/UserUtils.ts              |  25 +++-
 ui/cypress/support/utils/connect/ConnectUtils.ts   |   1 -
 ui/cypress/support/utils/pipeline/PipelineBtns.ts  |   4 +
 ui/cypress/support/utils/pipeline/PipelineUtils.ts |  13 +-
 ui/cypress/support/utils/user/PermissionUtils.ts   |  31 ++++
 ui/cypress/support/utils/user/UserBtns.ts          |  16 +-
 ...t.spec.ts => testAddAssetOnResourceCreation.ts} |   2 +-
 .../userManagement/testGroupManagement.spec.ts     |  13 +-
 .../userManagement/testUserRoleConnect.spec.ts     | 136 +++++++++++++----
 .../userManagement/testUserRolePipeline.spec.ts    | 166 +++++++++++++++------
 .../object-permission-dialog.component.html        |  20 ++-
 .../edit-user-dialog.component.html                |   1 +
 .../security-user-config.component.html            |   4 +-
 .../pipeline-element-discovery.component.html      |   8 +-
 16 files changed, 390 insertions(+), 127 deletions(-)

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 d2114f47a9..4bf64286fb 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
@@ -26,6 +26,7 @@ import 
org.apache.streampipes.model.base.NamedStreamPipesEntity;
 import org.apache.streampipes.model.client.user.Permission;
 import org.apache.streampipes.model.pipeline.Pipeline;
 import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
+import org.apache.streampipes.resource.management.CrudResourceManager;
 import org.apache.streampipes.resource.management.NotificationsResourceManager;
 import org.apache.streampipes.storage.api.IPermissionStorage;
 import org.apache.streampipes.storage.api.IPipelineStorage;
@@ -46,7 +47,9 @@ public class PipelineManager {
    * @return all pipelines
    */
   public static List<Pipeline> getAllPipelines() {
-    return 
StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineStorageAPI().findAll();
+    return StorageDispatcher.INSTANCE.getNoSqlStore()
+                                     .getPipelineStorageAPI()
+                                     .findAll();
   }
 
   /**
@@ -66,12 +69,14 @@ public class PipelineManager {
    * @param pipeline     to be added
    * @return pipelineId of the stored pipeline
    */
-  public static String addPipeline(String principalSid,
-      Pipeline pipeline) {
+  public static String addPipeline(
+      String principalSid,
+      Pipeline pipeline
+  ) {
 
     String pipelineId = Objects.isNull(pipeline.getPipelineId())
-            ? UUIDGenerator.generateUuid()
-            : pipeline.getPipelineId();
+        ? UUIDGenerator.generateUuid()
+        : pipeline.getPipelineId();
     preparePipelineBasics(principalSid, pipeline, pipelineId);
     new PipelineStorageService(pipeline).addPipeline();
 
@@ -101,8 +106,10 @@ public class PipelineManager {
    *                   containers could be reached
    * @return pipeline status of the start operation
    */
-  public static PipelineOperationStatus stopPipeline(String pipelineId,
-      boolean forceStop) {
+  public static PipelineOperationStatus stopPipeline(
+      String pipelineId,
+      boolean forceStop
+  ) {
     Pipeline pipeline = getPipeline(pipelineId);
 
     return new PipelineExecutor(pipeline).stopPipeline(forceStop);
@@ -114,16 +121,20 @@ public class PipelineManager {
    * @param pipelineId of pipeline to be deleted
    */
   public static void deletePipeline(String pipelineId) {
+    var pipelineCrudResourceManager = new 
CrudResourceManager<>(getPipelineStorage(), Pipeline.class);
+
     var pipeline = getPipeline(pipelineId);
     if (Objects.nonNull(pipeline)) {
-      getPipelineStorage().deleteElementById(pipelineId);
+      pipelineCrudResourceManager.delete(pipelineId);
       new 
NotificationsResourceManager().deleteNotificationsForPipeline(pipeline);
     }
   }
 
   public static List<PipelineOperationStatus> stopAllPipelines(boolean 
forceStop) {
     List<PipelineOperationStatus> status = new ArrayList<>();
-    List<Pipeline> pipelines = 
StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineStorageAPI().findAll();
+    List<Pipeline> pipelines = StorageDispatcher.INSTANCE.getNoSqlStore()
+                                                         
.getPipelineStorageAPI()
+                                                         .findAll();
 
     pipelines.forEach(p -> {
       if (p.isRunning()) {
@@ -140,36 +151,49 @@ public class PipelineManager {
    * @return all pipelines containing the element
    */
   public static List<Pipeline> getPipelinesContainingElements(String 
elementId) {
-    return PipelineManager.getAllPipelines().stream()
-        .filter(pipeline -> mergePipelineElement(pipeline)
-            .anyMatch(el -> el.getElementId().equals(elementId)))
-        .collect(Collectors.toList());
+    return PipelineManager.getAllPipelines()
+                          .stream()
+                          .filter(pipeline -> mergePipelineElement(pipeline)
+                              .anyMatch(el -> el.getElementId()
+                                                .equals(elementId)))
+                          .collect(Collectors.toList());
   }
 
   private static Stream<? extends NamedStreamPipesEntity> 
mergePipelineElement(Pipeline pipeline) {
     return Stream.concat(
         Stream.concat(
-            pipeline.getStreams().stream(),
-            pipeline.getSepas().stream()),
-        pipeline.getActions().stream());
+            pipeline.getStreams()
+                    .stream(),
+            pipeline.getSepas()
+                    .stream()
+        ),
+        pipeline.getActions()
+                .stream()
+    );
   }
 
-  private static void preparePipelineBasics(String username,
+  private static void preparePipelineBasics(
+      String username,
       Pipeline pipeline,
-      String pipelineId) {
+      String pipelineId
+  ) {
     pipeline.setPipelineId(pipelineId);
     pipeline.setRunning(false);
     pipeline.setCreatedByUser(username);
     pipeline.setCreatedAt(new Date().getTime());
-    pipeline.getSepas().forEach(processor -> 
processor.setCorrespondingUser(username));
-    pipeline.getActions().forEach(action -> 
action.setCorrespondingUser(username));
+    pipeline.getSepas()
+            .forEach(processor -> processor.setCorrespondingUser(username));
+    pipeline.getActions()
+            .forEach(action -> action.setCorrespondingUser(username));
   }
 
   private static IPipelineStorage getPipelineStorage() {
-    return StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineStorageAPI();
+    return StorageDispatcher.INSTANCE.getNoSqlStore()
+                                     .getPipelineStorageAPI();
   }
 
   private static IPermissionStorage getPermissionStorage() {
-    return StorageDispatcher.INSTANCE.getNoSqlStore().getPermissionStorage();
+    return StorageDispatcher.INSTANCE.getNoSqlStore()
+                                     .getPermissionStorage();
   }
 }
\ No newline at end of file
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
index 4596dbb7b3..bf89dc7778 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/ResetResource.java
@@ -43,6 +43,8 @@ public class ResetResource extends 
AbstractAuthGuardedRestResource {
   public ResponseEntity<SuccessMessage> reset() {
     ResetManagement.reset(getAuthenticatedUsername());
     var userStorage = getUserStorage();
+
+
     // Delete all users other than current user (admin) and their resources
     var allUsers = new ArrayList<Principal>(userStorage.getAllUsers());
     for (var user : allUsers) {
@@ -52,6 +54,13 @@ public class ResetResource extends 
AbstractAuthGuardedRestResource {
         userStorage.deleteUser(user.getPrincipalId());
       }
     }
+
+    // Delete all user Groups
+    var allUserGroups = getNoSqlStorage().getUserGroupStorage().findAll();
+    for (var group : allUserGroups) {
+      
getNoSqlStorage().getUserGroupStorage().deleteElementById(group.getElementId());
+    }
+
     var message = Notifications.success("Reset of system successfully 
performed");
     return ok(message);
   }
diff --git a/ui/cypress/support/utils/UserUtils.ts 
b/ui/cypress/support/utils/UserUtils.ts
index e5557d0ad1..249a2e6322 100644
--- a/ui/cypress/support/utils/UserUtils.ts
+++ b/ui/cypress/support/utils/UserUtils.ts
@@ -20,6 +20,7 @@ import { User } from '../model/User';
 import { UserBuilder } from '../builder/UserBuilder';
 import { UserRole } from '../../../src/app/_enums/user-role.enum';
 import { UserBtns } from './user/UserBtns';
+import { ConfigurationBtns } from './configuration/ConfigurationBtns';
 
 export class UserUtils {
     public static adminUser = 
UserBuilder.create('[email protected]')
@@ -76,7 +77,7 @@ export class UserUtils {
             0,
         );
 
-        UserBtns.editUserBtn(user.email);
+        UserBtns.editUserBtn(user.name);
 
         UserBtns.userRoleCheckbox(role).click();
 
@@ -117,4 +118,26 @@ export class UserUtils {
         UserBtns.deleteUserBtn(user.name).click();
         UserBtns.confirmDeleteBtn().click();
     }
+
+    public static createGroup(name: string, ...roles: UserRole[]) {
+        this.goToUserConfiguration();
+
+        ConfigurationBtns.newUserGroupBtn().click();
+        ConfigurationBtns.inputGroupName(name);
+        roles.forEach(role => {
+            cy.get(`input[value="${role}"]`).check();
+        });
+        UserBtns.saveEditUserBtn().click();
+    }
+
+    public static addGroupToUser(groupName: string, name: string) {
+        this.goToUserConfiguration();
+        UserBtns.editUserBtn(name);
+
+        cy.dataCy('group-' + groupName)
+            .children()
+            .click();
+
+        UserBtns.saveEditUserBtn().click();
+    }
 }
diff --git a/ui/cypress/support/utils/connect/ConnectUtils.ts 
b/ui/cypress/support/utils/connect/ConnectUtils.ts
index 4c2f556905..fced3e0d72 100644
--- a/ui/cypress/support/utils/connect/ConnectUtils.ts
+++ b/ui/cypress/support/utils/connect/ConnectUtils.ts
@@ -279,7 +279,6 @@ export class ConnectUtils {
     }
 
     public static editAsset(assetNameList = []) {
-        //cy.dataCy('show-asset-checkbox').click();
         cy.get('mat-tree.asset-tree', { timeout: 10000 }).should('exist');
 
         assetNameList.forEach(assetName => {
diff --git a/ui/cypress/support/utils/pipeline/PipelineBtns.ts 
b/ui/cypress/support/utils/pipeline/PipelineBtns.ts
index 41f2f923f6..1ba9c36db7 100644
--- a/ui/cypress/support/utils/pipeline/PipelineBtns.ts
+++ b/ui/cypress/support/utils/pipeline/PipelineBtns.ts
@@ -36,6 +36,10 @@ export class PipelineBtns {
         return cy.dataCy('pipelines-navigate-to-editor');
     }
 
+    public static spPipelineElementSelection() {
+        return cy.dataCy('sp-pipeline-element-selection', { timeout: 10000 });
+    }
+
     public static editorAddPipelineElement() {
         return cy.dataCy('sp-editor-add-pipeline-element', { timeout: 10000 });
     }
diff --git a/ui/cypress/support/utils/pipeline/PipelineUtils.ts 
b/ui/cypress/support/utils/pipeline/PipelineUtils.ts
index 62d76cd996..7d90bde728 100644
--- a/ui/cypress/support/utils/pipeline/PipelineUtils.ts
+++ b/ui/cypress/support/utils/pipeline/PipelineUtils.ts
@@ -94,11 +94,16 @@ export class PipelineUtils {
         PipelineBtns.pipelinesToEditor().click();
     }
 
+    public static checkDataStreamExists(dataSourceName: string) {
+        PipelineBtns.spPipelineElementSelection().should('be.visible');
+        PipelineBtns.editorAddPipelineElement().click();
+        cy.dataCy(dataSourceName).should('exist');
+        cy.dataCy('cancel-pipeline-element-discovery').click();
+    }
+
     public static selectDataStream(pipelineInput: PipelineInput) {
         // Select a stream
-        cy.dataCy('sp-pipeline-element-selection', { timeout: 10000 }).should(
-            'be.visible',
-        );
+        PipelineBtns.spPipelineElementSelection().should('be.visible');
         PipelineBtns.editorAddPipelineElement().click();
         cy.dataCy(pipelineInput.dataSource, { timeout: 10000 }).click();
     }
@@ -222,7 +227,7 @@ export class PipelineUtils {
             // 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');
+            cy.dataCy('no-table-entries').should('have.length', 2);
         } else {
             PipelineBtns.statusPipeline().should('have.length', amount);
         }
diff --git a/ui/cypress/support/utils/user/PermissionUtils.ts 
b/ui/cypress/support/utils/user/PermissionUtils.ts
index 3242960d22..4365eed24f 100644
--- a/ui/cypress/support/utils/user/PermissionUtils.ts
+++ b/ui/cypress/support/utils/user/PermissionUtils.ts
@@ -25,6 +25,13 @@ export class PermissionUtils {
         cy.dataCy('open-manage-permissions').click();
     }
 
+    public static changeOwnership(resourceName: string, email: string) {
+        PermissionUtils.openManagePermissions(resourceName);
+        cy.dataCy('owner-select').click();
+        cy.get(`[data-cy="owner-option-${email}"]`, { timeout: 10000 
}).click();
+        PermissionUtils.save();
+    }
+
     public static markElementAsPublic(resourceName: string) {
         PermissionUtils.openManagePermissions(resourceName);
         StaticPropertyUtils.clickCheckbox('permission-public-element');
@@ -40,7 +47,31 @@ export class PermissionUtils {
         PermissionUtils.save();
     }
 
+    public static authorizeGroup(resourceName: string, groupName: string) {
+        PermissionUtils.openManagePermissions(resourceName);
+        cy.dataCy('authorized-group').type(groupName);
+        cy.get(`[data-cy="group-option-${groupName}"]`).click();
+
+        PermissionUtils.save();
+    }
+
     public static save() {
         cy.dataCy('sp-manage-permissions-save').click();
     }
+
+    public static cancel() {
+        cy.dataCy('sp-manage-permissions-cancel').click();
+    }
+
+    public static validateUserCanNotChangePermissions(resourceName: string) {
+        PermissionUtils.openManagePermissions(resourceName);
+        cy.dataCy('warning-permissions-managed-by-owner').should('exist');
+        PermissionUtils.cancel();
+    }
+
+    public static validateUserCanChangePermissions(resourceName: string) {
+        PermissionUtils.openManagePermissions(resourceName);
+        cy.dataCy('permission-public-element').should('exist');
+        PermissionUtils.cancel();
+    }
 }
diff --git a/ui/cypress/support/utils/user/UserBtns.ts 
b/ui/cypress/support/utils/user/UserBtns.ts
index 6aa8c87052..0f75a04eec 100644
--- a/ui/cypress/support/utils/user/UserBtns.ts
+++ b/ui/cypress/support/utils/user/UserBtns.ts
@@ -17,22 +17,18 @@
  */
 
 export class UserBtns {
-    public static editUserBtn(username) {
-        cy.get('[data-cy="security-user-config"]')
-            .find('tr')
-            .contains('b', username)
-            .closest('tr')
-            .within(() => {
-                cy.get('[data-cy="user-edit-btn"]')
-                    .should('be.visible')
-                    .click();
-            });
+    public static editUserBtn(name: string) {
+        cy.dataCy(`user-edit-${name}`).click();
     }
 
     public static userRoleCheckbox(role) {
         return cy.dataCy('role-' + role).children();
     }
 
+    public static groupCheckbox(group: string) {
+        return cy.dataCy('group-' + group).children();
+    }
+
     public static saveEditUserBtn() {
         return cy.dataCy('sp-element-edit-user-save');
     }
diff --git a/ui/cypress/tests/userManagement/testUserRoleAsset.spec.ts 
b/ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
similarity index 98%
rename from ui/cypress/tests/userManagement/testUserRoleAsset.spec.ts
rename to ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
index ece8fba06d..a27908b732 100644
--- a/ui/cypress/tests/userManagement/testUserRoleAsset.spec.ts
+++ b/ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
@@ -27,7 +27,7 @@ import { DataExplorerUtils } from 
'../../support/utils/dataExplorer/DataExplorer
 import { DataExplorerBtns } from 
'../../support/utils/dataExplorer/DataExplorerBtns';
 import { ConnectBtns } from '../../support/utils/connect/ConnectBtns';
 
-describe('Test User Roles for Pipelines', () => {
+describe('Test that resources can be added to assets on creation', () => {
     let newUser;
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
diff --git a/ui/cypress/tests/userManagement/testGroupManagement.spec.ts 
b/ui/cypress/tests/userManagement/testGroupManagement.spec.ts
index 97a2302daa..07bdd5daca 100644
--- a/ui/cypress/tests/userManagement/testGroupManagement.spec.ts
+++ b/ui/cypress/tests/userManagement/testGroupManagement.spec.ts
@@ -85,16 +85,9 @@ describe('Test Group Management for Pipelines', () => {
             3,
         );
 
-        // Add new user group with pipeline admin role
-        ConfigurationBtns.newUserGroupBtn().click();
-        ConfigurationBtns.inputGroupName('User_Group');
-        cy.get('input[value="ROLE_PIPELINE_ADMIN"]').check();
-        UserBtns.saveEditUserBtn().click();
-
-        // Add first user to group
-        UserBtns.firstEditUserBtn().click();
-        cy.get('input[type="checkbox"]').eq(0).check();
-        UserBtns.saveEditUserBtn().click();
+        UserUtils.createGroup('User_Group', UserRole.ROLE_PIPELINE_ADMIN);
+
+        UserUtils.addGroupToUser('User_Group', user.name);
 
         // Add user group to pipeline
         PipelineUtils.goToPipelines();
diff --git a/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts 
b/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts
index a517d854d1..c3b5d585b3 100644
--- a/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts
@@ -19,53 +19,139 @@ import { UserRole } from 
'../../../src/app/_enums/user-role.enum';
 import { UserUtils } from '../../support/utils/UserUtils';
 import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
 import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
-import { NavigationUtils } from 
'../../support/utils/navigation/NavigationUtils';
 import { User } from '../../support/model/User';
+import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
 
 describe('Test User Roles for Connect', () => {
-    let connectAdminUser: User;
+    const adapterName = 'simulator';
+    let user1: User;
+    let user2: User;
+
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
-        connectAdminUser = UserUtils.createUser(
-            'user',
+        user1 = UserUtils.createUser(
+            'user1',
+            UserRole.ROLE_CONNECT_ADMIN,
+            UserRole.ROLE_PIPELINE_ADMIN,
+        );
+
+        user2 = UserUtils.createUser(
+            'user2',
             UserRole.ROLE_CONNECT_ADMIN,
+            UserRole.ROLE_PIPELINE_ADMIN,
         );
-        ConnectUtils.addMachineDataSimulator('simulator');
     });
 
-    it('Connect admin should not see adapters of other users', () => {
-        switchUserAndValidateConnectModuleIsShown();
+    it('Adapter is not shared with other users', () => {
+        // set up
+        UserUtils.switchUser(user1);
+        ConnectUtils.addMachineDataSimulator(adapterName);
 
-        // Validate that no adapter is visible
+        // check admin
+        UserUtils.switchUser(UserUtils.adminUser);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanChangePermissions(adapterName);
+
+        // check other users
+        UserUtils.switchUser(user2);
         ConnectUtils.checkAmountOfAdapters(0);
     });
 
-    it('Connect admin should see public adapters of other users', () => {
-        // Set adapter to public
-        PermissionUtils.markElementAsPublic('simulator');
+    it('Make adapter public', () => {
+        // set up
+        UserUtils.switchUser(user1);
+        ConnectUtils.addMachineDataSimulator(adapterName);
+        PermissionUtils.markElementAsPublic(adapterName);
 
-        switchUserAndValidateConnectModuleIsShown();
+        // check admin
+        UserUtils.switchUser(UserUtils.adminUser);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanChangePermissions(adapterName);
 
-        // Validate that adapter is visible
-        ConnectUtils.checkAmountOfAdapters(1);
+        // check other users
+        UserUtils.switchUser(user2);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanNotChangePermissions(adapterName);
     });
 
-    it('Connect admin should see shared adapters of other users', () => {
-        // Share adapter with user
-        PermissionUtils.authorizeUser('simulator', connectAdminUser.email);
+    it('Share adapter with other user and change ownership', () => {
+        const user3 = UserUtils.createUser(
+            'user3',
+            UserRole.ROLE_CONNECT_ADMIN,
+            UserRole.ROLE_PIPELINE_ADMIN,
+        );
+
+        // set up
+        UserUtils.switchUser(user1);
+        ConnectUtils.addMachineDataSimulator(adapterName);
+        PermissionUtils.authorizeUser(adapterName, user2.email);
+
+        // check admin
+        UserUtils.switchUser(UserUtils.adminUser);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanChangePermissions(adapterName);
+
+        // check authorized user
+        UserUtils.switchUser(user2);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanNotChangePermissions(adapterName);
 
-        switchUserAndValidateConnectModuleIsShown();
+        UserUtils.switchUser(user3);
+        ConnectUtils.checkAmountOfAdapters(0);
+
+        // change ownership to user3
+        UserUtils.switchUser(user1);
+        ConnectUtils.goToConnect();
+        PermissionUtils.changeOwnership(adapterName, user3.email);
+        ConnectUtils.checkAmountOfAdapters(0);
 
-        // Validate that adapter is visible
+        UserUtils.switchUser(UserUtils.adminUser);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanChangePermissions(adapterName);
+
+        // check authorized user
+        UserUtils.switchUser(user2);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanNotChangePermissions(adapterName);
+
+        // validate that user3 is owner now
+        UserUtils.switchUser(user3);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanChangePermissions(adapterName);
+    });
+
+    it('Adapter is shared with group for user 2', () => {
+        // Add group with connect admin rights
+        UserUtils.createGroup(
+            'connect_admin_group',
+            UserRole.ROLE_CONNECT_ADMIN,
+        );
+        UserUtils.addGroupToUser('connect_admin_group', user2.name);
+
+        // set up
+        UserUtils.switchUser(user1);
+        ConnectUtils.addMachineDataSimulator(adapterName);
+
+        PermissionUtils.authorizeGroup(adapterName, 'connect_admin_group');
+
+        // check admin
+        UserUtils.switchUser(UserUtils.adminUser);
+        validateAdapterIsVisible();
+        PermissionUtils.validateUserCanChangePermissions(adapterName);
+
+        // check other users
+        UserUtils.switchUser(user2);
         ConnectUtils.checkAmountOfAdapters(1);
     });
 
-    function switchUserAndValidateConnectModuleIsShown() {
-        UserUtils.switchUser(connectAdminUser);
+    function validateAdapterIsVisible() {
+        ConnectUtils.checkAmountOfAdapters(1);
+
+        ConnectUtils.validateEventsInPreview(adapterName, 7);
+
+        PipelineUtils.goToPipelineEditor();
+        PipelineUtils.checkDataStreamExists(adapterName);
 
-        NavigationUtils.validateActiveModules([
-            NavigationUtils.CONNECT,
-            NavigationUtils.CONFIGURATION,
-        ]);
+        ConnectUtils.goToConnect();
     }
 });
diff --git a/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts 
b/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts
index 67024c4ee4..be104523d0 100644
--- a/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRolePipeline.spec.ts
@@ -22,87 +22,155 @@ import { ConnectUtils } from 
'../../support/utils/connect/ConnectUtils';
 import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
 import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
 import { PipelineBtns } from '../../support/utils/pipeline/PipelineBtns';
-import { NavigationUtils } from 
'../../support/utils/navigation/NavigationUtils';
+import { User } from '../../support/model/User';
 
 describe('Test User Roles for Pipelines', () => {
+    const pipelineName = 'Persist simulator';
+    let pipelineUser1: User;
+    let pipelineAdmin1: User;
+    let pipelineAdmin2: User;
+
     beforeEach('Setup Test', () => {
         cy.initStreamPipesTest();
-        // Create a machine data simulator with a sample pipeline for the tests
-        ConnectUtils.addMachineDataSimulator('simulator', true);
-    });
 
-    it('Pipeline admin should not see pipelines of other users', () => {
-        const newUser = UserUtils.createUser(
-            'user',
+        pipelineUser1 = UserUtils.createUser(
+            'pipelineUser1',
+            UserRole.ROLE_PIPELINE_USER,
+        );
+
+        pipelineAdmin1 = UserUtils.createUser(
+            'pipelineAdmin1',
+            UserRole.ROLE_CONNECT_ADMIN,
+            UserRole.ROLE_PIPELINE_ADMIN,
+        );
+
+        pipelineAdmin2 = UserUtils.createUser(
+            'pipelineAdmin2',
             UserRole.ROLE_PIPELINE_ADMIN,
         );
+    });
 
-        // Login as user and check if pipeline is visible to user
-        UserUtils.switchUser(newUser);
+    it('Pipeline is not shared with other users', () => {
+        UserUtils.switchUser(pipelineAdmin1);
+        ConnectUtils.addMachineDataSimulator('simulator', true);
 
-        NavigationUtils.validateActiveModules([
-            NavigationUtils.PIPELINES,
-            NavigationUtils.CONFIGURATION,
-        ]);
+        assertPipelineIsVisibleAndEditableCanChangePermissions(
+            UserUtils.adminUser,
+        );
 
-        PipelineUtils.goToPipelines();
-        PipelineUtils.checkAmountOfPipelinesPipeline(0);
+        assertPipelineIsNotVisible(pipelineUser1);
+
+        UserUtils.switchUser(pipelineUser1);
+
+        assertPipelineIsNotVisible(pipelineAdmin2);
     });
 
-    it('Pipeline admin should see public pipelines of other users', () => {
-        const newUser = UserUtils.createUser(
-            'user',
-            UserRole.ROLE_PIPELINE_ADMIN,
-        );
+    it('Make pipeline public', () => {
+        UserUtils.switchUser(pipelineAdmin1);
+        ConnectUtils.addMachineDataSimulator('simulator', true);
 
-        // Add new authorized user to pipeline
         PipelineUtils.goToPipelines();
-        PermissionUtils.markElementAsPublic('Persist simulator');
+        PermissionUtils.markElementAsPublic(pipelineName);
 
-        // Login as user and check if pipeline is visible to user
-        UserUtils.switchUser(newUser);
+        assertPipelineIsVisibleAndEditableCanChangePermissions(
+            UserUtils.adminUser,
+        );
 
-        PipelineUtils.goToPipelines();
-        PipelineUtils.checkAmountOfPipelinesPipeline(1);
-    });
+        assertPipelineIsVisibleButNotEditable(pipelineUser1);
 
-    it(' Pipeline admin should see shared pipelines of other users', () => {
-        const newUser = UserUtils.createUser(
-            'user',
-            UserRole.ROLE_PIPELINE_ADMIN,
+        assertPipelineIsVisibleAndEditableCannotChangePermissions(
+            pipelineAdmin2,
         );
+    });
+
+    it('Share pipeline with other user and change ownership', () => {
+        UserUtils.switchUser(pipelineAdmin1);
+        ConnectUtils.addMachineDataSimulator('simulator', true);
 
-        // Add new authorized user to pipeline
         PipelineUtils.goToPipelines();
-        PermissionUtils.markElementAsPublic('Persist simulator');
-        PermissionUtils.authorizeUser('Persist simulator', newUser.email);
+        PermissionUtils.authorizeUser(pipelineName, pipelineAdmin2.email);
 
-        // Login as user and check if pipeline is visible to user
-        UserUtils.switchUser(newUser);
+        assertPipelineIsVisibleAndEditableCanChangePermissions(
+            UserUtils.adminUser,
+        );
+
+        assertPipelineIsVisibleAndEditableCannotChangePermissions(
+            pipelineAdmin2,
+        );
+
+        assertPipelineIsNotVisible(pipelineUser1);
 
+        UserUtils.switchUser(pipelineAdmin1);
         PipelineUtils.goToPipelines();
-        PipelineUtils.checkAmountOfPipelinesPipeline(1);
+        PermissionUtils.changeOwnership(pipelineName, pipelineAdmin2.email);
+
+        assertPipelineIsNotVisible(pipelineAdmin1);
+
+        assertPipelineIsVisibleAndEditableCanChangePermissions(
+            UserUtils.adminUser,
+        );
+
+        assertPipelineIsVisibleAndEditableCanChangePermissions(pipelineAdmin2);
+
+        assertPipelineIsNotVisible(pipelineUser1);
     });
 
-    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,
+    it('Pipeline is shared with group for user 2', () => {
+        UserUtils.createGroup(
+            'pipeline_admin_group',
+            UserRole.ROLE_PIPELINE_ADMIN,
         );
+        UserUtils.addGroupToUser('pipeline_admin_group', pipelineAdmin2.name);
+
+        // set up
+        UserUtils.switchUser(pipelineAdmin1);
+        ConnectUtils.addMachineDataSimulator('simulator', true);
 
-        // Add new authorized user to pipeline
         PipelineUtils.goToPipelines();
-        // PermissionUtils.markElementAsPublic();
-        PermissionUtils.authorizeUser('Persist simulator', newUser.email);
+        PermissionUtils.authorizeGroup(pipelineName, 'pipeline_admin_group');
 
-        // Login as user and check if pipeline is visible to user
-        UserUtils.switchUser(newUser);
+        assertPipelineIsVisibleAndEditableCanChangePermissions(
+            UserUtils.adminUser,
+        );
+
+        assertPipelineIsNotVisible(pipelineUser1);
 
+        assertPipelineIsVisibleAndEditableCannotChangePermissions(
+            pipelineAdmin2,
+        );
+    });
+
+    function assertPipelineIsVisibleAndEditableCanChangePermissions(
+        user: User,
+    ) {
+        UserUtils.switchUser(user);
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(1);
+        PipelineBtns.stopPipeline().should('not.be.disabled');
+        PermissionUtils.validateUserCanChangePermissions(pipelineName);
+    }
+
+    function assertPipelineIsVisibleAndEditableCannotChangePermissions(
+        user: User,
+    ) {
+        UserUtils.switchUser(user);
         PipelineUtils.goToPipelines();
         PipelineUtils.checkAmountOfPipelinesPipeline(1);
+        PipelineBtns.stopPipeline().should('not.be.disabled');
+        PermissionUtils.validateUserCanNotChangePermissions(pipelineName);
+    }
 
-        // A pipeline user should not be able to stop the pipeline or delete it
-        PipelineBtns.deletePipeline().should('not.exist');
+    function assertPipelineIsVisibleButNotEditable(user: User) {
+        UserUtils.switchUser(user);
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(1);
         PipelineBtns.stopPipeline().should('be.disabled');
-    });
+        PermissionUtils.validateUserCanNotChangePermissions(pipelineName);
+    }
+
+    function assertPipelineIsNotVisible(user: User) {
+        UserUtils.switchUser(user);
+        PipelineUtils.goToPipelines();
+        PipelineUtils.checkAmountOfPipelinesPipeline(0);
+    }
 });
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/object-permission-dialog/object-permission-dialog.component.html
 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/object-permission-dialog/object-permission-dialog.component.html
index 6e1cdf3c61..069e4044a4 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/object-permission-dialog/object-permission-dialog.component.html
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/object-permission-dialog/object-permission-dialog.component.html
@@ -35,10 +35,18 @@
                         }}</span>
                         <mat-form-field color="accent">
                             <mat-label>{{ 'Owner' | translate }}</mat-label>
-                            <mat-select formControlName="owner" fxFlex 
required>
+                            <mat-select
+                                data-cy="owner-select"
+                                formControlName="owner"
+                                fxFlex
+                                required
+                            >
                                 <mat-option
                                     *ngFor="let user of allUsers"
                                     [value]="user.principalId"
+                                    [attr.data-cy]="
+                                        'owner-option-' + user.email
+                                    "
                                     >{{ user.email }}
                                 </mat-option>
                             </mat-select>
@@ -144,6 +152,7 @@
                                     <input
                                         matInput
                                         [placeholder]="'Add' | translate"
+                                        data-cy="authorized-group"
                                         #groupInput
                                         [formControl]="groupCtrl"
                                         [matAutocomplete]="groupAuto"
@@ -161,6 +170,9 @@
                                     (optionSelected)="groupSelected($event)"
                                 >
                                     <mat-option
+                                        [attr.data-cy]="
+                                            'group-option-' + group.groupName
+                                        "
                                         *ngFor="
                                             let group of filteredGroups$ | 
async
                                         "
@@ -219,7 +231,10 @@
             </div>
         </div>
     } @else {
-        <div class="sp-dialog-content">
+        <div
+            class="sp-dialog-content"
+            data-cy="warning-permissions-managed-by-owner"
+        >
             <div fxFlex="100" fxLayoutAlign="center center" fxLayout="column">
                 <mat-icon>block</mat-icon>
                 <h5>
@@ -252,6 +267,7 @@
                 mat-button
                 mat-flat-button
                 class="mat-basic"
+                data-cy="sp-manage-permissions-cancel"
                 (click)="close(false)"
             >
                 {{ 'Cancel' | translate }}
diff --git 
a/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
 
b/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
index 3809333f0c..b1bfc9d312 100644
--- 
a/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
+++ 
b/ui/src/app/configuration/security-configuration/edit-user-dialog/edit-user-dialog.component.html
@@ -157,6 +157,7 @@
                             [value]="group.groupId"
                             [checked]="user.groups.indexOf(group.groupId) > -1"
                             (change)="changeGroupAssignment($event)"
+                            [attr.data-cy]="'group-' + group.groupName"
                         >
                             {{ group.groupName }}
                         </mat-checkbox>
diff --git 
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
 
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
index 7ddef77d55..a429801b3f 100644
--- 
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
+++ 
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
@@ -104,7 +104,9 @@
                                     class="mat-basic"
                                     [matTooltip]="'Edit user' | translate"
                                     matTooltipPosition="above"
-                                    [attr.data-cy]="'user-edit-btn'"
+                                    [attr.data-cy]="
+                                        'user-edit-' + account.fullName
+                                    "
                                     (click)="editUser(account)"
                                 >
                                     <i class="material-icons">edit</i>
diff --git 
a/ui/src/app/editor/dialog/pipeline-element-discovery/pipeline-element-discovery.component.html
 
b/ui/src/app/editor/dialog/pipeline-element-discovery/pipeline-element-discovery.component.html
index 02f4c257dc..eb0bf6a0d0 100644
--- 
a/ui/src/app/editor/dialog/pipeline-element-discovery/pipeline-element-discovery.component.html
+++ 
b/ui/src/app/editor/dialog/pipeline-element-discovery/pipeline-element-discovery.component.html
@@ -59,7 +59,13 @@
     </div>
     <mat-divider></mat-divider>
     <div class="sp-dialog-actions">
-        <button mat-button mat-flat-button class="mat-basic" (click)="hide()">
+        <button
+            data-cy="cancel-pipeline-element-discovery"
+            mat-button
+            mat-flat-button
+            class="mat-basic"
+            (click)="hide()"
+        >
             Cancel
         </button>
     </div>


Reply via email to