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

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


The following commit(s) were added to refs/heads/rel/0.98.0 by this push:
     new 0261a9bdf0 Role Management for Assets in create Tasks (#3897)
0261a9bdf0 is described below

commit 0261a9bdf0ffb3def7736917a49ff0b5b93e1b9f
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Tue Nov 11 16:36:41 2025 +0100

    Role Management for Assets in create Tasks (#3897)
    
    Co-authored-by: Philipp Zehnder <[email protected]>
---
 ui/cypress/support/utils/UserUtils.ts              |  17 +++
 ui/cypress/support/utils/connect/ConnectBtns.ts    |   4 +
 .../support/utils/dataExplorer/DataExplorerBtns.ts |  10 +-
 ui/cypress/support/utils/pipeline/PipelineBtns.ts  |  12 ++
 .../{pipeline/PipelineBtns.ts => user/UserBtns.ts} |  22 ++--
 .../tests/userManagement/testUserRoleAsset.spec.ts | 139 +++++++++++++++++++++
 .../security-user-config.component.html            |   6 +-
 .../start-adapter-configuration.component.html     | 108 ++++++++--------
 .../start-adapter-configuration.component.ts       |  21 +++-
 .../edit-dashboard-dialog.component.html           |  67 +++++-----
 .../edit-dashboard-dialog.component.ts             |  15 ++-
 ...data-explorer-chart-view-toolbar.component.html |  22 ++--
 .../data-explorer-chart-view-toolbar.component.ts  |  26 +++-
 .../save-pipeline-settings.component.html          |  38 +++---
 .../save-pipeline-settings.component.ts            |  12 ++
 .../save-pipeline/save-pipeline.component.html     |   1 +
 16 files changed, 396 insertions(+), 124 deletions(-)

diff --git a/ui/cypress/support/utils/UserUtils.ts 
b/ui/cypress/support/utils/UserUtils.ts
index 26802e32ea..ff660fae37 100644
--- a/ui/cypress/support/utils/UserUtils.ts
+++ b/ui/cypress/support/utils/UserUtils.ts
@@ -19,6 +19,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';
 
 export class UserUtils {
     public static adminUser = 
UserBuilder.create('[email protected]')
@@ -42,6 +43,7 @@ export class UserUtils {
 
     public static goToUserConfiguration() {
         cy.visit('#/configuration/security');
+        cy.dataCy('add-new-user', { timeout: 10000 }).should('exist');
     }
 
     public static addUser(user: User) {
@@ -66,6 +68,21 @@ export class UserUtils {
         cy.dataCy('sp-element-edit-user-save').click();
     }
 
+    public static toggleUserRole(user: User, role: UserRole) {
+        this.switchUser(this.adminUser);
+        this.goToUserConfiguration();
+        cy.get('table tbody tr', { timeout: 10000 }).should(
+            'have.length.greaterThan',
+            0,
+        );
+
+        UserBtns.editUserBtn(user.email);
+
+        UserBtns.userRoleCheckbox(role).click();
+
+        UserBtns.saveEditUserBtn().click();
+    }
+
     /**
      * Create a new user with the specified roles and a default password to 
the system.
      *
diff --git a/ui/cypress/support/utils/connect/ConnectBtns.ts 
b/ui/cypress/support/utils/connect/ConnectBtns.ts
index 8319dab829..c0940f8c16 100644
--- a/ui/cypress/support/utils/connect/ConnectBtns.ts
+++ b/ui/cypress/support/utils/connect/ConnectBtns.ts
@@ -93,6 +93,10 @@ export class ConnectBtns {
         return cy.dataCy('show-code-checkbox');
     }
 
+    public static assetCheckbox() {
+        return cy.dataCy('show-asset-checkbox');
+    }
+
     // ========================================================================
 
     // =====================  Event Schema buttons  ==========================
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts 
b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
index e18474ac40..275a1d9a77 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
@@ -38,7 +38,7 @@ export class DataExplorerBtns {
     }
 
     public static chartAssetCheckboxBtn() {
-        return cy.dataCy('sp-show-chart-asset-checkbox');
+        return cy.dataCy('add-to-Asset-data-view-btn');
     }
     public static confirmAssetSelectionBtn() {
         return cy
@@ -51,4 +51,12 @@ export class DataExplorerBtns {
             .dataCy('edit-data-view-' + widgetName.replaceAll(' ', ''))
             .click();
     }
+
+    public static dashboardAssetCheckboxBtn() {
+        return cy.dataCy('sp-show-dashboard-asset-checkbox');
+    }
+
+    public static closeDashboardCreate() {
+        return cy.dataCy('close-data-view');
+    }
 }
diff --git a/ui/cypress/support/utils/pipeline/PipelineBtns.ts 
b/ui/cypress/support/utils/pipeline/PipelineBtns.ts
index faece471a5..a79e14dccf 100644
--- a/ui/cypress/support/utils/pipeline/PipelineBtns.ts
+++ b/ui/cypress/support/utils/pipeline/PipelineBtns.ts
@@ -28,4 +28,16 @@ export class PipelineBtns {
     public static deletePipeline() {
         return cy.dataCy('delete-pipeline', { timeout: 10000 });
     }
+
+    public static pipelineEditorSave() {
+        return cy.dataCy('sp-editor-save-pipeline');
+    }
+
+    public static pipelineAssetCheckbox() {
+        return cy.dataCy('sp-show-pipeline-asset-checkbox');
+    }
+
+    public static pipelineEditorCancel() {
+        return cy.dataCy('sp-editor-cancel');
+    }
 }
diff --git a/ui/cypress/support/utils/pipeline/PipelineBtns.ts 
b/ui/cypress/support/utils/user/UserBtns.ts
similarity index 59%
copy from ui/cypress/support/utils/pipeline/PipelineBtns.ts
copy to ui/cypress/support/utils/user/UserBtns.ts
index faece471a5..b3cfb8525f 100644
--- a/ui/cypress/support/utils/pipeline/PipelineBtns.ts
+++ b/ui/cypress/support/utils/user/UserBtns.ts
@@ -16,16 +16,24 @@
  *
  */
 
-export class PipelineBtns {
-    public static statusPipeline() {
-        return cy.dataCy('status-pipeline-green', { timeout: 10000 });
+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 stopPipeline() {
-        return cy.dataCy('stop-pipeline-button', { timeout: 10000 });
+    public static userRoleCheckbox(role) {
+        return cy.dataCy('role-' + role).children();
     }
 
-    public static deletePipeline() {
-        return cy.dataCy('delete-pipeline', { timeout: 10000 });
+    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/testUserRoleAsset.spec.ts
new file mode 100644
index 0000000000..ece8fba06d
--- /dev/null
+++ b/ui/cypress/tests/userManagement/testUserRoleAsset.spec.ts
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { UserRole } from '../../../src/app/_enums/user-role.enum';
+import { UserUtils } from '../../support/utils/UserUtils';
+import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils';
+import { PipelineBtns } from '../../support/utils/pipeline/PipelineBtns';
+import { AssetUtils } from '../../support/utils/asset/AssetUtils';
+import { AdapterBuilder } from '../../support/builder/AdapterBuilder';
+import { DataExplorerUtils } from 
'../../support/utils/dataExplorer/DataExplorerUtils';
+import { DataExplorerBtns } from 
'../../support/utils/dataExplorer/DataExplorerBtns';
+import { ConnectBtns } from '../../support/utils/connect/ConnectBtns';
+
+describe('Test User Roles for Pipelines', () => {
+    let newUser;
+    beforeEach('Setup Test', () => {
+        cy.initStreamPipesTest();
+        newUser = UserUtils.createUser(
+            'user',
+            UserRole.ROLE_PIPELINE_ADMIN,
+            UserRole.ROLE_ASSET_ADMIN,
+            UserRole.ROLE_CONNECT_ADMIN,
+            UserRole.ROLE_DATA_EXPLORER_ADMIN,
+            UserRole.ROLE_DASHBOARD_ADMIN,
+        );
+
+        AssetUtils.goToAssets();
+        AssetUtils.addAndSaveAsset('Asset');
+    });
+
+    it('Check Role Asset Admin in Connect', () => {
+        UserUtils.switchUser(newUser);
+
+        ConnectUtils.goToConnect();
+
+        ConnectUtils.addAdapter(
+            AdapterBuilder.create('Machine_Data_Simulator')
+                .setName('Machine Data Simulator Test 1')
+                .addInput('input', 'wait-time-ms', '1000')
+                .setStartAdapter(false)
+                .build(),
+        );
+        ConnectBtns.assetCheckbox().should('exist');
+
+        UserUtils.toggleUserRole(newUser, UserRole.ROLE_ASSET_ADMIN);
+
+        UserUtils.switchUser(newUser);
+
+        ConnectUtils.goToConnect();
+
+        ConnectUtils.addAdapter(
+            AdapterBuilder.create('Machine_Data_Simulator')
+                .setName('Machine Data Simulator Test2')
+                .addInput('input', 'wait-time-ms', '1000')
+                .setStartAdapter(false)
+                .build(),
+        );
+        ConnectBtns.assetCheckbox().should('not.exist');
+    });
+
+    it('Check Role Asset Admin in Pipeline', () => {
+        UserUtils.switchUser(newUser);
+
+        PipelineUtils.goToPipelines();
+
+        PipelineUtils.addSampleAdapterAndPipeline();
+
+        PipelineUtils.editPipeline('Pipeline Test');
+
+        PipelineBtns.pipelineEditorSave().click();
+
+        PipelineBtns.pipelineAssetCheckbox().should('exist');
+
+        PipelineBtns.pipelineEditorCancel().click();
+
+        UserUtils.toggleUserRole(newUser, UserRole.ROLE_ASSET_ADMIN);
+
+        UserUtils.switchUser(newUser);
+
+        PipelineUtils.goToPipelines();
+
+        PipelineUtils.editPipeline('Pipeline Test');
+
+        PipelineBtns.pipelineEditorSave().click();
+
+        PipelineBtns.pipelineAssetCheckbox().should('not.exist');
+    });
+
+    it('Check Role Asset Admin in Charts', () => {
+        UserUtils.switchUser(newUser);
+
+        DataExplorerUtils.goToDatalake();
+        DataExplorerUtils.createAndEditDataView();
+
+        DataExplorerBtns.chartAssetCheckboxBtn().should('exist');
+
+        UserUtils.toggleUserRole(newUser, UserRole.ROLE_ASSET_ADMIN);
+
+        UserUtils.switchUser(newUser);
+
+        DataExplorerUtils.goToDatalake();
+        DataExplorerUtils.createAndEditDataView();
+
+        DataExplorerBtns.chartAssetCheckboxBtn().should('not.exist');
+    });
+
+    it('Check Role Asset Admin in Dashboard', () => {
+        UserUtils.switchUser(newUser);
+        DataExplorerUtils.goToDashboard();
+        DataExplorerUtils.createDashboard('Test');
+
+        DataExplorerBtns.dashboardAssetCheckboxBtn().should('exist');
+        DataExplorerBtns.closeDashboardCreate().click();
+
+        UserUtils.toggleUserRole(newUser, UserRole.ROLE_ASSET_ADMIN);
+
+        UserUtils.switchUser(newUser);
+
+        DataExplorerUtils.goToDashboard();
+        DataExplorerUtils.createDashboard('Test');
+        DataExplorerBtns.dashboardAssetCheckboxBtn().should('not.exist');
+    });
+});
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 9e17796d6e..a7cb3e39cf 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
@@ -112,7 +112,7 @@
                                     class="mat-basic"
                                     [matTooltip]="'Edit user' | translate"
                                     matTooltipPosition="above"
-                                    data-cy="user-edit-btn"
+                                    [attr.data-cy]="'user-edit-btn'"
                                     (click)="editUser(account)"
                                 >
                                     <i class="material-icons">edit</i>
@@ -124,9 +124,7 @@
                                 mat-flat-button
                                 [matTooltip]="'Delete user' | translate"
                                 matTooltipPosition="above"
-                                [attr.data-cy]="
-                                    'user-delete-btn-' + account.fullName
-                                "
+                                [attr.data-cy]="'user-delete-btn'"
                                 (click)="deleteUser(account)"
                             >
                                 <i class="material-icons">delete</i>
diff --git 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
index 20b14171b6..442b1fdf49 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
+++ 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
@@ -103,26 +103,30 @@
         >
         </sp-adapter-options-panel>
 
-        <sp-adapter-options-panel
-            [optionTitle]="'Add to Asset' | translate"
-            [optionDescription]="'Add Adapter to an existing Asset' | 
translate"
-            optionIcon="precision_manufacturing"
-            dataCy="show-asset-checkbox"
-            [isChecked]="
-                showAsset && (!isEditMode || originalAssets.length > 0)
-            "
-            (optionSelectedEmitter)="showAsset = $event"
-        >
-            <sp-asset-link-configuration
-                *ngIf="showAsset"
-                [isEdit]="isEditMode"
-                [itemId]="adapterDescription.elementId"
-                (selectedAssetsChange)="onSelectedAssetsChange($event)"
-                (deselectedAssetsChange)="onDeselectedAssetsChange($event)"
-                (originalAssetsEmitter)="onOriginalAssetsEmitted($event)"
+        @if (isAssetAdmin) {
+            <sp-adapter-options-panel
+                [optionTitle]="'Add to Asset' | translate"
+                [optionDescription]="
+                    'Add Adapter to an existing Asset' | translate
+                "
+                optionIcon="precision_manufacturing"
+                dataCy="show-asset-checkbox"
+                [isChecked]="
+                    showAsset && (!isEditMode || originalAssets.length > 0)
+                "
+                (optionSelectedEmitter)="showAsset = $event"
             >
-            </sp-asset-link-configuration>
-        </sp-adapter-options-panel>
+                <sp-asset-link-configuration
+                    *ngIf="showAsset"
+                    [isEdit]="isEditMode"
+                    [itemId]="adapterDescription.elementId"
+                    (selectedAssetsChange)="onSelectedAssetsChange($event)"
+                    (deselectedAssetsChange)="onDeselectedAssetsChange($event)"
+                    (originalAssetsEmitter)="onOriginalAssetsEmitted($event)"
+                >
+                </sp-asset-link-configuration>
+            </sp-adapter-options-panel>
+        }
 
         <sp-adapter-options-panel
             [optionTitle]="'Remove Duplicates' | translate"
@@ -197,38 +201,44 @@
         </sp-adapter-options-panel>
 
         <!-- Start pipeline template to store raw events in data lake -->
-        <sp-adapter-options-panel
-            [optionTitle]="'Persist events' | translate"
-            [optionDescription]="
-                'Store all events of this source in the internal data store'
-                    | translate
-            "
-            optionIcon="save"
-            dataCy="sp-store-in-datalake"
-            *ngIf="!isEditMode"
-            (optionSelectedEmitter)="handlePersistOption($event)"
-        >
-            <mat-form-field *ngIf="saveInDataLake" color="accent" 
class="mt-10">
-                <mat-label>{{ 'Select Time Field' | translate }}</mat-label>
-                <mat-select
-                    [(ngModel)]="dataLakeTimestampField"
-                    [ngModelOptions]="{ standalone: true }"
-                    data-cy="sp-store-in-datalake-timestamp"
+        @if (isPipelineAdmin) {
+            <sp-adapter-options-panel
+                [optionTitle]="'Persist events' | translate"
+                [optionDescription]="
+                    'Store all events of this source in the internal data 
store'
+                        | translate
+                "
+                optionIcon="save"
+                dataCy="sp-store-in-datalake"
+                *ngIf="!isEditMode"
+                (optionSelectedEmitter)="handlePersistOption($event)"
+            >
+                <mat-form-field
+                    *ngIf="saveInDataLake"
+                    color="accent"
+                    class="mt-10"
                 >
-                    <mat-option
-                        class="md-elevation-z1"
-                        style="background: white"
-                        *ngFor="
-                            let timestampField of eventSchema.eventProperties
-                                | timestampFilter
-                        "
-                        [value]="timestampField.runtimeName"
+                    <mat-label>{{ 'Select Time Field' | translate 
}}</mat-label>
+                    <mat-select
+                        [(ngModel)]="dataLakeTimestampField"
+                        [ngModelOptions]="{ standalone: true }"
+                        data-cy="sp-store-in-datalake-timestamp"
                     >
-                        {{ timestampField.runtimeName }}
-                    </mat-option>
-                </mat-select>
-            </mat-form-field>
-        </sp-adapter-options-panel>
+                        <mat-option
+                            class="md-elevation-z1"
+                            style="background: white"
+                            *ngFor="
+                                let timestampField of 
eventSchema.eventProperties
+                                    | timestampFilter
+                            "
+                            [value]="timestampField.runtimeName"
+                        >
+                            {{ timestampField.runtimeName }}
+                        </mat-option>
+                    </mat-select>
+                </mat-form-field>
+            </sp-adapter-options-panel>
+        }
 
         <sp-adapter-options-panel
             [optionTitle]="'Show code' | translate"
diff --git 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
index 6c69a048f6..eda1100774 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
+++ 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
@@ -22,6 +22,7 @@ import {
     EventSchema,
     SpAssetTreeNode,
     RemoveDuplicatesTransformationRuleDescription,
+    UserInfo,
 } from '@streampipes/platform-services';
 import {
     UntypedFormBuilder,
@@ -31,12 +32,17 @@ import {
 } from '@angular/forms';
 import { MatStepper } from '@angular/material/stepper';
 import { AdapterStartedDialog } from 
'../../../dialog/adapter-started/adapter-started-dialog.component';
-import { DialogService, PanelType } from '@streampipes/shared-ui';
+import {
+    CurrentUserService,
+    DialogService,
+    PanelType,
+} from '@streampipes/shared-ui';
 import { ShepherdService } from '../../../../services/tour/shepherd.service';
 import { TimestampPipe } from '../../../filter/timestamp.pipe';
 import { TransformationRuleService } from 
'../../../services/transformation-rule.service';
 import { ValidateName } from 
'../../../../core-ui/static-properties/input.validator';
 import { TranslateService } from '@ngx-translate/core';
+import { UserRole } from 'src/app/_enums/user-role.enum';
 
 @Component({
     selector: 'sp-start-adapter-configuration',
@@ -85,6 +91,8 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
 
     startAdapterSettingsFormValid = false;
 
+    currentUser: UserInfo;
+
     // preprocessing rule variables
     removeDuplicates = false;
     removeDuplicatesTime: number;
@@ -103,6 +111,9 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
     deselectedAssets = [];
     originalAssets = [];
 
+    isAssetAdmin = false;
+    isPipelineAdmin = false;
+
     constructor(
         private dialogService: DialogService,
         private shepherdService: ShepherdService,
@@ -110,10 +121,18 @@ export class StartAdapterConfigurationComponent 
implements OnInit {
         private timestampPipe: TimestampPipe,
         private transformationRuleService: TransformationRuleService,
         private translateService: TranslateService,
+        private currentUserService: CurrentUserService,
     ) {}
 
     ngOnInit(): void {
         this.showAsset = this.isEditMode;
+        this.currentUser = this.currentUserService.getCurrentUser();
+        this.isAssetAdmin = this.currentUserService.hasRole(
+            UserRole.ROLE_ASSET_ADMIN,
+        );
+        this.isPipelineAdmin = this.currentUserService.hasRole(
+            UserRole.ROLE_PIPELINE_ADMIN,
+        );
         this.startAdapterForm = this._formBuilder.group({});
         this.startAdapterForm.addControl(
             'adapterName',
diff --git 
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
 
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
index 8c2f062889..ae95727681 100644
--- 
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
+++ 
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
@@ -98,39 +98,43 @@
                     </mat-checkbox>
                 </div>
 
-                <div class="mt-10" fxLayout="column">
-                    <label>{{ 'Add Dashboard to Assets' | translate }}</label>
+                @if (isAssetAdmin) {
+                    <div class="mt-10" fxLayout="column">
+                        <label>{{
+                            'Add Dashboard to Assets' | translate
+                        }}</label>
 
-                    <mat-checkbox
-                        [(ngModel)]="addToAssets"
-                        color="accent"
-                        data-cy="sp-show-dashboard-asset-checkbox"
-                    >
-                        {{
-                            'Add the current dashboard to an existing asset'
-                                | translate
-                        }}
-                    </mat-checkbox>
+                        <mat-checkbox
+                            [(ngModel)]="addToAssets"
+                            color="accent"
+                            data-cy="sp-show-dashboard-asset-checkbox"
+                        >
+                            {{
+                                'Add the current dashboard to an existing 
asset'
+                                    | translate
+                            }}
+                        </mat-checkbox>
 
-                    @if (addToAssets) {
-                        <div class="mt-10">
-                            <sp-asset-link-configuration
-                                [isEdit]="!createMode"
-                                [itemId]="dashboard.elementId"
-                                (selectedAssetsChange)="
-                                    onSelectedAssetsChange($event)
-                                "
-                                (deselectedAssetsChange)="
-                                    onDeselectedAssetsChange($event)
-                                "
-                                (originalAssetsEmitter)="
-                                    onOriginalAssetsEmitted($event)
-                                "
-                            >
-                            </sp-asset-link-configuration>
-                        </div>
-                    }
-                </div>
+                        @if (addToAssets) {
+                            <div class="mt-10">
+                                <sp-asset-link-configuration
+                                    [isEdit]="!createMode"
+                                    [itemId]="dashboard.elementId"
+                                    (selectedAssetsChange)="
+                                        onSelectedAssetsChange($event)
+                                    "
+                                    (deselectedAssetsChange)="
+                                        onDeselectedAssetsChange($event)
+                                    "
+                                    (originalAssetsEmitter)="
+                                        onOriginalAssetsEmitted($event)
+                                    "
+                                >
+                                </sp-asset-link-configuration>
+                            </div>
+                        }
+                    </div>
+                }
 
                 <!--<mat-checkbox [(ngModel)]="dashboard.displayHeader">Show 
name and description in dashboard</mat-checkbox>-->
             </div>
@@ -153,6 +157,7 @@
             mat-flat-button
             class="mat-basic mr-10"
             (click)="onCancel()"
+            data-cy="close-data-view"
         >
             {{ 'Close' | translate }}
         </button>
diff --git 
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
 
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
index 426b676362..554e65cdd9 100644
--- 
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
+++ 
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
@@ -29,8 +29,14 @@ import {
     DashboardService,
     LinkageData,
     SpAssetTreeNode,
+    UserInfo,
 } from '@streampipes/platform-services';
-import { AssetSaveService, DialogRef } from '@streampipes/shared-ui';
+import {
+    AssetSaveService,
+    CurrentUserService,
+    DialogRef,
+} from '@streampipes/shared-ui';
+import { UserRole } from 'src/app/_enums/user-role.enum';
 
 @Component({
     selector: 'sp-edit-dashboard-dialog-component',
@@ -52,10 +58,17 @@ export class EditDashboardDialogComponent implements OnInit 
{
     private dialogRef = inject(DialogRef<EditDashboardDialogComponent>);
     private dashboardService = inject(DashboardService);
     private assetSaveService = inject(AssetSaveService);
+    private readonly currentUserService = inject(CurrentUserService);
 
+    currentUser: UserInfo;
+    isAssetAdmin = false;
     addToAssets: boolean = false;
 
     ngOnInit() {
+        this.currentUser = this.currentUserService.getCurrentUser();
+        this.isAssetAdmin = this.currentUserService.hasRole(
+            UserRole.ROLE_ASSET_ADMIN,
+        );
         if (!this.dashboard.dashboardGeneralSettings.defaultViewMode) {
             this.dashboard.dashboardGeneralSettings.defaultViewMode = 'grid';
         }
diff --git 
a/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.html
 
b/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.html
index 680c07a36f..739d94cbd1 100644
--- 
a/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.html
+++ 
b/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.html
@@ -67,16 +67,18 @@
             <mat-icon>save</mat-icon>
             {{ 'Save' | translate }}
         </button>
-        <button
-            mat-flat-button
-            [matTooltip]="'Add to Asset' | translate"
-            class="edit-menu-btn"
-            (click)="addToAssetEmitter.emit()"
-            data-cy="add-to-Asset-data-view-btn"
-        >
-            <mat-icon>precision_manufacturing</mat-icon>
-            {{ 'Add To Asset' | translate }}
-        </button>
+        @if (isAssetAdmin) {
+            <button
+                mat-flat-button
+                [matTooltip]="'Add to Asset' | translate"
+                class="edit-menu-btn"
+                (click)="addToAssetEmitter.emit()"
+                data-cy="add-to-Asset-data-view-btn"
+            >
+                <mat-icon>precision_manufacturing</mat-icon>
+                {{ 'Add To Asset' | translate }}
+            </button>
+        }
         <button
             mat-flat-button
             [matTooltip]="'Discard' | translate"
diff --git 
a/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.ts
 
b/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.ts
index 085271665e..dbc232a765 100644
--- 
a/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.ts
+++ 
b/ui/src/app/data-explorer/components/chart-view/toolbar/data-explorer-chart-view-toolbar.component.ts
@@ -16,11 +16,21 @@
  *
  */
 
-import { Component, EventEmitter, Input, Output } from '@angular/core';
+import {
+    Component,
+    EventEmitter,
+    inject,
+    Input,
+    OnInit,
+    Output,
+} from '@angular/core';
 import {
     DataExplorerWidgetModel,
     TimeSettings,
+    UserInfo,
 } from '@streampipes/platform-services';
+import { CurrentUserService } from '@streampipes/shared-ui';
+import { UserRole } from 'src/app/_enums/user-role.enum';
 
 @Component({
     selector: 'sp-data-explorer-data-view-toolbar',
@@ -28,7 +38,9 @@ import {
     styleUrls: ['../data-explorer-chart-view.component.scss'],
     standalone: false,
 })
-export class DataExplorerChartViewToolbarComponent {
+export class DataExplorerChartViewToolbarComponent implements OnInit {
+    private readonly currentUserService = inject(CurrentUserService);
+
     @Input()
     editMode = true;
 
@@ -54,4 +66,14 @@ export class DataExplorerChartViewToolbarComponent {
 
     @Output()
     downloadFileEmitter: EventEmitter<void> = new EventEmitter();
+
+    currentUser: UserInfo;
+    isAssetAdmin = false;
+
+    ngOnInit() {
+        this.currentUser = this.currentUserService.getCurrentUser();
+        this.isAssetAdmin = this.currentUserService.hasRole(
+            UserRole.ROLE_ASSET_ADMIN,
+        );
+    }
 }
diff --git 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
index cb88a730c0..f0898df853 100644
--- 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
+++ 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
@@ -122,24 +122,26 @@
     >
         Navigate to pipeline overview afterwards
     </mat-checkbox>
-    <mat-checkbox
-        [(ngModel)]="addToAssets"
-        color="accent"
-        data-cy="sp-show-pipeline-asset-checkbox"
-    >
-        Add Pipeline to Assets
-    </mat-checkbox>
-    @if (addToAssets) {
-        <div class="mt-10">
-            <sp-asset-link-configuration
-                [isEdit]="storageOptions.updateMode === 'update'"
-                [itemId]="pipeline._id"
-                (selectedAssetsChange)="onSelectedAssetsChange($event)"
-                (deselectedAssetsChange)="onDeselectedAssetsChange($event)"
-                (originalAssetsEmitter)="onOriginalAssetsEmitted($event)"
-            >
-            </sp-asset-link-configuration>
-        </div>
+    @if (isAssetAdmin) {
+        <mat-checkbox
+            [(ngModel)]="addToAssets"
+            color="accent"
+            data-cy="sp-show-pipeline-asset-checkbox"
+        >
+            Add Pipeline to Assets
+        </mat-checkbox>
+        @if (addToAssets) {
+            <div class="mt-10">
+                <sp-asset-link-configuration
+                    [isEdit]="storageOptions.updateMode === 'update'"
+                    [itemId]="pipeline._id"
+                    (selectedAssetsChange)="onSelectedAssetsChange($event)"
+                    (deselectedAssetsChange)="onDeselectedAssetsChange($event)"
+                    (originalAssetsEmitter)="onOriginalAssetsEmitted($event)"
+                >
+                </sp-asset-link-configuration>
+            </div>
+        }
     }
     <div class="mt-10">
         <mat-expansion-panel class="mat-elevation-z0 border-1">
diff --git 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
index b8b5284f16..f9cb65100b 100644
--- 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
+++ 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
@@ -19,6 +19,7 @@
 import {
     Component,
     EventEmitter,
+    Inject,
     inject,
     Input,
     OnInit,
@@ -35,9 +36,12 @@ import {
     Pipeline,
     PipelineService,
     SpAssetTreeNode,
+    UserInfo,
 } from '@streampipes/platform-services';
 import { PipelineStorageOptions } from '../../../model/editor.model';
 import { ValidateName } from 
'../../../../core-ui/static-properties/input.validator';
+import { CurrentUserService } from '@streampipes/shared-ui';
+import { UserRole } from 'src/app/_enums/user-role.enum';
 
 @Component({
     selector: 'sp-save-pipeline-settings',
@@ -46,6 +50,8 @@ import { ValidateName } from 
'../../../../core-ui/static-properties/input.valida
     standalone: false,
 })
 export class SavePipelineSettingsComponent implements OnInit {
+    private readonly currentUserService = inject(CurrentUserService);
+
     @Input()
     submitPipelineForm: UntypedFormGroup = new UntypedFormGroup({});
 
@@ -62,6 +68,8 @@ export class SavePipelineSettingsComponent implements OnInit {
     private pipelineService = inject(PipelineService);
 
     compactPipeline: CompactPipeline;
+    currentUser: UserInfo;
+    isAssetAdmin = false;
 
     addToAssets: boolean = false;
     @Input()
@@ -76,6 +84,10 @@ export class SavePipelineSettingsComponent implements OnInit 
{
     @Output() originalAssetsChange = new EventEmitter<SpAssetTreeNode[]>();
 
     ngOnInit() {
+        this.currentUser = this.currentUserService.getCurrentUser();
+        this.isAssetAdmin = this.currentUserService.hasRole(
+            UserRole.ROLE_ASSET_ADMIN,
+        );
         this.submitPipelineForm.addControl(
             'pipelineName',
             new UntypedFormControl(this.pipeline.name, [
diff --git 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline.component.html 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline.component.html
index 75ffc1fecb..2a1138adb7 100644
--- a/ui/src/app/editor/dialog/save-pipeline/save-pipeline.component.html
+++ b/ui/src/app/editor/dialog/save-pipeline/save-pipeline.component.html
@@ -88,6 +88,7 @@
             mat-flat-button
             class="mat-basic"
             (click)="hide(true)"
+            data-cy="sp-editor-cancel"
         >
             {{ 'Cancel' }}
         </button>


Reply via email to