From 51ce8c3e580c01c8c4014dc9ff40b0839472ac65 Mon Sep 17 00:00:00 2001 From: Philipp Zehnder Date: Mon, 16 Dec 2024 11:05:07 +0100 Subject: [PATCH] fix(#3372): WIP share adapter test --- .../management/AdapterMasterManagement.java | 7 +- .../rest/impl/connect/AdapterResource.java | 30 +++++++- .../support/utils/user/PermissionUtils.ts | 31 ++++++++ .../utils/userInput/StaticPropertyUtils.ts | 10 ++- .../enhancedAdapterDeletion.smoke.spec.ts | 43 +++++++++++ .../multiUser/adapterMultiUserSupport.ts | 43 ----------- ...leteAdapterWithMultipleUsers.smoke.spec.ts | 43 ----------- .../testUserRoleConnect.spec.ts | 72 +++++++++++++------ .../existing-adapters.component.html | 1 + .../object-permission-dialog.component.html | 8 ++- 10 files changed, 169 insertions(+), 119 deletions(-) create mode 100644 ui/cypress/support/utils/user/PermissionUtils.ts create mode 100644 ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts delete mode 100644 ui/cypress/tests/connect/multiUser/adapterMultiUserSupport.ts delete mode 100644 ui/cypress/tests/connect/multiUser/deleteAdapterWithMultipleUsers.smoke.spec.ts diff --git a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java index bdab68d34f..79310b96b1 100644 --- a/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java +++ b/streampipes-connect-management/src/main/java/org/apache/streampipes/connect/management/management/AdapterMasterManagement.java @@ -97,7 +97,7 @@ private void createDataStreamForAdapter( var storedDescription = new SourcesManagement() .createAdapterDataStream(adapterDescription, streamId); storedDescription.setCorrespondingAdapterId(adapterId); - installDataSource(storedDescription, principalSid, true); + installDataSource(storedDescription, principalSid); LOG.info("Install source (source URL: {} in backend", adapterDescription.getElementId()); } @@ -191,11 +191,10 @@ public void startStreamAdapter(String elementId) throws AdapterException { private void installDataSource( SpDataStream stream, - String principalSid, - boolean publicElement + String principalSid ) throws AdapterException { try { - new DataStreamVerifier(stream).verifyAndAdd(principalSid, publicElement); + new DataStreamVerifier(stream).verifyAndAdd(principalSid, false); } catch (SepaParseException e) { LOG.error("Error while installing data source: {}", stream.getElementId(), e); throw new AdapterException(); diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java index 44b9860f56..b0a7b2c8e6 100644 --- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java +++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java @@ -36,6 +36,7 @@ import org.apache.streampipes.resource.management.PermissionResourceManager; import org.apache.streampipes.resource.management.SpResourceManager; import org.apache.streampipes.rest.security.AuthConstants; +import org.apache.streampipes.rest.security.SpPermissionEvaluator; import org.apache.streampipes.rest.shared.constants.SpMediaType; import org.apache.streampipes.storage.api.IPipelineStorage; import org.apache.streampipes.storage.management.StorageDispatcher; @@ -136,7 +137,7 @@ public ResponseEntity> performPipelineMigrationPrefligh } @GetMapping(path = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, SpMediaType.YAML, SpMediaType.YML}) - @PreAuthorize("this.hasReadAuthority() and hasPermission('#elementId', 'READ')") + @PreAuthorize("this.hasReadAuthority()") public ResponseEntity getAdapter( @PathVariable("id") String elementId, @RequestParam(value = "output", @@ -145,7 +146,16 @@ public ResponseEntity getAdapter( ) { try { - AdapterDescription adapterDescription = getAdapterDescription(elementId); + var adapterDescription = getAdapterDescription(elementId); + + // This check is done here because the adapter permission is checked based on the corresponding data stream + // and not based on the element id + if (!checkAdapterReadPermission(adapterDescription)) { + LOG.error("User is not allowed to read adapter {}", elementId); + return ResponseEntity.status(HttpStatus.SC_UNAUTHORIZED) + .build(); + } + if (outputMode.equalsIgnoreCase("compact")) { return ok(toCompactAdapterDescription(adapterDescription)); } else { @@ -160,6 +170,20 @@ public ResponseEntity getAdapter( } } + /** + * Checks if the current user has the permission to read the adapter + */ + private boolean checkAdapterReadPermission(AdapterDescription adapterDescription) { + var spPermissionEvaluator = new SpPermissionEvaluator(); + var authentication = SecurityContextHolder.getContext() + .getAuthentication(); + return spPermissionEvaluator.hasPermission( + authentication, + adapterDescription.getCorrespondingDataStreamElementId(), + "READ" + ); + } + @PostMapping(path = "/{id}/stop", produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("this.hasWriteAuthority() and hasPermission('#elementId', 'WRITE')") public ResponseEntity stopAdapter(@PathVariable("id") String elementId) { @@ -269,7 +293,7 @@ public ResponseEntity deleteAdapter( @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("this.hasReadAuthority()") - @PostFilter("hasPermission(filterObject.elementId, 'READ')") + @PostFilter("hasPermission(filterObject.correspondingDataStreamElementId, 'READ')") public List getAllAdapters() { return managementService.getAllAdapterInstances(); } diff --git a/ui/cypress/support/utils/user/PermissionUtils.ts b/ui/cypress/support/utils/user/PermissionUtils.ts new file mode 100644 index 0000000000..f56ab3f7b9 --- /dev/null +++ b/ui/cypress/support/utils/user/PermissionUtils.ts @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { StaticPropertyUtils } from '../userInput/StaticPropertyUtils'; + +export class PermissionUtils { + public static openManagePermissions() { + cy.dataCy('open-manage-permissions').click(); + } + + public static markElementAsPublic() { + PermissionUtils.openManagePermissions(); + StaticPropertyUtils.clickCheckbox('permission-public-element'); + cy.dataCy('sp-manage-permissions-save').click(); + } +} diff --git a/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts index 34b84673f3..acc1a5eaf4 100644 --- a/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts +++ b/ui/cypress/support/utils/userInput/StaticPropertyUtils.ts @@ -24,7 +24,7 @@ export class StaticPropertyUtils { // Configure Properties configs.forEach(config => { if (config.type === 'checkbox') { - this.clickCheckbox(config); + this.clickCheckbox(config.selector); } else if (config.type === 'button') { cy.dataCy(config.selector, { timeout: 2000 }).click(); } else if (config.type === 'drop-down') { @@ -65,8 +65,12 @@ export class StaticPropertyUtils { }); } - private static clickCheckbox(input: UserInput) { - this.clickSelectionInput(input.selector, '.mdc-checkbox'); + /** + * This method can be used to check a mat checkbox + * @param selector + */ + public static clickCheckbox(selector: string) { + this.clickSelectionInput(selector, '.mdc-checkbox'); } private static clickRadio(input: UserInput) { diff --git a/ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts b/ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts new file mode 100644 index 0000000000..e10fd81873 --- /dev/null +++ b/ui/cypress/tests/connect/enhancedAdapterDeletion.smoke.spec.ts @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { ConnectUtils } from '../../support/utils/connect/ConnectUtils'; +import { UserUtils } from '../../support/utils/UserUtils'; +import { PipelineUtils } from '../../support/utils/pipeline/PipelineUtils'; + +describe('Test Enhanced Adapter Deletion', () => { + beforeEach('Setup Test', () => { + cy.initStreamPipesTest(); + UserUtils.addUser(UserUtils.userWithAdapterAndPipelineAdminRights); + }); + + it('Test Delete Adapter and Associated Pipelines', () => { + PipelineUtils.addSampleAdapterAndPipeline(); + + ConnectUtils.deleteAdapterAndAssociatedPipelines(); + }); + + it('Test Admin Should Be Able to Delete Adapter and Not Owned Associated Pipelines', () => { + // Let the user create the adapter and the pipeline + PipelineUtils.addSampleAdapterAndPipeline(); + + // Then let the admin delete them + UserUtils.switchUser(UserUtils.adminUser); + ConnectUtils.deleteAdapterAndAssociatedPipelines(true); + }); +}); diff --git a/ui/cypress/tests/connect/multiUser/adapterMultiUserSupport.ts b/ui/cypress/tests/connect/multiUser/adapterMultiUserSupport.ts deleted file mode 100644 index b2f4f80222..0000000000 --- a/ui/cypress/tests/connect/multiUser/adapterMultiUserSupport.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { ConnectUtils } from '../../../support/utils/connect/ConnectUtils'; -import { UserUtils } from '../../../support/utils/UserUtils'; -import { UserRole } from '../../../../src/app/_enums/user-role.enum'; - -const adapterName = 'simulator'; - -describe('Test Adapter Multi User support', () => { - beforeEach('Setup Test', () => { - cy.initStreamPipesTest(); - }); - - it('Test with connect admin account', () => { - const connectAdminUser = UserUtils.createUser( - 'user1', - UserRole.ROLE_CONNECT_ADMIN, - ); - - ConnectUtils.addMachineDataSimulator(adapterName, false); - - UserUtils.switchUser(connectAdminUser); - - // Validate that no adapter is visible - ConnectUtils.checkAmountOfAdapters(0); - }); -}); diff --git a/ui/cypress/tests/connect/multiUser/deleteAdapterWithMultipleUsers.smoke.spec.ts b/ui/cypress/tests/connect/multiUser/deleteAdapterWithMultipleUsers.smoke.spec.ts deleted file mode 100644 index 8fe22d42a0..0000000000 --- a/ui/cypress/tests/connect/multiUser/deleteAdapterWithMultipleUsers.smoke.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -import { ConnectUtils } from '../../../support/utils/connect/ConnectUtils'; -import { UserUtils } from '../../../support/utils/UserUtils'; -import { PipelineUtils } from '../../../support/utils/pipeline/PipelineUtils'; - -describe('Test Enhanced Adapter Deletion', () => { - beforeEach('Setup Test', () => { - cy.initStreamPipesTest(); - UserUtils.addUser(UserUtils.userWithAdapterAndPipelineAdminRights); - }); - - it('Test Delete Adapter and Associated Pipelines', () => { - PipelineUtils.addSampleAdapterAndPipeline(); - - ConnectUtils.deleteAdapterAndAssociatedPipelines(); - }); - - it('Test Admin Should Be Able to Delete Adapter and Not Owned Associated Pipelines', () => { - // Let the user create the adapter and the pipeline - PipelineUtils.addSampleAdapterAndPipeline(); - - // Then let the admin delete them - UserUtils.switchUser(UserUtils.adminUser); - ConnectUtils.deleteAdapterAndAssociatedPipelines(true); - }); -}); diff --git a/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts b/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts index 7202ebbdec..790f5c884d 100644 --- a/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts +++ b/ui/cypress/tests/userManagement/testUserRoleConnect.spec.ts @@ -15,40 +15,70 @@ * limitations under the License. * */ -import { UserBuilder } from '../../support/builder/UserBuilder'; import { UserRole } from '../../../src/app/_enums/user-role.enum'; import { UserUtils } from '../../support/utils/UserUtils'; import { ConnectUtils } from '../../support/utils/connect/ConnectUtils'; -import { ConnectBtns } from '../../support/utils/connect/ConnectBtns'; -import { GeneralUtils } from '../../support/utils/GeneralUtils'; +import { PermissionUtils } from '../../support/utils/user/PermissionUtils'; describe('Test User Roles for Connect', () => { beforeEach('Setup Test', () => { cy.initStreamPipesTest(); - ConnectUtils.addMachineDataSimulator('simulator'); }); - it('Perform Test', () => { - // Add connect admin user - const connect_admin = UserBuilder.create('user@streampipes.apache.org') - .setName('connect_admin') - .setPassword('password') - .addRole(UserRole.ROLE_CONNECT_ADMIN) - .build(); - UserUtils.addUser(connect_admin); + // it('Connect admin should not see adapters of other users', () => { + // const connectAdminUser = UserUtils.createUser( + // 'user', + // UserRole.ROLE_CONNECT_ADMIN, + // ); + // + // ConnectUtils.addMachineDataSimulator('simulator'); + // + // UserUtils.switchUser(connectAdminUser); + // + // GeneralUtils.validateAmountOfNavigationIcons(3); + // + // // Validate that no adapter is visible + // ConnectUtils.checkAmountOfAdapters(0); + // }); + + // it('Connect admin should see public adapters of other users', () => { + // const connectAdminUser = UserUtils.createUser( + // 'user', + // UserRole.ROLE_CONNECT_ADMIN, + // ); + // + // ConnectUtils.addMachineDataSimulator('simulator'); + // + // // Set adapter to public + // PermissionUtils.markElementAsPublic(); + // + // UserUtils.switchUser(connectAdminUser); + // + // GeneralUtils.validateAmountOfNavigationIcons(3); + // + // // Validate that adapter is visible + // ConnectUtils.checkAmountOfAdapters(1); + // }); - // Login as user and check if connect is visible to user - UserUtils.switchUser(connect_admin); + it('Connect admin should see shared adapters of other users', () => { + const connectAdminUser = UserUtils.createUser( + 'user', + UserRole.ROLE_CONNECT_ADMIN, + ); - GeneralUtils.validateAmountOfNavigationIcons(3); + ConnectUtils.addMachineDataSimulator('simulator'); - ConnectUtils.goToConnect(); + // Set adapter to public + PermissionUtils.openManagePermissions(); + cy.dataCy('authorized-user').type(`${connectAdminUser.email}{tab}`); - ConnectUtils.checkAmountOfAdapters(1); + // cy.dataCy('sp-manage-permissions-save').click(); - // validate that adapter can be stopped and edited - ConnectBtns.stopAdapter().click(); - ConnectBtns.editAdapter().should('not.be.disabled'); - ConnectBtns.editAdapter().click(); + // UserUtils.switchUser(connectAdminUser); + // + // GeneralUtils.validateAmountOfNavigationIcons(3); + // + // // Validate that adapter is visible + // ConnectUtils.checkAmountOfAdapters(1); }); }); diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html index 5c1b11994c..6147d2cf9c 100644 --- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html +++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html @@ -258,6 +258,7 @@
mat-icon-button matTooltip="Manage permissions" matTooltipPosition="above" + data-cy="open-manage-permissions" *ngIf="isAdmin" (click)="showPermissionsDialog(adapter)" > diff --git a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html index cf16da3273..7dda706b7e 100644 --- a/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html +++ b/ui/src/app/core-ui/object-permission-dialog/object-permission-dialog.component.html @@ -33,7 +33,10 @@

{{ headerTitle }}

> - + Public Element @@ -66,6 +69,7 @@

{{ headerTitle }}

[matChipInputSeparatorKeyCodes]=" separatorKeysCodes " + data-cy="authorized-user" (matChipInputTokenEnd)="addUser($event)" /> @@ -140,7 +144,7 @@

{{ headerTitle }}

(click)="save()" style="margin-right: 10px" [disabled]="!parentForm.valid" - data-cy="sp-element-edit-user-save" + data-cy="sp-manage-permissions-save" > save Save