Merge branch 'master' into Feature_Improve_Code_Quality

Signed-off-by: Dominic Schabel <dominic.schabel@bosch-si.com>

# Conflicts:
#	hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/WithSpringAuthorityRule.java
This commit is contained in:
Dominic Schabel
2016-08-08 16:40:00 +02:00
21 changed files with 274 additions and 179 deletions

View File

@@ -73,6 +73,7 @@ import org.springframework.security.config.annotation.web.servlet.configuration.
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
@@ -292,6 +293,9 @@ public class SecurityManagedConfiguration {
@Autowired @Autowired
private SecurityProperties springSecurityProperties; private SecurityProperties springSecurityProperties;
@Autowired
private SystemSecurityContext systemSecurityContext;
@Override @Override
protected void configure(final HttpSecurity http) throws Exception { protected void configure(final HttpSecurity http) throws Exception {
@@ -320,14 +324,14 @@ public class SecurityManagedConfiguration {
userAuthenticationFilter.destroy(); userAuthenticationFilter.destroy();
} }
}, RequestHeaderAuthenticationFilter.class) }, RequestHeaderAuthenticationFilter.class)
.addFilterAfter( .addFilterAfter(new AuthenticationSuccessTenantMetadataCreationFilter(systemManagement,
new AuthenticationSuccessTenantMetadataCreationFilter(tenantAware, systemManagement), systemSecurityContext), SessionManagementFilter.class)
SessionManagementFilter.class)
.authorizeRequests().anyRequest().authenticated() .authorizeRequests().anyRequest().authenticated()
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**") .antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN); .hasAnyAuthority(SpPermission.SYSTEM_ADMIN);
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint); httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint);
httpSec.anonymous().disable();
} }
} }
@@ -524,12 +528,15 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
@Autowired @Autowired
private SystemManagement systemManagement; private SystemManagement systemManagement;
@Autowired
private SystemSecurityContext systemSecurityContext;
@Override @Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception { public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) { if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) {
systemManagement systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(),
.getTenantMetadata(((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString()); ((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString());
} else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) { } else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) {
// TODO: vaadin4spring-ext-security does not give us the // TODO: vaadin4spring-ext-security does not give us the
// fullyAuthenticatedToken // fullyAuthenticatedToken
@@ -538,7 +545,8 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
// LoginView. This needs to be changed with the update of // LoginView. This needs to be changed with the update of
// vaadin4spring 0.0.7 because it // vaadin4spring 0.0.7 because it
// has been fixed. // has been fixed.
systemManagement.getTenantMetadata("DEFAULT"); final String defaultTenant = "DEFAULT";
systemSecurityContext.runAsSystemAsTenant(() -> systemManagement.getTenantMetadata(), defaultTenant);
} }
super.onAuthenticationSuccess(authentication); super.onAuthenticationSuccess(authentication);
@@ -550,13 +558,13 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
*/ */
class AuthenticationSuccessTenantMetadataCreationFilter implements Filter { class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
private final TenantAware tenantAware;
private final SystemManagement systemManagement; private final SystemManagement systemManagement;
private final SystemSecurityContext systemSecurityContext;
AuthenticationSuccessTenantMetadataCreationFilter(final TenantAware tenantAware, AuthenticationSuccessTenantMetadataCreationFilter(final SystemManagement systemManagement,
final SystemManagement systemManagement) { final SystemSecurityContext systemSecurityContext) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement; this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext;
} }
@Override @Override
@@ -567,14 +575,16 @@ class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
@Override @Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
lazyCreateTenantMetadata();
chain.doFilter(request, response);
final String currentTenant = tenantAware.getCurrentTenant();
if (currentTenant != null) {
// lazy initialize tenant meta data after successful authentication
systemManagement.getTenantMetadata(currentTenant);
} }
chain.doFilter(request, response); private void lazyCreateTenantMetadata() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
systemSecurityContext.runAsSystem(() -> systemManagement.getTenantMetadata());
}
} }
@Override @Override

View File

