From 911824f0ac8f69f1b120164f8d2500146b9813e3 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Fri, 15 Mar 2019 14:43:59 +0100 Subject: [PATCH 01/16] Added Camunda authentication context to all rest controller This allows Camunda to perform the action behind the controller as the user and enforce and limit the action based on the users permissions --- ...gurer.java => CustomWebMvcConfigurer.java} | 19 ++++- .../listener/ListenerRegistrarPlugin.java | 6 ++ .../engine/rest/ScanJobResource.java | 29 +++++-- .../rest/SecurityTestDefinitionResource.java | 8 ++ .../engine/rest/SecurityTestResource.java | 16 +++- .../engine/service/AuthService.java | 38 ++++++++- .../tenancy/CustomTenantIdProvider.java | 84 +++++++++++++++++++ 7 files changed, 187 insertions(+), 13 deletions(-) rename scb-engine/src/main/java/io/securecodebox/engine/{WebMvcConfigurer.java => CustomWebMvcConfigurer.java} (67%) create mode 100644 scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java diff --git a/scb-engine/src/main/java/io/securecodebox/engine/WebMvcConfigurer.java b/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java similarity index 67% rename from scb-engine/src/main/java/io/securecodebox/engine/WebMvcConfigurer.java rename to scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java index 8f15a32b..655c43ce 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/WebMvcConfigurer.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java @@ -19,9 +19,18 @@ package io.securecodebox.engine; +import io.securecodebox.engine.service.AuthService; +import org.camunda.bpm.engine.IdentityService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * This class adds additional resources to the spring application. @@ -31,7 +40,13 @@ * @since 02.03.18 */ @Configuration -public class WebMvcConfigurer extends WebMvcConfigurerAdapter { +public class CustomWebMvcConfigurer implements WebMvcConfigurer { + @Autowired + IdentityService identityService; + + @Autowired + AuthService authService; + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/forms/**").addResourceLocations("classpath:/forms/"); diff --git a/scb-engine/src/main/java/io/securecodebox/engine/listener/ListenerRegistrarPlugin.java b/scb-engine/src/main/java/io/securecodebox/engine/listener/ListenerRegistrarPlugin.java index a635e024..110f4c4e 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/listener/ListenerRegistrarPlugin.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/listener/ListenerRegistrarPlugin.java @@ -19,6 +19,7 @@ package io.securecodebox.engine.listener; +import io.securecodebox.engine.tenancy.CustomTenantIdProvider; import org.camunda.bpm.engine.impl.bpmn.parser.BpmnParseListener; import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin; import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl; @@ -38,9 +39,14 @@ public class ListenerRegistrarPlugin extends AbstractProcessEnginePlugin { @Autowired DefaultListenerRegistrar registrar; + @Autowired + CustomTenantIdProvider tenantIdProvider; + @Override public void preInit(ProcessEngineConfigurationImpl processEngineConfiguration) { + processEngineConfiguration.setTenantIdProvider(tenantIdProvider); + // get all existing preParseListeners List preParseListeners = processEngineConfiguration.getCustomPreBPMNParseListeners(); diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java index 31fa79a0..90a312ee 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java @@ -36,6 +36,7 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.Authorization; +import org.camunda.bpm.engine.IdentityService; import org.camunda.bpm.engine.ProcessEngine; import org.camunda.bpm.engine.exception.NotFoundException; import org.camunda.bpm.engine.externaltask.ExternalTask; @@ -75,6 +76,9 @@ public class ScanJobResource { @Autowired ProcessEngine engine; + @Autowired + IdentityService identityService; + @Autowired AuthService authService; @@ -117,12 +121,14 @@ public ResponseEntity lockJob( ) @PathVariable UUID scannerId ) { - try{ + try { authService.checkAuthorizedFor(ResourceType.SECURITY_TEST, PermissionType.READ); - }catch (InsufficientAuthenticationException e){ + } catch (InsufficientAuthenticationException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } + identityService.setAuthentication(authService.getAuthentication()); + ExternalTaskQueryBuilder externalTaskQueryBuilder = engine.getExternalTaskService() .fetchAndLock(1, scannerId.toString()); externalTaskQueryBuilder.topic(topic, LOCK_DURATION_MS); @@ -132,8 +138,10 @@ public ResponseEntity lockJob( ScanConfiguration config = new ScanConfiguration(); config.setJobId(UUID.fromString(result.getId())); config.setTargets(getVariableListFromJsonField(result, DefaultFields.PROCESS_TARGETS, Target.class)); + identityService.clearAuthentication(); return ResponseEntity.ok(config); } else { + identityService.clearAuthentication(); return ResponseEntity.noContent().build(); } } @@ -167,9 +175,9 @@ public ResponseEntity completeJob( @PathVariable UUID id, @Valid @RequestBody ScanResult result ) { - try{ + try { authService.checkAuthorizedFor(id.toString(), ResourceType.SECURITY_TEST, PermissionType.UPDATE); - } catch (InsufficientAuthenticationException e){ + } catch (InsufficientAuthenticationException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } @@ -188,7 +196,11 @@ public ResponseEntity completeJob( } try { + identityService.setAuthentication(authService.getAuthentication()); + engine.getExternalTaskService().complete(id.toString(), result.getScannerId().toString(), variables); + + identityService.clearAuthentication(); } catch (NotFoundException e) { LOG.info("Can not find taskId {}", id, e); return ResponseEntity.notFound().build(); @@ -227,9 +239,9 @@ public ResponseEntity failJob( @PathVariable UUID id, @Valid @RequestBody ScanFailure result ) { - try{ + try { authService.checkAuthorizedFor(id.toString(), ResourceType.SECURITY_TEST, PermissionType.UPDATE); - }catch (InsufficientAuthenticationException e){ + } catch (InsufficientAuthenticationException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } @@ -250,9 +262,14 @@ public ResponseEntity failJob( retriesLeft = externalTask.getRetries() - 1; } + identityService.setAuthentication(authService.getAuthentication()); + engine.getExternalTaskService() .handleFailure(id.toString(), result.getScannerId().toString(), result.getErrorMessage(), result.getErrorDetails(), retriesLeft, 1000); + + identityService.clearAuthentication(); + return ResponseEntity.ok().build(); } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java index d08aae61..98b731f9 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java @@ -28,6 +28,7 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.Authorization; +import org.camunda.bpm.engine.IdentityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -52,6 +53,9 @@ public class SecurityTestDefinitionResource { @Autowired AuthService authService; + @Autowired + IdentityService identityService; + @ApiOperation(value = "Lists all available securityTest definitions.", authorizations = { @Authorization(value="basicAuth") @@ -81,6 +85,8 @@ public class SecurityTestDefinitionResource { }) @RequestMapping(method = RequestMethod.GET) public ResponseEntity> getSecurityTestDefinitions(){ + identityService.setAuthentication(authService.getAuthentication()); + try { authService.checkAuthorizedFor(ResourceType.SECURITY_TEST_DEFINITION, PermissionType.READ); }catch (InsufficientAuthorizationException e){ @@ -89,6 +95,8 @@ public ResponseEntity> getSecurityTestDefinitions(){ List securityTests = securityTestService.getAvailableSecurityTestDefinitionNames(); + identityService.clearAuthentication(); + return ResponseEntity.ok(securityTests); } } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java index d9e44004..2d3fe783 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java @@ -32,6 +32,7 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.Authorization; +import org.camunda.bpm.engine.IdentityService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -59,6 +60,9 @@ public class SecurityTestResource { @Autowired AuthService authService; + @Autowired + IdentityService identityService; + @Autowired SecurityTestService securityTestService; @@ -139,12 +143,16 @@ public ResponseEntity> startSecurityTests( } } + identityService.setAuthentication(authService.getAuthentication()); + List processInstances = new LinkedList<>(); for (SecurityTestConfiguration securityTest : securityTests) { processInstances.add(securityTestService.startSecurityTest(securityTest)); } + identityService.clearAuthentication(); + return ResponseEntity.status(HttpStatus.CREATED).body(processInstances); } @@ -204,17 +212,23 @@ public ResponseEntity getSecurityTest( } try { + identityService.setAuthentication(authService.getAuthentication()); + SecurityTest securityTest = securityTestService.getCompletedSecurityTest(id); if (securityTest.isFinished()) { return ResponseEntity.status(HttpStatus.OK).body(securityTest); } - return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(securityTest); + identityService.clearAuthentication(); + + return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(securityTest); } catch (SecurityTestService.SecurityTestNotFoundException e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } catch (SecurityTestService.SecurityTestErroredException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } + + } } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java b/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java index 7101bc90..20453032 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java @@ -22,6 +22,7 @@ import io.securecodebox.engine.model.PermissionType; import io.securecodebox.engine.model.ResourceType; import org.camunda.bpm.engine.identity.Group; +import org.camunda.bpm.engine.identity.Tenant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; @@ -46,13 +47,13 @@ public class AuthService { private String authType; public void checkAuthorizedFor(String resourceId, ResourceType resource, PermissionType permission) throws InsufficientAuthorizationException { - if(AUTH_DISABLED_TYPE.equals(authType)){ + if (AUTH_DISABLED_TYPE.equals(authType)) { return; } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if(authentication == null) { + if (authentication == null) { throw new InsufficientAuthorizationException("No authentication provided."); } @@ -66,7 +67,7 @@ public void checkAuthorizedFor(String resourceId, ResourceType resource, Permiss .collect(Collectors.toList()); boolean isAuthorized = false; - if(resourceId == null){ + if (resourceId == null) { isAuthorized = engine.getAuthorizationService().isUserAuthorized( authentication.getName(), groups, @@ -86,7 +87,7 @@ public void checkAuthorizedFor(String resourceId, ResourceType resource, Permiss LOG.trace("Current User '{}' with groups: '{}'", authentication.getName(), groups); LOG.trace("Access check for [{}, {}, {}]: {}", resourceId, resource, permission, isAuthorized); - if(!isAuthorized){ + if (!isAuthorized) { throw new InsufficientAuthorizationException("User is not authorised to perform this action."); } } @@ -94,4 +95,33 @@ public void checkAuthorizedFor(String resourceId, ResourceType resource, Permiss public void checkAuthorizedFor(ResourceType resource, PermissionType permission) throws InsufficientAuthorizationException { this.checkAuthorizedFor(null, resource, permission); } + + public org.camunda.bpm.engine.impl.identity.Authentication getAuthentication() { + String userId = SecurityContextHolder.getContext().getAuthentication().getName(); + + List groups = engine + .getIdentityService() + .createGroupQuery() + .groupMember(userId) + .list() + .stream() + .map(Group::getId) + .collect(Collectors.toList()); + + List tenants = engine + .getIdentityService() + .createTenantQuery() + .userMember(userId) + .list() + .stream() + .map(Tenant::getId) + .collect(Collectors.toList()); + + return new org.camunda.bpm.engine.impl.identity.Authentication( + userId, + groups, + tenants + ); + + } } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java b/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java new file mode 100644 index 00000000..8ab688b9 --- /dev/null +++ b/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java @@ -0,0 +1,84 @@ +/* + * + * SecureCodeBox (SCB) + * Copyright 2015-2018 iteratec GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ +package io.securecodebox.engine.tenancy; + +import org.camunda.bpm.engine.IdentityService; +import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProvider; +import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProviderCaseInstanceContext; +import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProviderHistoricDecisionInstanceContext; +import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProviderProcessInstanceContext; +import org.camunda.bpm.engine.impl.context.Context; +import org.camunda.bpm.engine.impl.identity.Authentication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class CustomTenantIdProvider implements TenantIdProvider { + private static final Logger LOG = LoggerFactory.getLogger(CustomTenantIdProvider.class); + + CustomTenantIdProvider (){ + super(); + LOG.debug("Init CustomTenantIdProvider"); + } + + @Override + public String provideTenantIdForProcessInstance(TenantIdProviderProcessInstanceContext ctx) { + return getTenantIdOfCurrentAuthentication(); + } + + @Override + public String provideTenantIdForCaseInstance(TenantIdProviderCaseInstanceContext ctx) { + return getTenantIdOfCurrentAuthentication(); + } + + @Override + public String provideTenantIdForHistoricDecisionInstance(TenantIdProviderHistoricDecisionInstanceContext ctx) { + return getTenantIdOfCurrentAuthentication(); + } + + protected String getTenantIdOfCurrentAuthentication() { + + IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService(); + Authentication currentAuthentication = identityService.getCurrentAuthentication(); + + + if (currentAuthentication != null) { + LOG.debug("Fetching Tenant for user ${}", currentAuthentication.getUserId()); + + List tenantIds = currentAuthentication.getTenantIds(); + if (tenantIds.size() == 1) { + + return tenantIds.get(0); + + } else if (tenantIds.isEmpty()) { + throw new IllegalStateException("no authenticated tenant"); + + } else { + throw new IllegalStateException("more than one authenticated tenant"); + } + + } else { + throw new IllegalStateException("no authentication"); + } + } + +} From 6ab8a8cc3461517201b73898f48848f928ecb24a Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 27 Mar 2019 12:02:34 +0100 Subject: [PATCH 02/16] Removed unused services --- .../java/io/securecodebox/engine/CustomWebMvcConfigurer.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java b/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java index 655c43ce..50e5eeee 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java @@ -41,11 +41,6 @@ */ @Configuration public class CustomWebMvcConfigurer implements WebMvcConfigurer { - @Autowired - IdentityService identityService; - - @Autowired - AuthService authService; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { From 8cc99a525b48a69eacda6debf28bf5d1a6da2c9a Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 27 Mar 2019 12:03:11 +0100 Subject: [PATCH 03/16] Removed manual context setup from controllers --- .../engine/rest/ScanJobResource.java | 16 ---------------- .../rest/SecurityTestDefinitionResource.java | 4 ---- .../engine/rest/SecurityTestResource.java | 4 ---- 3 files changed, 24 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java index 90a312ee..f82a36c6 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java @@ -76,9 +76,6 @@ public class ScanJobResource { @Autowired ProcessEngine engine; - @Autowired - IdentityService identityService; - @Autowired AuthService authService; @@ -126,9 +123,6 @@ public ResponseEntity lockJob( } catch (InsufficientAuthenticationException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } - - identityService.setAuthentication(authService.getAuthentication()); - ExternalTaskQueryBuilder externalTaskQueryBuilder = engine.getExternalTaskService() .fetchAndLock(1, scannerId.toString()); externalTaskQueryBuilder.topic(topic, LOCK_DURATION_MS); @@ -138,10 +132,8 @@ public ResponseEntity lockJob( ScanConfiguration config = new ScanConfiguration(); config.setJobId(UUID.fromString(result.getId())); config.setTargets(getVariableListFromJsonField(result, DefaultFields.PROCESS_TARGETS, Target.class)); - identityService.clearAuthentication(); return ResponseEntity.ok(config); } else { - identityService.clearAuthentication(); return ResponseEntity.noContent().build(); } } @@ -196,11 +188,7 @@ public ResponseEntity completeJob( } try { - identityService.setAuthentication(authService.getAuthentication()); - engine.getExternalTaskService().complete(id.toString(), result.getScannerId().toString(), variables); - - identityService.clearAuthentication(); } catch (NotFoundException e) { LOG.info("Can not find taskId {}", id, e); return ResponseEntity.notFound().build(); @@ -262,14 +250,10 @@ public ResponseEntity failJob( retriesLeft = externalTask.getRetries() - 1; } - identityService.setAuthentication(authService.getAuthentication()); - engine.getExternalTaskService() .handleFailure(id.toString(), result.getScannerId().toString(), result.getErrorMessage(), result.getErrorDetails(), retriesLeft, 1000); - identityService.clearAuthentication(); - return ResponseEntity.ok().build(); } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java index 98b731f9..832bfd9b 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java @@ -85,8 +85,6 @@ public class SecurityTestDefinitionResource { }) @RequestMapping(method = RequestMethod.GET) public ResponseEntity> getSecurityTestDefinitions(){ - identityService.setAuthentication(authService.getAuthentication()); - try { authService.checkAuthorizedFor(ResourceType.SECURITY_TEST_DEFINITION, PermissionType.READ); }catch (InsufficientAuthorizationException e){ @@ -95,8 +93,6 @@ public ResponseEntity> getSecurityTestDefinitions(){ List securityTests = securityTestService.getAvailableSecurityTestDefinitionNames(); - identityService.clearAuthentication(); - return ResponseEntity.ok(securityTests); } } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java index 2d3fe783..8526ff5c 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java @@ -143,16 +143,12 @@ public ResponseEntity> startSecurityTests( } } - identityService.setAuthentication(authService.getAuthentication()); - List processInstances = new LinkedList<>(); for (SecurityTestConfiguration securityTest : securityTests) { processInstances.add(securityTestService.startSecurityTest(securityTest)); } - identityService.clearAuthentication(); - return ResponseEntity.status(HttpStatus.CREATED).body(processInstances); } From 1b6e9e8b2f5be53a2afaf386d3568c9b0e5fd50b Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 27 Mar 2019 12:54:39 +0100 Subject: [PATCH 04/16] Added Camunda auth context filter --- .../engine/auth/CamundaAuthContextSetup.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java diff --git a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java new file mode 100644 index 00000000..4c817fed --- /dev/null +++ b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java @@ -0,0 +1,73 @@ +/* + * + * SecureCodeBox (SCB) + * Copyright 2015-2018 iteratec GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ +package io.securecodebox.engine.auth; + +import io.securecodebox.engine.service.AuthService; +import org.camunda.bpm.engine.IdentityService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.web.filter.GenericFilterBean; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; + +@Configuration +@EnableWebSecurity +@Order(1231231) +public class CamundaAuthContextSetup extends WebSecurityConfigurerAdapter { + private static final Logger LOG = LoggerFactory.getLogger(CamundaAuthContextSetup.class); + + @Autowired + IdentityService identityService; + + @Autowired + private AuthService authService; + + @Override + public void configure(HttpSecurity http) { + LOG.debug("Init: Camunda Auth Contex Setup Filter"); + http.antMatcher("/box/**").addFilterAfter(new CamundaAuthContextSetupFilter(), FilterSecurityInterceptor.class); + } + + /** + * Sets up the Camunda Authentication Context before the Resource gets executed + */ + private class CamundaAuthContextSetupFilter extends GenericFilterBean { + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + LOG.debug("Setting up Camunda Auth Context for request"); + + identityService.clearAuthentication(); + + identityService.setAuthentication(authService.getAuthentication()); + + filterChain.doFilter(servletRequest, servletResponse); + } + } +} From 69b2cf18495e453904c8d145b55f8590b68d297b Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 27 Mar 2019 14:17:52 +0100 Subject: [PATCH 05/16] Added field to specify as which tenant a securityTest gets started --- .../engine/service/SecurityTestService.java | 4 ++ .../tenancy/CustomTenantIdProvider.java | 50 +++++++++++-------- .../constants/DefaultFields.java | 3 +- .../securitytest/AbstractSecurityTest.java | 18 ++++++- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java b/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java index 12916df8..e5d9e451 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java @@ -77,6 +77,10 @@ public UUID startSecurityTest(SecurityTestConfiguration securityTest){ values.put(DefaultFields.PROCESS_TARGETS.name(), ProcessVariableHelper.generateObjectValue(targets)); values.put(DefaultFields.PROCESS_META_DATA.name(), securityTest.getMetaData()); + if(securityTest.getTenant() != null){ + values.put(DefaultFields.PROCESS_TENANT.name(), securityTest.getTenant()); + } + ProcessInstance instance = engine.getRuntimeService().startProcessInstanceByKey(securityTest.getProcessDefinitionKey(), values); return UUID.fromString(instance.getProcessInstanceId()); } diff --git a/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java b/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java index 8ab688b9..f0e320ce 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java @@ -18,6 +18,8 @@ */ package io.securecodebox.engine.tenancy; +import io.securecodebox.constants.DefaultFields; +import io.securecodebox.engine.auth.InsufficientAuthorizationException; import org.camunda.bpm.engine.IdentityService; import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProvider; import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProviderCaseInstanceContext; @@ -25,12 +27,11 @@ import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProviderProcessInstanceContext; import org.camunda.bpm.engine.impl.context.Context; import org.camunda.bpm.engine.impl.identity.Authentication; +import org.camunda.bpm.engine.variable.VariableMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; -import java.util.List; - @Component public class CustomTenantIdProvider implements TenantIdProvider { private static final Logger LOG = LoggerFactory.getLogger(CustomTenantIdProvider.class); @@ -42,42 +43,51 @@ public class CustomTenantIdProvider implements TenantIdProvider { @Override public String provideTenantIdForProcessInstance(TenantIdProviderProcessInstanceContext ctx) { - return getTenantIdOfCurrentAuthentication(); + return getTenantIdOfCurrentAuthentication(ctx.getVariables()); } @Override public String provideTenantIdForCaseInstance(TenantIdProviderCaseInstanceContext ctx) { - return getTenantIdOfCurrentAuthentication(); + return getTenantIdOfCurrentAuthentication(ctx.getVariables()); } @Override public String provideTenantIdForHistoricDecisionInstance(TenantIdProviderHistoricDecisionInstanceContext ctx) { - return getTenantIdOfCurrentAuthentication(); + return getTenantIdOfCurrentAuthentication(ctx.getExecution().getVariablesTyped()); } - protected String getTenantIdOfCurrentAuthentication() { - - IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService(); - Authentication currentAuthentication = identityService.getCurrentAuthentication(); + protected String getTenantIdOfCurrentAuthentication(VariableMap variableMap) { + LOG.debug("Determining if process should be started with a tenant."); + // Process doesn't have tenant variable -> was most likely started via camunda ui + if(!variableMap.containsKey(DefaultFields.PROCESS_TENANT.name())){ + LOG.debug("Process started without tenant variable -> Process will not be associated with a tenant"); + return null; + } - if (currentAuthentication != null) { - LOG.debug("Fetching Tenant for user ${}", currentAuthentication.getUserId()); + String specifiedTenant = variableMap.getValue(DefaultFields.PROCESS_TENANT.name(), String.class); - List tenantIds = currentAuthentication.getTenantIds(); - if (tenantIds.size() == 1) { + // Tenant Id was left empty or was explicitly set to null -> start without tenant + if(specifiedTenant == null || specifiedTenant.equals("")){ + LOG.debug("Tenant field in target was not specified or set to null -> Process will not be associated with a tenant"); + return null; + } - return tenantIds.get(0); + IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService(); + Authentication currentAuthentication = identityService.getCurrentAuthentication(); - } else if (tenantIds.isEmpty()) { - throw new IllegalStateException("no authenticated tenant"); + if (currentAuthentication == null) { + throw new IllegalStateException("no authentication"); + } - } else { - throw new IllegalStateException("more than one authenticated tenant"); - } + boolean userIsMemberOfTenant = currentAuthentication.getTenantIds().stream().anyMatch(tenant -> tenant.equals(specifiedTenant)); + if(userIsMemberOfTenant){ + LOG.debug("Process started with tenant and user is member of tenant -> Process will be started with tenant '{}'", specifiedTenant); + return specifiedTenant; } else { - throw new IllegalStateException("no authentication"); + LOG.debug("Process started with tenant, BUT user is NOT a member of the tenant -> Process will crash"); + throw new InsufficientAuthorizationException("User is not a member of the specified Tenant"); } } diff --git a/scb-sdk/src/main/java/io/securecodebox/constants/DefaultFields.java b/scb-sdk/src/main/java/io/securecodebox/constants/DefaultFields.java index 1f1af1f6..f812784d 100644 --- a/scb-sdk/src/main/java/io/securecodebox/constants/DefaultFields.java +++ b/scb-sdk/src/main/java/io/securecodebox/constants/DefaultFields.java @@ -27,5 +27,6 @@ public enum DefaultFields { PROCESS_CONTEXT, PROCESS_SCANNER_ID, PROCESS_SCANNER_TYPE, PROCESS_AUTOMATED, PROCESS_FINDINGS, PROCESS_RAW_FINDINGS, PROCESS_SCANNERS, PROCESS_TARGETS, PROCESS_REPORT, - PROCESS_RESULT_APPROVED, PROCESS_ATTRIBUTE_MAPPING, PROCESS_NAME, PROCESS_META_DATA + PROCESS_RESULT_APPROVED, PROCESS_ATTRIBUTE_MAPPING, PROCESS_NAME, PROCESS_META_DATA, + PROCESS_TENANT } diff --git a/scb-sdk/src/main/java/io/securecodebox/model/securitytest/AbstractSecurityTest.java b/scb-sdk/src/main/java/io/securecodebox/model/securitytest/AbstractSecurityTest.java index c64e72d1..a1eed49a 100644 --- a/scb-sdk/src/main/java/io/securecodebox/model/securitytest/AbstractSecurityTest.java +++ b/scb-sdk/src/main/java/io/securecodebox/model/securitytest/AbstractSecurityTest.java @@ -43,7 +43,15 @@ public abstract class AbstractSecurityTest { Target target; @JsonProperty - private Map metaData; + @ApiModelProperty( + value = "A tenant is a camunda concept. A tenant can have both users and groups. It can be used to restrict the access to your security tests to members of the tenant.", + allowEmptyValue = true, + example = "team-1" + ) + String tenant; + + @JsonProperty + private Map metaData; public String getContext() { return context; @@ -76,4 +84,12 @@ public Map getMetaData() { public void setMetaData(Map metaData) { this.metaData = metaData; } + + public String getTenant() { + return tenant; + } + + public void setTenant(String tenant) { + this.tenant = tenant; + } } From 62180f01c8f3a8c4200ba8d9c57de4382c162826 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 27 Mar 2019 15:11:49 +0100 Subject: [PATCH 06/16] Use spring security context for authentication --- .../securecodebox/engine/rest/SecurityTestResource.java | 9 +++++++-- .../io/securecodebox/engine/service/AuthService.java | 6 +++++- .../engine/service/SecurityTestService.java | 3 ++- .../engine/tenancy/CustomTenantIdProvider.java | 8 +++++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java index 8526ff5c..e74a877a 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java @@ -43,6 +43,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpClientErrorException; import javax.validation.Valid; import java.util.*; @@ -145,8 +146,12 @@ public ResponseEntity> startSecurityTests( List processInstances = new LinkedList<>(); - for (SecurityTestConfiguration securityTest : securityTests) { - processInstances.add(securityTestService.startSecurityTest(securityTest)); + try { + for (SecurityTestConfiguration securityTest : securityTests) { + processInstances.add(securityTestService.startSecurityTest(securityTest)); + } + } catch (InsufficientAuthorizationException e){ + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } return ResponseEntity.status(HttpStatus.CREATED).body(processInstances); diff --git a/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java b/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java index 20453032..a6c7a9e9 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/service/AuthService.java @@ -40,12 +40,16 @@ public class AuthService { private static String AUTH_DISABLED_TYPE = "none"; private static final Logger LOG = LoggerFactory.getLogger(AuthService.class); - @Autowired ProcessEngine engine; @Value("${securecodebox.rest.auth}") private String authType; + @Autowired + public AuthService(ProcessEngine engine){ + this.engine = engine; + } + public void checkAuthorizedFor(String resourceId, ResourceType resource, PermissionType permission) throws InsufficientAuthorizationException { if (AUTH_DISABLED_TYPE.equals(authType)) { return; diff --git a/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java b/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java index e5d9e451..ddc5a439 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java @@ -19,6 +19,7 @@ package io.securecodebox.engine.service; import io.securecodebox.constants.DefaultFields; +import io.securecodebox.engine.auth.InsufficientAuthorizationException; import io.securecodebox.model.execution.Target; import io.securecodebox.model.findings.Finding; import io.securecodebox.model.rest.Report; @@ -65,7 +66,7 @@ public void checkSecurityTestDefinitionExistence(String key) throws NonExistentS } } - public UUID startSecurityTest(SecurityTestConfiguration securityTest){ + public UUID startSecurityTest(SecurityTestConfiguration securityTest) throws InsufficientAuthorizationException { Map values = new HashMap<>(); List targets = new LinkedList<>(); diff --git a/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java b/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java index f0e320ce..b8b0fed0 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/tenancy/CustomTenantIdProvider.java @@ -20,6 +20,7 @@ import io.securecodebox.constants.DefaultFields; import io.securecodebox.engine.auth.InsufficientAuthorizationException; +import io.securecodebox.engine.service.AuthService; import org.camunda.bpm.engine.IdentityService; import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProvider; import org.camunda.bpm.engine.impl.cfg.multitenancy.TenantIdProviderCaseInstanceContext; @@ -30,6 +31,7 @@ import org.camunda.bpm.engine.variable.VariableMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component @@ -73,11 +75,11 @@ protected String getTenantIdOfCurrentAuthentication(VariableMap variableMap) { return null; } - IdentityService identityService = Context.getProcessEngineConfiguration().getIdentityService(); - Authentication currentAuthentication = identityService.getCurrentAuthentication(); + AuthService authService = new AuthService(Context.getProcessEngineConfiguration().getProcessEngine()); + Authentication currentAuthentication = authService.getAuthentication(); if (currentAuthentication == null) { - throw new IllegalStateException("no authentication"); + throw new InsufficientAuthorizationException("No authenticated user"); } boolean userIsMemberOfTenant = currentAuthentication.getTenantIds().stream().anyMatch(tenant -> tenant.equals(specifiedTenant)); From 144e06fbcca6a70150f87b5997759f9403656f72 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 10 Apr 2019 11:14:12 +0200 Subject: [PATCH 07/16] Auth context now gets deleted in the spring filter destroy method --- .../engine/auth/CamundaAuthContextSetup.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java index 4c817fed..74209c43 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java @@ -51,23 +51,28 @@ public class CamundaAuthContextSetup extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) { - LOG.debug("Init: Camunda Auth Contex Setup Filter"); + LOG.debug("Init: Camunda Auth Context Setup Filter"); http.antMatcher("/box/**").addFilterAfter(new CamundaAuthContextSetupFilter(), FilterSecurityInterceptor.class); } - /** - * Sets up the Camunda Authentication Context before the Resource gets executed - */ private class CamundaAuthContextSetupFilter extends GenericFilterBean { + + /** + * Sets up the Camunda Authentication Context before the Resource gets executed + */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - LOG.debug("Setting up Camunda Auth Context for request"); - - identityService.clearAuthentication(); - identityService.setAuthentication(authService.getAuthentication()); filterChain.doFilter(servletRequest, servletResponse); } + + /** + * Tears down the Camunda Authentication Context after the Resource got executed + */ + @Override + public void destroy(){ + identityService.clearAuthentication(); + } } } From 94186a8e9592b71cfcf399edd7fc1b60ccc41065 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 10 Apr 2019 11:24:13 +0200 Subject: [PATCH 08/16] Removed manual auth context setup --- .../io/securecodebox/engine/rest/SecurityTestResource.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java index e74a877a..bfe14c58 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java @@ -213,23 +213,17 @@ public ResponseEntity getSecurityTest( } try { - identityService.setAuthentication(authService.getAuthentication()); - SecurityTest securityTest = securityTestService.getCompletedSecurityTest(id); if (securityTest.isFinished()) { return ResponseEntity.status(HttpStatus.OK).body(securityTest); } - identityService.clearAuthentication(); - return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(securityTest); } catch (SecurityTestService.SecurityTestNotFoundException e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); } catch (SecurityTestService.SecurityTestErroredException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } - - } } From b6cef34450e6ff19383d10252d363b6b308d1992 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 10 Apr 2019 11:24:37 +0200 Subject: [PATCH 09/16] Disable auth context filter in test profile --- .../io/securecodebox/engine/auth/CamundaAuthContextSetup.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java index 74209c43..861168ee 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java @@ -23,6 +23,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -40,6 +41,7 @@ @Configuration @EnableWebSecurity @Order(1231231) +@ConditionalOnProperty(name = "securecodebox.rest.auth", havingValue = "basic auth") public class CamundaAuthContextSetup extends WebSecurityConfigurerAdapter { private static final Logger LOG = LoggerFactory.getLogger(CamundaAuthContextSetup.class); From 182cf116f16b3a3c8111aa876447e1147b77cb31 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 10 Apr 2019 13:12:08 +0200 Subject: [PATCH 10/16] Removed unnecessary imports --- .../engine/rest/SecurityTestDefinitionResource.java | 4 ---- .../io/securecodebox/engine/rest/SecurityTestResource.java | 5 ----- 2 files changed, 9 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java index 832bfd9b..d08aae61 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestDefinitionResource.java @@ -28,7 +28,6 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.Authorization; -import org.camunda.bpm.engine.IdentityService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -53,9 +52,6 @@ public class SecurityTestDefinitionResource { @Autowired AuthService authService; - @Autowired - IdentityService identityService; - @ApiOperation(value = "Lists all available securityTest definitions.", authorizations = { @Authorization(value="basicAuth") diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java index bfe14c58..cfa7d900 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/SecurityTestResource.java @@ -32,7 +32,6 @@ import io.swagger.annotations.ApiResponse; import io.swagger.annotations.ApiResponses; import io.swagger.annotations.Authorization; -import org.camunda.bpm.engine.IdentityService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -43,7 +42,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.HttpClientErrorException; import javax.validation.Valid; import java.util.*; @@ -61,9 +59,6 @@ public class SecurityTestResource { @Autowired AuthService authService; - @Autowired - IdentityService identityService; - @Autowired SecurityTestService securityTestService; From 6e2c32c728168dba4aaba0eeab42ed0ede9b60f3 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 10 Apr 2019 13:44:30 +0200 Subject: [PATCH 11/16] Properly returning the tenant id in the securityTest result endpoint --- .../securecodebox/engine/service/SecurityTestService.java | 7 ++++++- .../io/securecodebox/model/securitytest/SecurityTest.java | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java b/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java index ddc5a439..37424a15 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/service/SecurityTestService.java @@ -135,10 +135,15 @@ public SecurityTest getCompletedSecurityTest(UUID id) throws SecurityTestNotFoun String context = (String) variables.get(DefaultFields.PROCESS_CONTEXT.name()).getValue(); String name = (String) variables.get(DefaultFields.PROCESS_NAME.name()).getValue(); + String tenant = null; + if(variables.containsKey(DefaultFields.PROCESS_TENANT.name())){ + tenant = (String) variables.get(DefaultFields.PROCESS_TENANT.name()).getValue(); + } + List targets = getListValue(variables, DefaultFields.PROCESS_TARGETS, Target.class); Map metaData = (Map) variables.get(DefaultFields.PROCESS_META_DATA.name()).getValue(); - return new SecurityTest(id, context, name, targets.get(0), report, metaData); + return new SecurityTest(id, context, name, targets.get(0), report, metaData, tenant); } private List getListValue(Map variables, DefaultFields name, Class type) { diff --git a/scb-sdk/src/main/java/io/securecodebox/model/securitytest/SecurityTest.java b/scb-sdk/src/main/java/io/securecodebox/model/securitytest/SecurityTest.java index c221c2be..c6f159d6 100644 --- a/scb-sdk/src/main/java/io/securecodebox/model/securitytest/SecurityTest.java +++ b/scb-sdk/src/main/java/io/securecodebox/model/securitytest/SecurityTest.java @@ -37,11 +37,16 @@ public class SecurityTest extends AbstractSecurityTest { public SecurityTest() {} public SecurityTest(UUID id, String context, String name, Target target, Report report, Map metaData) { + this(id, context, name, target, report, metaData, null); + } + + public SecurityTest(UUID id, String context, String name, Target target, Report report, Map metaData, String tenant) { this.id = id; this.context = context; this.name = name; this.target = target; this.report = report; + this.tenant = tenant; this.setMetaData(metaData); } From e578edfb8bed566d90b045a8f7696e90a3f30188 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 23 Apr 2019 19:13:20 +0200 Subject: [PATCH 12/16] Migrated filter to a spring interceptor --- .../engine/auth/CamundaAuthContextSetup.java | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java index 861168ee..a36cfd68 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java @@ -25,24 +25,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; -import org.springframework.web.filter.GenericFilterBean; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; @Configuration @EnableWebSecurity -@Order(1231231) @ConditionalOnProperty(name = "securecodebox.rest.auth", havingValue = "basic auth") -public class CamundaAuthContextSetup extends WebSecurityConfigurerAdapter { +public class CamundaAuthContextSetup implements WebMvcConfigurer { private static final Logger LOG = LoggerFactory.getLogger(CamundaAuthContextSetup.class); @Autowired @@ -52,28 +47,28 @@ public class CamundaAuthContextSetup extends WebSecurityConfigurerAdapter { private AuthService authService; @Override - public void configure(HttpSecurity http) { - LOG.debug("Init: Camunda Auth Context Setup Filter"); - http.antMatcher("/box/**").addFilterAfter(new CamundaAuthContextSetupFilter(), FilterSecurityInterceptor.class); + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new CamundaAuthContextSetupInterceptor()).addPathPatterns("/box/**"); } - private class CamundaAuthContextSetupFilter extends GenericFilterBean { + private class CamundaAuthContextSetupInterceptor implements HandlerInterceptor { - /** - * Sets up the Camunda Authentication Context before the Resource gets executed - */ @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + public boolean preHandle( + HttpServletRequest request, + HttpServletResponse response, + Object handler) { identityService.setAuthentication(authService.getAuthentication()); - filterChain.doFilter(servletRequest, servletResponse); + return true; } - /** - * Tears down the Camunda Authentication Context after the Resource got executed - */ @Override - public void destroy(){ + public void postHandle( + HttpServletRequest request, + HttpServletResponse response, + Object handler, + ModelAndView modelAndView) { identityService.clearAuthentication(); } } From dc9833744d03bc4e06b6bdc9acd568080f2a979b Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Tue, 23 Apr 2019 19:15:26 +0200 Subject: [PATCH 13/16] Readded comment explaining the existence of the interceptor --- .../io/securecodebox/engine/auth/CamundaAuthContextSetup.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java index a36cfd68..20d9bddf 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/auth/CamundaAuthContextSetup.java @@ -51,6 +51,10 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CamundaAuthContextSetupInterceptor()).addPathPatterns("/box/**"); } + /** + * Sets up the Camunda Authentication Context before + * the Resource gets executed and tears it down afterwards + */ private class CamundaAuthContextSetupInterceptor implements HandlerInterceptor { @Override From 05832436db631f4266deb7b828fe99d0a2f6822e Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 24 Apr 2019 09:18:18 +0200 Subject: [PATCH 14/16] Removed unused automatic imports --- .../io/securecodebox/engine/CustomWebMvcConfigurer.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java b/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java index 50e5eeee..58ab3080 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/CustomWebMvcConfigurer.java @@ -19,19 +19,10 @@ package io.securecodebox.engine; -import io.securecodebox.engine.service.AuthService; -import org.camunda.bpm.engine.IdentityService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - /** * This class adds additional resources to the spring application. * For example forms provided in the classpath of every process instance. From 9717dec79018026c1628a1113672ede8f27f0f28 Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 24 Apr 2019 10:13:28 +0200 Subject: [PATCH 15/16] Ensure that workers with a tenant will not work on tenant specific tasks jobs --- .../io/securecodebox/engine/rest/ScanJobResource.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java b/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java index f82a36c6..1e189841 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/rest/ScanJobResource.java @@ -56,9 +56,11 @@ import javax.validation.Valid; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; /** * API / Endpoint for scan jobs. @@ -125,7 +127,13 @@ public ResponseEntity lockJob( } ExternalTaskQueryBuilder externalTaskQueryBuilder = engine.getExternalTaskService() .fetchAndLock(1, scannerId.toString()); - externalTaskQueryBuilder.topic(topic, LOCK_DURATION_MS); + + List tenantIds = authService.getAuthentication().getTenantIds(); + if(tenantIds.isEmpty()){ + externalTaskQueryBuilder.topic(topic, LOCK_DURATION_MS).withoutTenantId(); + } else { + externalTaskQueryBuilder.topic(topic, LOCK_DURATION_MS).tenantIdIn(tenantIds.stream().toArray(String[]::new)); + } LockedExternalTask result = Iterables.getFirst(externalTaskQueryBuilder.execute(), null); if (result != null) { From f7fd5cddc31c93bb4ae8f7994e7bbb3842ff80bd Mon Sep 17 00:00:00 2001 From: Jannik Hollenbach Date: Wed, 24 Apr 2019 10:38:48 +0200 Subject: [PATCH 16/16] Added process definition read, read_instance & update_instance permissions Due to the enforcement of Camunda permissions the process definition is now mandatory for a user to successfully submit a job result. The other two were added as a precaution. Note: This is a potential **breaking change** for users not using the `scanner` group provided by default for their scanner user permissions. The permissions of the default `scanner` group will get updated by default, all others will have to manually expand their scanner groups to match these permissions. --- .../engine/helper/DefaultGroupConfiguration.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scb-engine/src/main/java/io/securecodebox/engine/helper/DefaultGroupConfiguration.java b/scb-engine/src/main/java/io/securecodebox/engine/helper/DefaultGroupConfiguration.java index 2c4d43e6..30bb304f 100644 --- a/scb-engine/src/main/java/io/securecodebox/engine/helper/DefaultGroupConfiguration.java +++ b/scb-engine/src/main/java/io/securecodebox/engine/helper/DefaultGroupConfiguration.java @@ -63,7 +63,12 @@ public void postProcessEngineBuild(final ProcessEngine processEngine) { Resources.PROCESS_INSTANCE, Permissions.READ, Permissions.UPDATE ); - + createAuthorizationForGroup( + processEngine.getAuthorizationService(), + GROUP_SCANNER, + Resources.PROCESS_DEFINITION, + Permissions.READ, Permissions.READ_INSTANCE, Permissions.UPDATE_INSTANCE + ); createGroup(identityService, GROUP_CI); createAuthorizationForGroup(