Skip to content
This repository was archived by the owner on Feb 26, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* This class adds additional resources to the spring application.
Expand All @@ -31,7 +31,8 @@
* @since 02.03.18
*/
@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
public class CustomWebMvcConfigurer implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/forms/**").addResourceLocations("classpath:/forms/");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
*
* 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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Configuration
@EnableWebSecurity
@ConditionalOnProperty(name = "securecodebox.rest.auth", havingValue = "basic auth")
public class CamundaAuthContextSetup implements WebMvcConfigurer {
private static final Logger LOG = LoggerFactory.getLogger(CamundaAuthContextSetup.class);

@Autowired
IdentityService identityService;

@Autowired
private AuthService authService;

@Override
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
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) {
identityService.setAuthentication(authService.getAuthentication());

return true;
}

@Override
public void postHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
identityService.clearAuthentication();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<BpmnParseListener> preParseListeners = processEngineConfiguration.getCustomPreBPMNParseListeners();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -55,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.
Expand Down Expand Up @@ -117,15 +120,20 @@ public ResponseEntity<ScanConfiguration> 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();
}

ExternalTaskQueryBuilder externalTaskQueryBuilder = engine.getExternalTaskService()
.fetchAndLock(1, scannerId.toString());
externalTaskQueryBuilder.topic(topic, LOCK_DURATION_MS);

List<String> 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) {
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -227,9 +235,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();
}

Expand All @@ -253,6 +261,7 @@ public ResponseEntity failJob(
engine.getExternalTaskService()
.handleFailure(id.toString(), result.getScannerId().toString(), result.getErrorMessage(),
result.getErrorDetails(), retriesLeft, 1000);

return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,12 @@ public ResponseEntity<List<UUID>> startSecurityTests(

List<UUID> 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);
Expand Down Expand Up @@ -209,8 +213,8 @@ public ResponseEntity<SecurityTest> getSecurityTest(
if (securityTest.isFinished()) {
return ResponseEntity.status(HttpStatus.OK).body(securityTest);
}
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(securityTest);

return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT).body(securityTest);
} catch (SecurityTestService.SecurityTestNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (SecurityTestService.SecurityTestErroredException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,20 +40,24 @@ 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)){
if (AUTH_DISABLED_TYPE.equals(authType)) {
return;
}

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if(authentication == null) {
if (authentication == null) {
throw new InsufficientAuthorizationException("No authentication provided.");
}

Expand All @@ -66,7 +71,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,
Expand All @@ -86,12 +91,41 @@ 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.");
}
}

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<String> groups = engine
.getIdentityService()
.createGroupQuery()
.groupMember(userId)
.list()
.stream()
.map(Group::getId)
.collect(Collectors.toList());

List<String> 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
);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,7 +66,7 @@ public void checkSecurityTestDefinitionExistence(String key) throws NonExistentS
}
}

public UUID startSecurityTest(SecurityTestConfiguration securityTest){
public UUID startSecurityTest(SecurityTestConfiguration securityTest) throws InsufficientAuthorizationException {
Map<String, Object> values = new HashMap<>();

List<Target> targets = new LinkedList<>();
Expand All @@ -77,6 +78,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());
}
Expand Down Expand Up @@ -130,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<Target> targets = getListValue(variables, DefaultFields.PROCESS_TARGETS, Target.class);
Map<String, String> metaData = (Map<String, String>) 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 <T> List<T> getListValue(Map<String, HistoricVariableInstance> variables, DefaultFields name, Class<T> type) {
Expand Down
Loading