@@ -8,6 +8,10 @@
*/ */
package org.eclipse.hawkbit.ddi.rest.resource; package org.eclipse.hawkbit.ddi.rest.resource;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE;
import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
@@ -23,7 +27,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
@@ -57,7 +60,8 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD
@Test @Test
@Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.") @Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.")
@WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = "ROLE_CONTROLLER", autoCreateTenant = false) @WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = { CONTROLLER_ROLE,
SYSTEM_ROLE }, autoCreateTenant = false)
public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception { public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception {
mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print()) mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
@@ -91,10 +95,8 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD
// make a poll, audit information should not be changed, run as // make a poll, audit information should not be changed, run as
// controller principal! // controller principal!
securityRule.runAs( securityRule.runAs(WithSpringAuthorityRule.withUser("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
WithSpringAuthorityRule.withUser("controller", SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS), () -> { mvc.perform(get("/{tenant}/controller/v1/" + knownTargetControllerId, tenantAware.getCurrentTenant()))
mvc.perform(
get("/{tenant}/controller/v1/" + knownTargetControllerId, tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null; return null;
}); });
@@ -143,16 +145,13 @@ public class DdiRootControllerTest extends AbstractRestIntegrationTestWithMongoD
@Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.") @Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.")
@WithUser(principal = "knownpricipal", allSpPermissions = false) @WithUser(principal = "knownpricipal", allSpPermissions = false)
public void pollWithModifiedGloablPollingTime() throws Exception { public void pollWithModifiedGloablPollingTime() throws Exception {
securityRule.runAs( securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
WithSpringAuthorityRule.withUser("tenantadmin", SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION),
() -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL, tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:02:00"); "00:02:00");
return null; return null;
}); });
securityRule.runAs( securityRule.runAs(WithSpringAuthorityRule.withUser("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
WithSpringAuthorityRule.withUser("controller", SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())) mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaTypes.HAL_JSON)) .andExpect(content().contentType(MediaTypes.HAL_JSON))

View File

@@ -39,7 +39,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -72,15 +72,15 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
@Autowired @Autowired
private SystemManagement systemManagement; private SystemManagement systemManagement;
@Autowired
private TenantAware currentTenant;
@Autowired @Autowired
private EntityFactory entityFactory; private EntityFactory entityFactory;
@Autowired @Autowired
private DistributionSetManagement distributionSetManagement; private DistributionSetManagement distributionSetManagement;
@Autowired
private SystemSecurityContext systemSecurityContext;
@Override @Override
public ResponseEntity<PagedList<MgmtDistributionSet>> getDistributionSets( public ResponseEntity<PagedList<MgmtDistributionSet>> getDistributionSets(
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
@@ -119,8 +119,9 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
LOG.debug("creating {} distribution sets", sets.size()); LOG.debug("creating {} distribution sets", sets.size());
// set default Ds type if ds type is null // set default Ds type if ds type is null
sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(this.systemManagement final String defaultDsKey = systemSecurityContext
.getTenantMetadata(this.currentTenant.getCurrentTenant()).getDefaultDsType().getKey())); .runAsSystem(() -> this.systemManagement.getTenantMetadata().getDefaultDsType().getKey());
sets.stream().filter(ds -> ds.getType() == null).forEach(ds -> ds.setType(defaultDsKey));
final Iterable<DistributionSet> createdDSets = this.distributionSetManagement final Iterable<DistributionSet> createdDSets = this.distributionSetManagement
.createDistributionSets(MgmtDistributionSetMapper.dsFromRequest(sets, this.softwareManagement, .createDistributionSets(MgmtDistributionSetMapper.dsFromRequest(sets, this.softwareManagement,

View File

@@ -40,10 +40,29 @@
<groupId>org.springframework.hateoas</groupId> <groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId> <artifactId>spring-hateoas</artifactId>
</dependency> </dependency>
<!-- Optional -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- TEST -->
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -183,6 +183,8 @@ public interface ControllerManagement {
* @return the security context of the target, in case no target exists for * @return the security context of the target, in case no target exists for
* the given controllerId {@code null} is returned * the given controllerId {@code null} is returned
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_TARGET_SEC_TOKEN)
String getSecurityTokenByControllerId(@NotEmpty String controllerId); String getSecurityTokenByControllerId(@NotEmpty String controllerId);
/** /**

View File

@@ -340,6 +340,7 @@ public interface SoftwareManagement {
* to search for * to search for
* @return {@link List} of found {@link SoftwareModule}s * @return {@link List} of found {@link SoftwareModule}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
List<SoftwareModule> findSoftwareModulesById(@NotEmpty Collection<Long> ids); List<SoftwareModule> findSoftwareModulesById(@NotEmpty Collection<Long> ids);
/** /**

View File

@@ -61,6 +61,9 @@ public interface SystemManagement {
/** /**
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()} * @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()}
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
TenantMetaData getTenantMetadata(); TenantMetaData getTenantMetadata();
/** /**
@@ -77,6 +80,7 @@ public interface SystemManagement {
* to retrieve data for * to retrieve data for
* @return {@link TenantMetaData} of given tenant * @return {@link TenantMetaData} of given tenant
*/ */
@PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE)
TenantMetaData getTenantMetadata(@NotNull String tenant); TenantMetaData getTenantMetadata(@NotNull String tenant);
/** /**
@@ -86,6 +90,7 @@ public interface SystemManagement {
* to update * to update
* @return updated {@link TenantMetaData} entity * @return updated {@link TenantMetaData} entity
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
TenantMetaData updateTenantMetadata(@NotNull TenantMetaData metaData); TenantMetaData updateTenantMetadata(@NotNull TenantMetaData metaData);
} }

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository;
import static org.fest.assertions.Assertions.assertThat;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.junit.Test;
import org.springframework.security.access.prepost.PreAuthorize;
import com.google.common.reflect.ClassPath;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Unit Tests - Repository")
@Stories("Security Test")
public class RepositoryManagementMethodPreAuthorizeAnnotatedTest {
private static final Set<Method> METHOD_SECURITY_EXCLUSION = new HashSet<>();
static {
METHOD_SECURITY_EXCLUSION.add(getMethod(SystemManagement.class, "currentTenant"));
}
@Test
@Description("Verfies that repository methods are @PreAuthorize annotated")
public void repositoryManagementMethodsArePreAuthorizedAnnotated()
throws ClassNotFoundException, URISyntaxException, IOException {
final List<Class<?>> findInterfacesInPackage = findInterfacesInPackage(getClass().getPackage(),
Pattern.compile(".*Management"));
assertThat(findInterfacesInPackage).isNotEmpty();
for (final Class<?> interfaceToCheck : findInterfacesInPackage) {
assertDeclaredMethodsContainsPreAuthorizeAnnotaions(interfaceToCheck);
}
// all exclusion should be used, otherwise the method exlusion should be
// cleaned up again
assertThat(METHOD_SECURITY_EXCLUSION).isEmpty();
}
/**
* asserts that the given methods are annotated with the
* {@link PreAuthorize} annotation for security. Inherited methods are not
* checked. The following methods are excluded due inherited from
* {@link Object}, like equals() or toString().
*
* @param clazz
* the class to retrieve the public declared methods
*/
private static void assertDeclaredMethodsContainsPreAuthorizeAnnotaions(final Class<?> clazz) {
final Method[] declaredMethods = clazz.getDeclaredMethods();
for (final Method method : declaredMethods) {
final boolean methodExcluded = METHOD_SECURITY_EXCLUSION.contains(method);
if (methodExcluded || method.isSynthetic() || Modifier.isPublic(method.getModifiers())) {
// skip method because it should be excluded
METHOD_SECURITY_EXCLUSION.remove(method);
continue;
}
final PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
assertThat(annotation).as("The public method " + method.getName() + " in class " + clazz.getName()
+ " is not annoated with @PreAuthorize, security leak?").isNotNull();
}
}
private List<Class<?>> findInterfacesInPackage(final Package p, final Pattern includeFilter)
throws URISyntaxException, IOException, ClassNotFoundException {
return ClassPath.from(Thread.currentThread().getContextClassLoader()).getTopLevelClasses(p.getName()).stream()
.filter(clazzInfo -> includeFilter.matcher(clazzInfo.getSimpleName()).matches())
.map(clazzInfo -> clazzInfo.load()).filter(clazz -> clazz.isInterface()).collect(Collectors.toList());
}
private static Method getMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterTypes) {
try {
return clazz.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

View File

@@ -1,63 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import static org.fest.assertions.Assertions.assertThat;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.access.prepost.PreAuthorize;
public class MethodSecurityUtil {
private static final Set<String> METHOD_SECURITY_EXCLUSION = new HashSet<>();
static {
METHOD_SECURITY_EXCLUSION.add("equals");
METHOD_SECURITY_EXCLUSION.add("toString");
METHOD_SECURITY_EXCLUSION.add("hashCode");
METHOD_SECURITY_EXCLUSION.add("clone");
METHOD_SECURITY_EXCLUSION.add("setEnvironment");
// this method shouldn't be public on the DeploymentManagemeht but it is
METHOD_SECURITY_EXCLUSION.add("setOverrideObsoleteUpdateActions");
METHOD_SECURITY_EXCLUSION.add("isOverrideObsoleteUpdateActions");
// this method must be public accessible without security because it's
// necessary to acccess
// the security-token of a target without being authenticated because
// the security-token is
// the authentication process
// ControllerManagement#getSecurityTokenByControllerId()
METHOD_SECURITY_EXCLUSION.add("getSecurityTokenByControllerId");
}
/**
* asserts that the given methods are annotated with the
* {@link PreAuthorize} annotation for security. Inherited methods are not
* checked. The following methods are excluded due inherited from
* {@link Object}, like equals() or toString().
*
* @param clazz
* the class to retrieve the public declared methods
*/
public static void assertDeclaredMethodsContainsPreAuthorizeAnnotaions(final Class<?> clazz) {
final Method[] declaredMethods = clazz.getDeclaredMethods();
for (final Method method : declaredMethods) {
if (!METHOD_SECURITY_EXCLUSION.contains(method.getName()) && !method.isSynthetic()
&& Modifier.isPublic(method.getModifiers())) {
final PreAuthorize annotation = method.getAnnotation(PreAuthorize.class);
assertThat(annotation).as(
"The public method " + method.getName() + " is not annoated with @PreAuthorize, security leak?")
.isNotNull();
}
}
}
}

View File

@@ -14,10 +14,10 @@ import java.io.ByteArrayInputStream;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantMetaData;
import org.eclipse.hawkbit.repository.report.model.TenantUsage; import org.eclipse.hawkbit.repository.report.model.TenantUsage;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule; import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.junit.Test; import org.junit.Test;
@@ -30,15 +30,6 @@ import ru.yandex.qatools.allure.annotations.Stories;
@Stories("System Management") @Stories("System Management")
public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB { public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB {
@Test
@Description("Ensures that you can create a tenant without setting the necessary security context which holds a current tenant")
public void createInitialTenantWithoutSecurityContext() {
securityRule.clear();
final String tenantToBeCreated = "newTenantToCreate";
final TenantMetaData tenantMetadata = systemManagement.getTenantMetadata(tenantToBeCreated);
assertThat(tenantMetadata).isNotNull();
}
@Test @Test
@Description("Ensures that findTenants returns all tenants and not only restricted to the tenant which currently is logged in") @Description("Ensures that findTenants returns all tenants and not only restricted to the tenant which currently is logged in")
public void findTenantsReturnsAllTenantsNotOnlyWhichLoggedIn() throws Exception { public void findTenantsReturnsAllTenantsNotOnlyWhichLoggedIn() throws Exception {
@@ -109,7 +100,8 @@ public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB
for (int i = 0; i < tenants; i++) { for (int i = 0; i < tenants; i++) {
final String tenantname = "tenant" + i; final String tenantname = "tenant" + i;
securityRule.runAs(WithSpringAuthorityRule.withUserAndTenant("bumlux", tenantname), () -> { securityRule.runAs(WithSpringAuthorityRule.withUserAndTenant("bumlux", tenantname, true, true,
SpringEvalExpressions.SYSTEM_ROLE), () -> {
systemManagement.getTenantMetadata(tenantname); systemManagement.getTenantMetadata(tenantname);
if (artifactSize > 0) { if (artifactSize > 0) {
createTestArtifact(random); createTestArtifact(random);
@@ -119,8 +111,8 @@ public class SystemManagementTest extends AbstractJpaIntegrationTestWithMongoDB
final List<Target> createdTargets = createTestTargets(targets); final List<Target> createdTargets = createTestTargets(targets);
if (updates > 0) { if (updates > 0) {
for (int x = 0; x < updates; x++) { for (int x = 0; x < updates; x++) {
final DistributionSet ds = testdataFactory.createDistributionSet("to be deployed" + x, final DistributionSet ds = testdataFactory
true); .createDistributionSet("to be deployed" + x, true);
deploymentManagement.assignDistributionSet(ds, createdTargets); deploymentManagement.assignDistributionSet(ds, createdTargets);
} }

View File

@@ -8,6 +8,9 @@
*/ */
package org.eclipse.hawkbit.repository.test.util; package org.eclipse.hawkbit.repository.test.util;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE;
import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter; import org.eclipse.hawkbit.ExcludePathAwareShallowETagFilter;
import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -58,7 +61,7 @@ import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration @WebAppConfiguration
@ActiveProfiles({ "test" }) @ActiveProfiles({ "test" })
@WithUser(principal = "bumlux", allSpPermissions = true, authorities = "ROLE_CONTROLLER") @WithUser(principal = "bumlux", allSpPermissions = true, authorities = { CONTROLLER_ROLE, SYSTEM_ROLE })
@SpringApplicationConfiguration(classes = { TestConfiguration.class }) @SpringApplicationConfiguration(classes = { TestConfiguration.class })
// destroy the context after each test class because otherwise we get problem // destroy the context after each test class because otherwise we get problem
// when context is // when context is

View File

@@ -52,10 +52,10 @@ public class WithSpringAuthorityRule implements TestRule {
annotation = description.getTestClass().getAnnotation(WithUser.class); annotation = description.getTestClass().getAnnotation(WithUser.class);
} }
if (annotation != null) { if (annotation != null) {
setSecurityContext(annotation);
if (annotation.autoCreateTenant()) { if (annotation.autoCreateTenant()) {
SystemManagementHolder.getInstance().getSystemManagement().getTenantMetadata(annotation.tenantId()); createTenant(annotation.tenantId());
} }
setSecurityContext(annotation);
} }
return oldContext; return oldContext;
} }
@@ -149,7 +149,7 @@ public class WithSpringAuthorityRule implements TestRule {
final SecurityContext oldContext = SecurityContextHolder.getContext(); final SecurityContext oldContext = SecurityContextHolder.getContext();
setSecurityContext(withUser); setSecurityContext(withUser);
if (withUser.autoCreateTenant()) { if (withUser.autoCreateTenant()) {
SystemManagementHolder.getInstance().getSystemManagement().getTenantMetadata(withUser.tenantId()); createTenant(withUser.tenantId());
} }
try { try {
return callable.call(); return callable.call();
@@ -158,6 +158,16 @@ public class WithSpringAuthorityRule implements TestRule {
} }
} }
private void createTenant(final String tenantId) {
final SecurityContext oldContext = SecurityContextHolder.getContext();
setSecurityContext(privilegedUser());
try {
SystemManagementHolder.getInstance().getSystemManagement().getTenantMetadata(tenantId);
} finally {
after(oldContext);
}
}
public static WithUser withUser(final String principal, final String... authorities) { public static WithUser withUser(final String principal, final String... authorities) {
return withUserAndTenant(principal, "default", true, true, authorities); return withUserAndTenant(principal, "default", true, true, authorities);
} }
@@ -180,7 +190,7 @@ public class WithSpringAuthorityRule implements TestRule {
} }
private static WithUser privilegedUser() { private static WithUser privilegedUser() {
return createWithUser("bumlux", "default", true, true, new String[] { "ROLE_CONTROLLER" }); return createWithUser("bumlux", "default", true, true, new String[] { "ROLE_CONTROLLER", "ROLE_SYSTEM_CODE" });
} }
private static WithUser createWithUser(final String principal, final String tenant, final boolean autoCreateTenant, private static WithUser createWithUser(final String principal, final String tenant, final boolean autoCreateTenant,

View File

@@ -294,6 +294,14 @@ public final class SpPermission {
public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX + HAS_AUTH_OR public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX + HAS_AUTH_OR
+ IS_SYSTEM_CODE; + IS_SYSTEM_CODE;
/**
* Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#READ_TARGET_SEC_TOKEN} or
* {@link #IS_SYSTEM_CODE}.
*/
public static final String HAS_AUTH_READ_TARGET_SEC_TOKEN = HAS_AUTH_PREFIX + READ_TARGET_SEC_TOKEN
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
/** /**
* Spring security eval hasAuthority expression to check if spring * Spring security eval hasAuthority expression to check if spring
* context contains {@link SpPermission#CREATE_TARGET} or * context contains {@link SpPermission#CREATE_TARGET} or

View File

@@ -186,6 +186,9 @@ public class SoftwareModuleDetailsTable extends Table {
private void unassignSW(final ClickEvent event, final DistributionSet distributionSet, private void unassignSW(final ClickEvent event, final DistributionSet distributionSet,
final Set<SoftwareModule> alreadyAssignedSwModules) { final Set<SoftwareModule> alreadyAssignedSwModules) {
final SoftwareModule unAssignedSw = getSoftwareModule(event.getButton().getId(), alreadyAssignedSwModules); final SoftwareModule unAssignedSw = getSoftwareModule(event.getButton().getId(), alreadyAssignedSwModules);
if (distributionSetManagement.isDistributionSetInUse(distributionSet)) {
uiNotification.displayValidationError(i18n.get("message.error.notification.ds.target.assigned",distributionSet.getName(), distributionSet.getVersion()));
} else {
final DistributionSet newDistributionSet = distributionSetManagement.unassignSoftwareModule(distributionSet, final DistributionSet newDistributionSet = distributionSetManagement.unassignSoftwareModule(distributionSet,
unAssignedSw); unAssignedSw);
manageDistUIState.setLastSelectedEntity(DistributionSetIdName.generate(newDistributionSet)); manageDistUIState.setLastSelectedEntity(DistributionSetIdName.generate(newDistributionSet));
@@ -194,6 +197,8 @@ public class SoftwareModuleDetailsTable extends Table {
uiNotification.displaySuccess(i18n.get("message.sw.unassigned", unAssignedSw.getName())); uiNotification.displaySuccess(i18n.get("message.sw.unassigned", unAssignedSw.getName()));
} }
}
private static boolean isSoftModAvaiableForSoftType(final Set<SoftwareModule> swModulesSet, private static boolean isSoftModAvaiableForSoftType(final Set<SoftwareModule> swModulesSet,
final SoftwareModuleType swModType) { final SoftwareModuleType swModType) {
for (final SoftwareModule sw : swModulesSet) { for (final SoftwareModule sw : swModulesSet) {

View File

@@ -12,6 +12,7 @@ import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
@@ -57,7 +58,7 @@ public abstract class AbstractTagToken<T extends BaseEntity> implements Serializ
protected IndexedContainer container; protected IndexedContainer container;
protected final transient Map<Long, TagData> tagDetails = new HashMap<>(); protected final transient Map<Long, TagData> tagDetails = new ConcurrentHashMap<>();
protected final transient Map<Long, TagData> tokensAdded = new HashMap<>(); protected final transient Map<Long, TagData> tokensAdded = new HashMap<>();
@@ -146,11 +147,13 @@ public abstract class AbstractTagToken<T extends BaseEntity> implements Serializ
} }
protected void setContainerPropertValues(final Long tagId, final String tagName, final String tagColor) { protected void setContainerPropertValues(final Long tagId, final String tagName, final String tagColor) {
tagDetails.put(tagId, new TagData(tagId, tagName, tagColor)); final TagData tagData = tagDetails.putIfAbsent(tagId, new TagData(tagId, tagName, tagColor));
if (tagData == null) {
final Item item = container.addItem(tagId); final Item item = container.addItem(tagId);
item.getItemProperty("id").setValue(tagId); item.getItemProperty("id").setValue(tagId);
updateItem(tagName, tagColor, item); updateItem(tagName, tagColor, item);
} }
}
protected void updateItem(final String tagName, final String tagColor, final Item item) { protected void updateItem(final String tagName, final String tagColor, final Item item) {
item.getItemProperty("name").setValue(tagName); item.getItemProperty("name").setValue(tagName);

View File

@@ -108,6 +108,7 @@ public class DistributionTagToken extends AbstractTagToken<DistributionSet> {
@Override @Override
protected void populateContainer() { protected void populateContainer() {
container.removeAllItems(); container.removeAllItems();
tagDetails.clear();
for (final DistributionSetTag tag : tagManagement.findAllDistributionSetTags()) { for (final DistributionSetTag tag : tagManagement.findAllDistributionSetTags()) {
setContainerPropertValues(tag.getId(), tag.getName(), tag.getColour()); setContainerPropertValues(tag.getId(), tag.getName(), tag.getColour());
} }

View File

@@ -103,6 +103,7 @@ public class TargetTagToken extends AbstractTargetTagToken<Target> {
@Override @Override
protected void populateContainer() { protected void populateContainer() {
container.removeAllItems(); container.removeAllItems();
tagDetails.clear();
for (final TargetTag tag : tagManagement.findAllTargetTags()) { for (final TargetTag tag : tagManagement.findAllTargetTags()) {
setContainerPropertValues(tag.getId(), tag.getName(), tag.getColour()); setContainerPropertValues(tag.getId(), tag.getName(), tag.getColour());
} }

View File

@@ -334,9 +334,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
} }
if (distributionSetManagement.isDistributionSetInUse(ds)) { if (distributionSetManagement.isDistributionSetInUse(ds)) {
notification.displayValidationError( notification.displayValidationError(i18n.get("message.error.notification.ds.target.assigned", ds.getName(), ds.getVersion()));
String.format("Distribution set %s:%s is already assigned to targets and cannot be changed",
ds.getName(), ds.getVersion()));
return false; return false;
} }
return true; return true;
@@ -506,7 +504,7 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
return columnList; return columnList;
} }
private Button createManageMetadataButton(String nameVersionStr) { private Button createManageMetadataButton(final String nameVersionStr) {
final Button manageMetadataBtn = SPUIComponentProvider.getButton( final Button manageMetadataBtn = SPUIComponentProvider.getButton(
SPUIComponentIdProvider.DS_TABLE_MANAGE_METADATA_ID + "." + nameVersionStr, "", "", null, false, SPUIComponentIdProvider.DS_TABLE_MANAGE_METADATA_ID + "." + nameVersionStr, "", "", null, false,
FontAwesome.LIST_ALT, SPUIButtonStyleSmallNoBorder.class); FontAwesome.LIST_ALT, SPUIButtonStyleSmallNoBorder.class);
@@ -515,8 +513,8 @@ public class DistributionSetTable extends AbstractNamedVersionTable<Distribution
return manageMetadataBtn; return manageMetadataBtn;
} }
private void showMetadataDetails(Long itemId) { private void showMetadataDetails(final Long itemId) {
DistributionSet ds = distributionSetManagement.findDistributionSetByIdWithDetails(itemId); final DistributionSet ds = distributionSetManagement.findDistributionSetByIdWithDetails(itemId);
UI.getCurrent().addWindow(dsMetadataPopupLayout.getWindow(ds,null)); UI.getCurrent().addWindow(dsMetadataPopupLayout.getWindow(ds,null));
} }

View File

@@ -386,6 +386,7 @@ message.metadata.updated = Metadata with key {0} successfully updated !
message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value
message.metadata.deleted.successfully = Metadata with key {0} successfully deleted ! message.metadata.deleted.successfully = Metadata with key {0} successfully deleted !
message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ? message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ?
message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed
# Login view # Login view
notification.login.title=Welcome to Bosch IoT Software Provisioning. notification.login.title=Welcome to Bosch IoT Software Provisioning.

View File

@@ -375,6 +375,7 @@ message.metadata.updated = Metadata with key {0} successfully updated !
message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value
message.metadata.deleted.successfully = Metadata with key {0} successfully deleted ! message.metadata.deleted.successfully = Metadata with key {0} successfully deleted !
message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ? message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ?
message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed
# Login view # Login view

View File

@@ -371,6 +371,7 @@ message.metadata.updated = Metadata with key {0} successfully updated !
message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value message.metadata.duplicate.check = Metadata with key {0} already exists, please enter another value
message.metadata.deleted.successfully = Metadata with key {0} successfully deleted ! message.metadata.deleted.successfully = Metadata with key {0} successfully deleted !
message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ? message.confirm.delete.metadata = Are you sure that you want to delete metadata with key {0} ?
message.error.notification.ds.target.assigned = Distribution set {0}:{1} is already assigned to targets and cannot be changed
# Login view # Login view
notification.login.title=Welcome to Bosch IoT Software Provisioning. notification.login.title=Welcome to Bosch IoT Software Provisioning.