Fine grained repository permissions (#2562)
1. Introduce @PrreAuthorize check based on hasPermission - allowing custom processing (compared with non-modifiable hasAuthority/Role processing) 2. Dedicated permissions could be implemented on management api level. Check is made by plugged in PermissionEvaluator 3. Thus common XXX_REPOSITORY permissions could differ for extending services 4. Change create/update entity builder pattern - not via EntityFactory but via clean static lombok based builders (with fine fluent api). 5. Implement abstract repository management jpa class that handles the boilerplate code from extending classes in single place consistently -> AbsreactJpaRepositoryManagement 6. Register management api-s as **Sevice**-s instead of **Bean**-s in order to make easier maintainable and get away from heavy argument forwading 7. Simplify custom hawkbit repository registration + adding proxy to handle exception mapping at lower level - thus not depending on Aspects for converting exceptions 8. Implemented general purpose 'copy' utility (ObjectCopyUtil) that using getter/setter patterns is able to copy (e.g. Create/Update) objects to other objects (e.g. JPA entity objects)
This commit is contained in:
@@ -14,6 +14,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.eclipse.hawkbit.im.authentication.Hierarchy;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
@@ -26,6 +27,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
|
||||
@@ -40,6 +43,7 @@ import org.springframework.util.function.SingletonSupplier;
|
||||
/**
|
||||
* Default configuration that is common to all repository implementations.
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableMethodSecurity(proxyTargetClass = true, securedEnabled = true)
|
||||
@EnableConfigurationProperties({ RepositoryProperties.class, ControllerPollProperties.class, TenantConfigurationProperties.class })
|
||||
@@ -52,10 +56,37 @@ public class RepositoryConfiguration {
|
||||
return RoleHierarchyImpl.fromHierarchy(Hierarchy.DEFAULT);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PermissionEvaluator permissionEvaluator(final RoleHierarchy roleHierarchy) {
|
||||
return new DenyAllPermissionEvaluator() {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final Authentication authentication, final Object targetDomainObject, final Object permission) {
|
||||
if (targetDomainObject instanceof MethodSecurityExpressionOperations root) {
|
||||
final String neededPermission =
|
||||
permission + "_" + (root.getThis() instanceof RepositoryManagement<?, ?, ?> repositoryManagement
|
||||
? repositoryManagement.permissionGroup()
|
||||
: "REPOSITORY"); // TODO - should not fall back here - all using parmissions should extend repository management interface
|
||||
final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.anyMatch(authority -> authority.equals(neededPermission));
|
||||
if (!hasPermission) {
|
||||
log.debug(
|
||||
"User {} does not have permission {} for target {}",
|
||||
authentication.getName(), neededPermission, targetDomainObject);
|
||||
}
|
||||
return hasPermission;
|
||||
}
|
||||
return super.hasPermission(authentication, targetDomainObject, permission);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
MethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
||||
final Optional<RoleHierarchy> roleHierarchy, final Optional<ApplicationContext> applicationContext) {
|
||||
final RoleHierarchy roleHierarchy, final PermissionEvaluator permissionEvaluator,
|
||||
final Optional<ApplicationContext> applicationContext) {
|
||||
final DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler() {
|
||||
|
||||
@Override
|
||||
@@ -69,7 +100,8 @@ public class RepositoryConfiguration {
|
||||
return super.createSecurityExpressionRoot(new RawAuthoritiesAuthentication(authentication), mi);
|
||||
}
|
||||
};
|
||||
roleHierarchy.ifPresent(methodSecurityExpressionHandler::setRoleHierarchy);
|
||||
methodSecurityExpressionHandler.setRoleHierarchy(roleHierarchy);
|
||||
methodSecurityExpressionHandler.setPermissionEvaluator(permissionEvaluator);
|
||||
applicationContext.ifPresent(methodSecurityExpressionHandler::setApplicationContext);
|
||||
return methodSecurityExpressionHandler;
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Create and update builder DTO.
|
||||
*
|
||||
* @param <T> update or create builder interface
|
||||
*/
|
||||
public abstract class AbstractDistributionSetTypeUpdateCreate<T> extends AbstractTypeUpdateCreate<T> {
|
||||
|
||||
protected Collection<Long> mandatory;
|
||||
protected Collection<Long> optional;
|
||||
|
||||
public T mandatory(final Collection<Long> mandatory) {
|
||||
this.mandatory = mandatory;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T optional(final Collection<Long> optional) {
|
||||
this.optional = optional;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Optional<Collection<Long>> getMandatory() {
|
||||
return Optional.ofNullable(mandatory);
|
||||
}
|
||||
|
||||
public Optional<Collection<Long>> getOptional() {
|
||||
return Optional.ofNullable(optional);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.eclipse.hawkbit.repository.ValidString;
|
||||
|
||||
/**
|
||||
* Create and update builder DTO.
|
||||
*
|
||||
* @param <T> update or create builder interface
|
||||
*/
|
||||
public abstract class AbstractDistributionSetUpdateCreate<T> extends AbstractNamedEntityBuilder<T> {
|
||||
|
||||
@ValidString
|
||||
protected String version;
|
||||
protected Boolean requiredMigrationStep;
|
||||
@Getter
|
||||
protected Collection<Long> modules;
|
||||
|
||||
public T modules(final Collection<Long> modules) {
|
||||
this.modules = modules;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T requiredMigrationStep(final Boolean requiredMigrationStep) {
|
||||
this.requiredMigrationStep = requiredMigrationStep;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Boolean isRequiredMigrationStep() {
|
||||
return requiredMigrationStep;
|
||||
}
|
||||
|
||||
public T version(final String version) {
|
||||
this.version = strip(version);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Optional<String> getVersion() {
|
||||
return Optional.ofNullable(version);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
/**
|
||||
* Create and update builder DTO.
|
||||
*
|
||||
* @param <T> update or create builder interface
|
||||
*/
|
||||
public abstract class AbstractSoftwareModuleTypeUpdateCreate<T> extends AbstractTypeUpdateCreate<T> {
|
||||
|
||||
protected int maxAssignments = 1;
|
||||
|
||||
public T maxAssignments(final int maxAssignments) {
|
||||
this.maxAssignments = maxAssignments;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public int getMaxAssignments() {
|
||||
return maxAssignments;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ValidString;
|
||||
|
||||
/**
|
||||
* Create and update builder DTO.
|
||||
*
|
||||
* @param <T> update or create builder interface
|
||||
*/
|
||||
public abstract class AbstractSoftwareModuleUpdateCreate<T> extends AbstractNamedEntityBuilder<T> {
|
||||
|
||||
@ValidString
|
||||
protected String version;
|
||||
@ValidString
|
||||
protected String vendor;
|
||||
@ValidString
|
||||
protected String type;
|
||||
|
||||
public T type(final String type) {
|
||||
this.type = AbstractBaseEntityBuilder.strip(type);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Optional<String> getType() {
|
||||
return Optional.ofNullable(type);
|
||||
}
|
||||
|
||||
public T vendor(final String vendor) {
|
||||
this.vendor = AbstractBaseEntityBuilder.strip(vendor);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Optional<String> getVendor() {
|
||||
return Optional.ofNullable(vendor);
|
||||
}
|
||||
|
||||
public T version(final String version) {
|
||||
this.version = AbstractBaseEntityBuilder.strip(version);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public Optional<String> getVersion() {
|
||||
return Optional.ofNullable(version);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
/**
|
||||
* Update implementation.
|
||||
*/
|
||||
public class GenericDistributionSetTypeUpdate extends AbstractDistributionSetTypeUpdateCreate<DistributionSetTypeUpdate>
|
||||
implements DistributionSetTypeUpdate {
|
||||
|
||||
public GenericDistributionSetTypeUpdate(final Long id) {
|
||||
super.id = id;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Update implementation.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true) // override locked()
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class GenericDistributionSetUpdate extends AbstractDistributionSetUpdateCreate<DistributionSetUpdate> implements DistributionSetUpdate {
|
||||
|
||||
@Nullable
|
||||
protected Boolean locked;
|
||||
|
||||
public GenericDistributionSetUpdate(final Long id) {
|
||||
super.id = id;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
/**
|
||||
* Update implementation.
|
||||
*/
|
||||
public class GenericSoftwareModuleTypeUpdate extends AbstractSoftwareModuleTypeUpdateCreate<SoftwareModuleTypeUpdate>
|
||||
implements SoftwareModuleTypeUpdate {
|
||||
|
||||
public GenericSoftwareModuleTypeUpdate(final Long id) {
|
||||
super.id = id;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.builder;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* Update implementation.
|
||||
*/
|
||||
@Data
|
||||
@Accessors(fluent = true) // override locked()
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class GenericSoftwareModuleUpdate extends AbstractSoftwareModuleUpdateCreate<SoftwareModuleUpdate>
|
||||
implements SoftwareModuleUpdate {
|
||||
|
||||
@Nullable
|
||||
protected Boolean locked;
|
||||
|
||||
public GenericSoftwareModuleUpdate(final Long id) {
|
||||
super.id = id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user