diff --git a/hawkbit-autoconfigure/pom.xml b/hawkbit-autoconfigure/pom.xml
index 1cd89aa38..a2c0ede90 100644
--- a/hawkbit-autoconfigure/pom.xml
+++ b/hawkbit-autoconfigure/pom.xml
@@ -91,5 +91,14 @@
protostuff-runtime
true
+
+ org.vaadin.spring.extensions
+ vaadin-spring-ext-security
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java
index a14657b32..3c3a210d9 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/MgmtUiAutoConfiguration.java
@@ -12,10 +12,13 @@ import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.hawkbit.DistributedResourceBundleMessageSource;
import org.eclipse.hawkbit.ui.MgmtUiConfiguration;
+import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.push.DelayedEventBusPushStrategy;
import org.eclipse.hawkbit.ui.push.EventPushStrategy;
+import org.eclipse.hawkbit.ui.push.HawkbitEventPermissionChecker;
import org.eclipse.hawkbit.ui.push.HawkbitEventProvider;
+import org.eclipse.hawkbit.ui.push.UIEventPermissionChecker;
import org.eclipse.hawkbit.ui.push.UIEventProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -26,7 +29,7 @@ import org.springframework.context.annotation.Import;
import org.vaadin.spring.annotation.EnableVaadinExtensions;
import org.vaadin.spring.events.EventBus.UIEventBus;
import org.vaadin.spring.events.annotation.EnableEventBus;
-import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
+import org.vaadin.spring.security.annotation.EnableVaadinManagedSecurity;
import com.vaadin.spring.annotation.UIScope;
@@ -34,7 +37,7 @@ import com.vaadin.spring.annotation.UIScope;
* The Management UI auto configuration.
*/
@Configuration
-@EnableVaadinSecurity
+@EnableVaadinManagedSecurity
@EnableVaadinExtensions
@EnableEventBus
@ConditionalOnClass(MgmtUiConfiguration.class)
@@ -68,6 +71,18 @@ public class MgmtUiAutoConfiguration {
return new HawkbitEventProvider();
}
+ /**
+ * A event permission checker bean which verifies supported events for the
+ * UI.
+ *
+ * @return the permission checker bean
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ UIEventPermissionChecker eventPermissionChecker(final SpPermissionChecker permChecker) {
+ return new HawkbitEventPermissionChecker(permChecker);
+ }
+
/**
* The UI scoped event push strategy. Session scope is necessary, that every
* UI has an own strategy.
@@ -80,6 +95,8 @@ public class MgmtUiAutoConfiguration {
* the ui event bus
* @param eventProvider
* the event provider
+ * @param eventPermissionChecker
+ * the event permission checker
* @param uiProperties
* the ui properties
* @return the push strategy bean
@@ -89,10 +106,12 @@ public class MgmtUiAutoConfiguration {
@UIScope
EventPushStrategy eventPushStrategy(final ConfigurableApplicationContext applicationContext,
final ScheduledExecutorService executorService, final UIEventBus eventBus,
- final UIEventProvider eventProvider, final UiProperties uiProperties) {
+ final UIEventProvider eventProvider, final UIEventPermissionChecker eventPermissionChecker,
+ final UiProperties uiProperties) {
final DelayedEventBusPushStrategy delayedEventBusPushStrategy = new DelayedEventBusPushStrategy(executorService,
- eventBus, eventProvider, uiProperties.getEvent().getPush().getDelay());
+ eventBus, eventProvider, eventPermissionChecker, uiProperties.getEvent().getPush().getDelay());
applicationContext.addApplicationListener(delayedEventBusPushStrategy);
+
return delayedEventBusPushStrategy;
}
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java
index 3871dad01..9fd9049e3 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/mgmt/ui/RedirectController.java
@@ -9,7 +9,7 @@
package org.eclipse.hawkbit.autoconfigure.mgmt.ui;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
/**
@@ -23,7 +23,7 @@ public class RedirectController {
/**
* @return redirect to the Management UI
*/
- @RequestMapping("/")
+ @GetMapping("/")
public ModelAndView home() {
return new ModelAndView("redirect:/UI/");
}
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java
index 6ecf9c1f6..4e5c68237 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/InMemoryUserManagementAutoConfiguration.java
@@ -32,6 +32,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.vaadin.spring.security.config.AuthenticationManagerConfigurer;
/**
* Auto-configuration for the in-memory-user-management.
@@ -40,7 +41,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
@Configuration
@ConditionalOnMissingBean(UserDetailsService.class)
@EnableConfigurationProperties({ MultiUserProperties.class })
-public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticationConfigurerAdapter {
+public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticationConfigurerAdapter
+ implements AuthenticationManagerConfigurer {
private static final String DEFAULT_TENANT = "DEFAULT";
diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java
index 655a8bc1c..1bb4ae651 100644
--- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java
+++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityManagedConfiguration.java
@@ -13,7 +13,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -26,7 +25,7 @@ import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.ddi.rest.resource.DdiApiConfiguration;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
-import org.eclipse.hawkbit.im.authentication.TenantUserPasswordAuthenticationToken;
+import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.im.authentication.UserAuthenticationFilter;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
import org.eclipse.hawkbit.mgmt.rest.resource.MgmtApiConfiguration;
@@ -62,7 +61,7 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@@ -97,11 +96,12 @@ import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import org.vaadin.spring.security.VaadinSecurityContext;
-import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
+import org.vaadin.spring.http.HttpService;
+import org.vaadin.spring.security.annotation.EnableVaadinSharedSecurity;
+import org.vaadin.spring.security.config.VaadinSharedSecurityConfiguration;
+import org.vaadin.spring.security.shared.VaadinAuthenticationSuccessHandler;
+import org.vaadin.spring.security.shared.VaadinUrlAuthenticationSuccessHandler;
import org.vaadin.spring.security.web.VaadinRedirectStrategy;
-import org.vaadin.spring.security.web.authentication.VaadinAuthenticationSuccessHandler;
-import org.vaadin.spring.security.web.authentication.VaadinUrlAuthenticationSuccessHandler;
/**
* All configurations related to HawkBit's authentication and authorization
@@ -530,11 +530,10 @@ public class SecurityManagedConfiguration {
// Only get the first client registration. Testing against every
// client could increase the
// attack vector
- ClientRegistration clientRegistration = null;
- for (final ClientRegistration cr : clientRegistrationRepository) {
- clientRegistration = cr;
- break;
- }
+ final ClientRegistration clientRegistration = clientRegistrationRepository != null
+ && clientRegistrationRepository.iterator().hasNext()
+ ? clientRegistrationRepository.iterator().next()
+ : null;
Assert.notNull(clientRegistration, "There must be a valid client registration");
httpSec.oauth2ResourceServer().jwt().jwkSetUri(clientRegistration.getProviderDetails().getJwkSetUri());
@@ -596,18 +595,13 @@ public class SecurityManagedConfiguration {
*/
@Configuration
@Order(400)
- @EnableVaadinSecurity
+ @EnableVaadinSharedSecurity
@ConditionalOnClass(MgmtUiConfiguration.class)
public static class UISecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
- @Autowired
- private VaadinSecurityContext vaadinSecurityContext;
-
@Autowired
private HawkbitSecurityProperties hawkbitSecurityProperties;
- private final VaadinUrlAuthenticationSuccessHandler handler;
-
@Autowired(required = false)
private OAuth2UserService oidcUserService;
@@ -620,14 +614,6 @@ public class SecurityManagedConfiguration {
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
-
- public UISecurityConfigurationAdapter(final VaadinRedirectStrategy redirectStrategy) {
- handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler();
- handler.setRedirectStrategy(redirectStrategy);
- handler.setDefaultTargetUrl("/UI/");
- handler.setTargetUrlParameter("r");
- }
-
/**
* Filter to protect the hawkBit management UI against to many requests.
*
@@ -651,17 +637,8 @@ public class SecurityManagedConfiguration {
return filterRegBean;
}
- /**
- * post construct for setting the authentication success handler for the
- * vaadin security context.
- */
- @PostConstruct
- public void afterPropertiesSet() {
- this.vaadinSecurityContext.addAuthenticationSuccessHandler(handler);
- }
-
@Override
- @Bean(name = "authenticationManager")
+ @Bean(name = VaadinSharedSecurityConfiguration.AUTHENTICATION_MANAGER_BEAN)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@@ -669,8 +646,13 @@ public class SecurityManagedConfiguration {
/**
* @return the vaadin success authentication handler
*/
- @Bean
- public VaadinAuthenticationSuccessHandler redirectSaveHandler() {
+ @Bean(name = VaadinSharedSecurityConfiguration.VAADIN_AUTHENTICATION_SUCCESS_HANDLER_BEAN)
+ public VaadinAuthenticationSuccessHandler redirectSaveHandler(final HttpService httpService,
+ final VaadinRedirectStrategy redirectStrategy) {
+ final VaadinUrlAuthenticationSuccessHandler handler = new TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(
+ httpService, redirectStrategy, "/UI/");
+ handler.setTargetUrlParameter("r");
+
return handler;
}
@@ -717,8 +699,8 @@ public class SecurityManagedConfiguration {
}
// UI
- httpSec.authorizeRequests().antMatchers("/UI/login/**", "/UI/UIDL/**").permitAll()
- .anyRequest().authenticated();
+ httpSec.authorizeRequests().antMatchers("/UI/login/**", "/UI/UIDL/**").permitAll().anyRequest()
+ .authenticated();
if (enableOidc) {
// OIDC
@@ -737,7 +719,7 @@ public class SecurityManagedConfiguration {
@Override
public void configure(final WebSecurity webSecurity) throws Exception {
- // Not security for static content
+ // No security for static content
webSecurity.ignoring().antMatchers("/documentation/**", "/VAADIN/**", "/*.*", "/docs/**");
}
}
@@ -756,30 +738,31 @@ class TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler extends
@Autowired
private SystemSecurityContext systemSecurityContext;
+ public TenantMetadataSavedRequestAwareVaadinAuthenticationSuccessHandler(final HttpService http,
+ final VaadinRedirectStrategy redirectStrategy, final String defaultTargetUrl) {
+ super(http, redirectStrategy, defaultTargetUrl);
+ }
+
@Override
public void onAuthenticationSuccess(final Authentication authentication) throws Exception {
-
- if (authentication.getClass().equals(TenantUserPasswordAuthenticationToken.class)) {
- systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata,
- ((TenantUserPasswordAuthenticationToken) authentication).getTenant().toString());
- } else if (authentication.getClass().equals(UsernamePasswordAuthenticationToken.class)) {
- // TODO: vaadin4spring-ext-security does not give us the
- // fullyAuthenticatedToken
- // in the GenericVaadinSecurity class. Only the token which has been
- // created in the
- // LoginView. This needs to be changed with the update of
- // vaadin4spring 0.0.7 because it
- // has been fixed.
- final String defaultTenant = "DEFAULT";
- systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, defaultTenant);
- }
+ systemSecurityContext.runAsSystemAsTenant(systemManagement::getTenantMetadata, getTenantFrom(authentication));
super.onAuthenticationSuccess(authentication);
}
+
+ private static String getTenantFrom(final Authentication authentication) {
+ final Object details = authentication.getDetails();
+ if (details instanceof TenantAwareAuthenticationDetails) {
+ return ((TenantAwareAuthenticationDetails) details).getTenant();
+ }
+
+ throw new InsufficientAuthenticationException("Authentication details/tenant info are not specified!");
+ }
}
/**
- * Servletfilter to create metadata after successful authentication over RESTful.
+ * Servletfilter to create metadata after successful authentication over
+ * RESTful.
*/
class AuthenticationSuccessTenantMetadataCreationFilter implements Filter {
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java
index 00b4d4c7d..5e83a0424 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java
@@ -21,9 +21,9 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
+import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
-import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
@@ -135,6 +135,21 @@ public interface SoftwareModuleManagement
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
Page findByAssignedTo(@NotNull Pageable pageable, long setId);
+ /**
+ * Returns count of all modules assigned to given {@link DistributionSet}.
+ *
+ * @param setId
+ * to search for
+ *
+ * @return count of {@link SoftwareModule}s that are assigned to given
+ * {@link DistributionSet}.
+ *
+ * @throws EntityNotFoundException
+ * if distribution set with given ID does not exist
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
+ long countByAssignedTo(long setId);
+
/**
* Filter {@link SoftwareModule}s with given
* {@link SoftwareModule#getName()} or {@link SoftwareModule#getVersion()}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java
index 6376f06b4..a4fc90e62 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetTagManagement.java
@@ -155,6 +155,16 @@ public interface TargetTagManagement {
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
Optional get(long id);
+ /**
+ * Finds {@link TargetTag} by given ids.
+ *
+ * @param ids
+ * the ids to for
+ * @return the found {@link TargetTag}s
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
+ List get(@NotEmpty Collection ids);
+
/**
* updates the {@link TargetTag}.
*
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java
index 386016337..583e78a09 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/AbstractActionEvent.java
@@ -16,6 +16,7 @@ import org.eclipse.hawkbit.repository.model.Action;
public abstract class AbstractActionEvent extends RemoteEntityEvent {
private static final long serialVersionUID = 1L;
+ private Long targetId;
private Long rolloutId;
private Long rolloutGroupId;
@@ -31,6 +32,8 @@ public abstract class AbstractActionEvent extends RemoteEntityEvent {
*
* @param action
* the created action
+ * @param targetId
+ * targetId identifier (optional)
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
@@ -38,13 +41,18 @@ public abstract class AbstractActionEvent extends RemoteEntityEvent {
* @param applicationId
* the origin application id
*/
- public AbstractActionEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
- final String applicationId) {
+ public AbstractActionEvent(final Action action, final Long targetId, final Long rolloutId,
+ final Long rolloutGroupId, final String applicationId) {
super(action, applicationId);
+ this.targetId = targetId;
this.rolloutId = rolloutId;
this.rolloutGroupId = rolloutGroupId;
}
+ public Long getTargetId() {
+ return targetId;
+ }
+
public Long getRolloutId() {
return rolloutId;
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java
index 859e90615..681fd987f 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionCreatedEvent.java
@@ -29,6 +29,8 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre
*
* @param action
* the created action
+ * @param targetId
+ * targetId identifier (optional)
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
@@ -36,9 +38,9 @@ public class ActionCreatedEvent extends AbstractActionEvent implements EntityCre
* @param applicationId
* the origin application id
*/
- public ActionCreatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
+ public ActionCreatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId,
final String applicationId) {
- super(action, rolloutId, rolloutGroupId, applicationId);
+ super(action, targetId, rolloutId, rolloutGroupId, applicationId);
}
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java
index e3a9fae93..6ebfda76a 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/ActionUpdatedEvent.java
@@ -29,6 +29,8 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd
*
* @param action
* the updated action
+ * @param targetId
+ * targetId identifier (optional)
* @param rolloutId
* rollout identifier (optional)
* @param rolloutGroupId
@@ -36,9 +38,9 @@ public class ActionUpdatedEvent extends AbstractActionEvent implements EntityUpd
* @param applicationId
* the origin application id
*/
- public ActionUpdatedEvent(final Action action, final Long rolloutId, final Long rolloutGroupId,
+ public ActionUpdatedEvent(final Action action, final Long targetId, final Long rolloutId, final Long rolloutGroupId,
final String applicationId) {
- super(action, rolloutId, rolloutGroupId, applicationId);
+ super(action, targetId, rolloutId, rolloutGroupId, applicationId);
}
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java
index fc3a6c207..ca4f07b71 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/SoftwareModuleTypeUpdatedEvent.java
@@ -8,13 +8,15 @@
*/
package org.eclipse.hawkbit.repository.event.remote.entity;
+import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
/**
* Defines the remote event for updating a {@link SoftwareModuleType}.
*
*/
-public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent {
+public class SoftwareModuleTypeUpdatedEvent extends RemoteEntityEvent
+ implements EntityUpdatedEvent {
private static final long serialVersionUID = 1L;
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java
index 9215fe46f..94e5a72af 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/AssignedSoftwareModule.java
@@ -10,13 +10,15 @@ package org.eclipse.hawkbit.repository.model;
import java.io.Serializable;
+import org.springframework.hateoas.Identifiable;
+
/**
* Use to display software modules for the selected distribution.
*
*/
-public class AssignedSoftwareModule implements Serializable {
+public class AssignedSoftwareModule implements Serializable, Identifiable {
- private static final long serialVersionUID = 6144585781451168439L;
+ private static final long serialVersionUID = 1L;
private final SoftwareModule softwareModule;
@@ -89,4 +91,9 @@ public class AssignedSoftwareModule implements Serializable {
return true;
}
+ @Override
+ public Long getId() {
+ return softwareModule.getId();
+ }
+
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java
index 6f011be68..1b36d8927 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetType.java
@@ -18,22 +18,7 @@ import java.util.Set;
* assigned to a {@link Target}.
*
*/
-public interface DistributionSetType extends NamedEntity {
- /**
- * Maximum length of key.
- */
- int KEY_MAX_SIZE = 64;
-
- /**
- * Maximum length of colour in Management UI.
- */
- int COLOUR_MAX_SIZE = 16;
-
- /**
- * @return true if the type is deleted and only kept for
- * history purposes.
- */
- boolean isDeleted();
+public interface DistributionSetType extends Type {
/**
* @return immutable set of {@link SoftwareModuleType}s that need to be in a
@@ -60,6 +45,18 @@ public interface DistributionSetType extends NamedEntity {
return containsMandatoryModuleType(softwareModuleType) || containsOptionalModuleType(softwareModuleType);
}
+ /**
+ * Checks if the given {@link SoftwareModuleType} is in this
+ * {@link DistributionSetType}.
+ *
+ * @param softwareModuleTypeId
+ * search for by {@link SoftwareModuleType#getId()}
+ * @return true if found
+ */
+ default boolean containsModuleType(final Long softwareModuleTypeId) {
+ return containsMandatoryModuleType(softwareModuleTypeId) || containsOptionalModuleType(softwareModuleTypeId);
+ }
+
/**
* Checks if the given {@link SoftwareModuleType} is in
* {@link #getMandatoryModuleTypes()}.
@@ -118,11 +115,6 @@ public interface DistributionSetType extends NamedEntity {
*/
boolean areModuleEntriesIdentical(DistributionSetType dsType);
- /**
- * @return business key of this {@link DistributionSetType}.
- */
- String getKey();
-
/**
* @param distributionSet
* to check for completeness
@@ -130,10 +122,4 @@ public interface DistributionSetType extends NamedEntity {
* in the system.
*/
boolean checkComplete(DistributionSet distributionSet);
-
- /**
- * @return get color code to by used in management UI views.
- */
- String getColour();
-
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java
index a8ce522e1..12e7a1444 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/SoftwareModuleType.java
@@ -14,37 +14,11 @@ package org.eclipse.hawkbit.repository.model;
* specific information.
*
*/
-public interface SoftwareModuleType extends NamedEntity {
- /**
- * Maximum length of key.
- */
- int KEY_MAX_SIZE = 64;
-
- /**
- * Maximum length of colour in Management UI.
- */
- int COLOUR_MAX_SIZE = 16;
-
- /**
- * @return business key of this {@link SoftwareModuleType}.
- */
- String getKey();
+public interface SoftwareModuleType extends Type {
/**
* @return maximum assignments of an {@link SoftwareModule} of this type to
* a {@link DistributionSet}.
*/
int getMaxAssignments();
-
- /**
- * @return true if the type is deleted and only kept for
- * history purposes.
- */
- boolean isDeleted();
-
- /**
- * @return get color code to by used in management UI views.
- */
- String getColour();
-
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java
index 9193b47ef..41f7ccbc2 100644
--- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java
@@ -9,13 +9,14 @@
package org.eclipse.hawkbit.repository.model;
import org.eclipse.hawkbit.repository.model.Action.Status;
+import org.springframework.hateoas.Identifiable;
/**
*
* Target with action status.
*
*/
-public class TargetWithActionStatus {
+public class TargetWithActionStatus implements Identifiable {
private Target target;
@@ -46,4 +47,9 @@ public class TargetWithActionStatus {
this.status = status;
}
+ @Override
+ public Long getId() {
+ return target.getId();
+ }
+
}
diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Type.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Type.java
new file mode 100644
index 000000000..90c0134b2
--- /dev/null
+++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Type.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2019 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.model;
+
+/**
+ * {@link Type} is an abstract definition for {@link DistributionSetType}s and
+ * {@link SoftwareModuleType}s
+ */
+public interface Type extends NamedEntity {
+ /**
+ * Maximum length of key.
+ */
+ int KEY_MAX_SIZE = 64;
+
+ /**
+ * Maximum length of color in Management UI.
+ */
+ int COLOUR_MAX_SIZE = 16;
+
+ /**
+ * @return business key.
+ */
+ String getKey();
+
+ /**
+ * @return true if the type is deleted and only kept for
+ * history purposes.
+ */
+ boolean isDeleted();
+
+ /**
+ * @return get color code to be used in management UI views.
+ */
+ String getColour();
+
+}
diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java
index 0d81ec878..dd6c8d459 100644
--- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java
+++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/repository/builder/AbstractDistributionSetTypeUpdateCreate.java
@@ -41,18 +41,10 @@ public abstract class AbstractDistributionSetTypeUpdateCreate extends Abstrac
}
public Optional> getMandatory() {
- if (CollectionUtils.isEmpty(mandatory)) {
- return Optional.empty();
- }
-
return Optional.ofNullable(mandatory);
}
public Optional> getOptional() {
- if (CollectionUtils.isEmpty(optional)) {
- return Optional.empty();
- }
-
return Optional.ofNullable(optional);
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java
index 6fe288128..485d168fa 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/DefaultRolloutApprovalStrategy.java
@@ -8,16 +8,22 @@
*/
package org.eclipse.hawkbit.repository.jpa;
+import java.util.Collection;
+
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.UserPrincipal;
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.Rollout;
+import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.util.StringUtils;
/**
* Default implementation of {@link RolloutApprovalStrategy}. Decides whether
@@ -33,38 +39,50 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
private final SystemSecurityContext systemSecurityContext;
- DefaultRolloutApprovalStrategy(UserDetailsService userDetailsService,
- TenantConfigurationManagement tenantConfigurationManagement,
- SystemSecurityContext systemSecurityContext) {
+ DefaultRolloutApprovalStrategy(final UserDetailsService userDetailsService,
+ final TenantConfigurationManagement tenantConfigurationManagement,
+ final SystemSecurityContext systemSecurityContext) {
this.userDetailsService = userDetailsService;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.systemSecurityContext = systemSecurityContext;
}
/**
- * Returns true, if rollout approval is enabled and rollout creator doesn't have
- * approval role.
+ * Returns true, if rollout approval is enabled and rollout creator doesn't
+ * have approval role.
*/
@Override
public boolean isApprovalNeeded(final Rollout rollout) {
- final UserDetails userDetails = this.getActor(rollout);
- final boolean approvalEnabled = this.tenantConfigurationManagement
- .getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue();
- return approvalEnabled && userDetails.getAuthorities().stream()
- .noneMatch(authority -> SpPermission.APPROVE_ROLLOUT.equals(authority.getAuthority()));
+ return isApprovalEnabled() && hasNoApproveRolloutPermission(getActor(rollout).getAuthorities());
}
+ private boolean isApprovalEnabled() {
+ return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
+ .getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue());
+ }
- private UserDetails getActor(Rollout rollout) {
- final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() : rollout.getCreatedBy();
- return systemSecurityContext.runAsSystem(() -> {
- UserPrincipal userPrincipal = (UserPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
- if(userPrincipal.getUsername().equals(actor)) {
- return userPrincipal;
- } else {
- return this.userDetailsService.loadUserByUsername(actor);
+ private UserDetails getActor(final Rollout rollout) {
+ // rollout state transition from CREATING to CREATED is managed by
+ // scheduler under SYSTEM user context, thus we get the
+ // user based on the properties of initially created rollout entity
+ if (RolloutStatus.CREATING == rollout.getStatus()) {
+ final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy()
+ : rollout.getCreatedBy();
+ if (!StringUtils.isEmpty(actor)) {
+ return systemSecurityContext.runAsSystem(() -> userDetailsService.loadUserByUsername(actor));
}
- });
+ }
+
+ return (UserPrincipal) getCurrentAuthentication().getPrincipal();
+ }
+
+ private static Authentication getCurrentAuthentication() {
+ return SecurityContextHolder.getContext().getAuthentication();
+ }
+
+ private static boolean hasNoApproveRolloutPermission(final Collection extends GrantedAuthority> authorities) {
+ return authorities.stream()
+ .noneMatch(authority -> SpPermission.APPROVE_ROLLOUT.equals(authority.getAuthority()));
}
/***
@@ -74,12 +92,12 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
* rollout to create approval task for.
*/
@Override
- public void onApprovalRequired(Rollout rollout) {
+ public void onApprovalRequired(final Rollout rollout) {
// do nothing per default, can be extended by further implementations.
}
@Override
- public String getApprovalUser(Rollout rollout) {
- return SecurityContextHolder.getContext().getAuthentication().getName();
+ public String getApprovalUser(final Rollout rollout) {
+ return getCurrentAuthentication().getName();
}
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java
index 12174e745..abc9465fd 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetManagement.java
@@ -630,7 +630,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
specList.add(spec);
}
- if (isDSWithNoTagSelected(distributionSetFilter) || isTagsSelected(distributionSetFilter)) {
+ if (hasTagsFilterActive(distributionSetFilter)) {
spec = DistributionSetSpecification.hasTags(distributionSetFilter.getTagNames(),
distributionSetFilter.getSelectDSWithNoTag());
specList.add(spec);
@@ -666,12 +666,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
- private static Boolean isDSWithNoTagSelected(final DistributionSetFilter distributionSetFilter) {
- return distributionSetFilter.getSelectDSWithNoTag() != null && distributionSetFilter.getSelectDSWithNoTag();
- }
+ private static boolean hasTagsFilterActive(final DistributionSetFilter distributionSetFilter) {
+ final boolean isNoTagActive = Boolean.TRUE.equals(distributionSetFilter.getSelectDSWithNoTag());
+ final boolean isAtLeastOneTagActive = !CollectionUtils.isEmpty(distributionSetFilter.getTagNames());
- private static Boolean isTagsSelected(final DistributionSetFilter distributionSetFilter) {
- return !CollectionUtils.isEmpty(distributionSetFilter.getTagNames());
+ return isNoTagActive || isAtLeastOneTagActive;
}
/**
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java
index 48f34deaa..390ac3c12 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDistributionSetTypeManagement.java
@@ -13,7 +13,11 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.LongFunction;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
@@ -21,9 +25,9 @@ import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate;
+import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
-import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
@@ -95,18 +99,51 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
update.getDescription().ifPresent(type::setDescription);
update.getColour().ifPresent(type::setColour);
- if (hasModules(update)) {
+ if (hasModuleChanges(update)) {
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(update.getId());
- update.getMandatory().ifPresent(
- mand -> softwareModuleTypeRepository.findAllById(mand).forEach(type::addMandatoryModuleType));
- update.getOptional().ifPresent(
- opt -> softwareModuleTypeRepository.findAllById(opt).forEach(type::addOptionalModuleType));
+ final Collection currentMandatorySmTypeIds = type.getMandatoryModuleTypes().stream()
+ .map(SoftwareModuleType::getId).collect(Collectors.toSet());
+ final Collection currentOptionalSmTypeIds = type.getOptionalModuleTypes().stream()
+ .map(SoftwareModuleType::getId).collect(Collectors.toSet());
+ final Collection currentSmTypeIds = Stream
+ .concat(currentMandatorySmTypeIds.stream(), currentOptionalSmTypeIds.stream())
+ .collect(Collectors.toSet());
+
+ final Collection updatedMandatorySmTypeIds = update.getMandatory().orElse(currentMandatorySmTypeIds);
+ final Collection updatedOptionalSmTypeIds = update.getOptional().orElse(currentOptionalSmTypeIds);
+ final Collection updatedSmTypeIds = Stream
+ .concat(updatedMandatorySmTypeIds.stream(), updatedOptionalSmTypeIds.stream())
+ .collect(Collectors.toSet());
+
+ addModuleTypes(currentMandatorySmTypeIds, updatedMandatorySmTypeIds, type::addMandatoryModuleType);
+ addModuleTypes(currentOptionalSmTypeIds, updatedOptionalSmTypeIds, type::addOptionalModuleType);
+
+ removeModuleTypes(currentSmTypeIds, updatedSmTypeIds, type::removeModuleType);
}
return distributionSetTypeRepository.save(type);
}
+ private void addModuleTypes(final Collection currentSmTypeIds, final Collection updatedSmTypeIds,
+ final Function addModuleTypeCallback) {
+ final Set smTypeIdsToAdd = updatedSmTypeIds.stream().filter(id -> !currentSmTypeIds.contains(id))
+ .collect(Collectors.toSet());
+ if (!CollectionUtils.isEmpty(smTypeIdsToAdd)) {
+ softwareModuleTypeRepository.findAllById(smTypeIdsToAdd).forEach(addModuleTypeCallback::apply);
+ }
+ }
+
+ private static void removeModuleTypes(final Collection currentSmTypeIds,
+ final Collection updatedSmTypeIds,
+ final LongFunction removeModuleTypeCallback) {
+ final Set smTypeIdsToRemove = currentSmTypeIds.stream().filter(id -> !updatedSmTypeIds.contains(id))
+ .collect(Collectors.toSet());
+ if (!CollectionUtils.isEmpty(smTypeIdsToRemove)) {
+ smTypeIdsToRemove.forEach(removeModuleTypeCallback::apply);
+ }
+ }
+
@Override
@Transactional
@Retryable(include = {
@@ -258,7 +295,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
.orElseThrow(() -> new EntityNotFoundException(DistributionSetType.class, setId));
}
- private static boolean hasModules(final GenericDistributionSetTypeUpdate update) {
+ private static boolean hasModuleChanges(final GenericDistributionSetTypeUpdate update) {
return update.getOptional().isPresent() || update.getMandatory().isPresent();
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java
index 26a31d931..6d2134542 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java
@@ -44,8 +44,6 @@ import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEve
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
-import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
-import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
@@ -61,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
-import org.eclipse.hawkbit.repository.jpa.utils.TenantConfigHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -1019,7 +1016,9 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
update.getActionType().ifPresent(rollout::setActionType);
update.getForcedTime().ifPresent(rollout::setForcedTime);
update.getWeight().ifPresent(rollout::setWeight);
- update.getStartAt().ifPresent(rollout::setStartAt);
+ // reseting back to manual start is done by setting start at time to
+ // null
+ rollout.setStartAt(update.getStartAt().orElse(null));
update.getSet().ifPresent(setId -> {
final DistributionSet set = distributionSetManagement.get(setId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
index 860265f5b..ea85e11f6 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSoftwareModuleManagement.java
@@ -21,8 +21,11 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
+import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Expression;
+import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
@@ -344,70 +347,43 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
@Override
public Slice findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc(
- final Pageable pageable, final long orderByDistributionId, final String searchText, final Long typeId) {
+ final Pageable pageable, final long dsId, final String searchText, final Long smTypeId) {
+ final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+ final CriteriaQuery query = cb.createTupleQuery();
+ final Root smRoot = query.from(JpaSoftwareModule.class);
+
+ final ListJoin assignedDsList = smRoot
+ .join(JpaSoftwareModule_.assignedTo, JoinType.LEFT);
+
+ final Expression assignedCaseMax = cb.max(
+ cb. selectCase(assignedDsList.get(JpaDistributionSet_.id)).when(dsId, 1).otherwise(0));
+
+ query.multiselect(smRoot.alias("sm"), assignedCaseMax.alias("assigned"));
+
+ final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, smTypeId),
+ smRoot, query, cb);
+
+ if (specPredicate.length > 0) {
+ query.where(specPredicate);
+ }
+
+ query.groupBy(smRoot);
+
+ query.orderBy(cb.desc(assignedCaseMax), cb.asc(smRoot.get(JpaSoftwareModule_.name)),
+ cb.asc(smRoot.get(JpaSoftwareModule_.version)));
+
+ final int pageSize = pageable.getPageSize();
+ final List smWithAssignedFlagList = entityManager.createQuery(query)
+ .setFirstResult((int) pageable.getOffset()).setMaxResults(pageSize).getResultList();
+ final boolean hasNext = smWithAssignedFlagList.size() > pageSize;
final List resultList = new ArrayList<>();
- final int pageSize = pageable.getPageSize();
- final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
- // get the assigned software modules
- final CriteriaQuery assignedQuery = cb.createQuery(JpaSoftwareModule.class);
- final Root assignedRoot = assignedQuery.from(JpaSoftwareModule.class);
- assignedQuery.distinct(true);
- final ListJoin assignedDsJoin = assignedRoot
- .join(JpaSoftwareModule_.assignedTo);
- // build the specifications and then to predicates necessary by the
- // given filters
- final Predicate[] specPredicate = specificationsToPredicate(buildSpecificationList(searchText, typeId),
- assignedRoot, assignedQuery, cb,
- cb.equal(assignedDsJoin.get(JpaDistributionSet_.id), orderByDistributionId));
- // if we have some predicates then add it to the where clause of the
- // multi select
- assignedQuery.where(specPredicate);
- assignedQuery.orderBy(cb.asc(assignedRoot.get(JpaSoftwareModule_.name)),
- cb.asc(assignedRoot.get(JpaSoftwareModule_.version)));
- // don't page the assigned query on database, we need all assigned
- // software modules to filter
- // them out in the unassigned query
- final List assignedSoftwareModules = entityManager.createQuery(assignedQuery)
- .getResultList();
- // map result
- if (pageable.getOffset() < assignedSoftwareModules.size()) {
- assignedSoftwareModules
- .subList((int) pageable.getOffset(),
- Math.min(assignedSoftwareModules.size(), pageable.getPageSize()))
- .forEach(sw -> resultList.add(new AssignedSoftwareModule(sw, true)));
- }
+ smWithAssignedFlagList.forEach(smWithAssignedFlag -> resultList
+ .add(new AssignedSoftwareModule(smWithAssignedFlag.get("sm", JpaSoftwareModule.class),
+ smWithAssignedFlag.get("assigned", Number.class).longValue() == 1)));
- if (assignedSoftwareModules.size() >= pageSize) {
- return new SliceImpl<>(resultList);
- }
-
- // get the unassigned software modules
- final CriteriaQuery unassignedQuery = cb.createQuery(JpaSoftwareModule.class);
- unassignedQuery.distinct(true);
- final Root unassignedRoot = unassignedQuery.from(JpaSoftwareModule.class);
-
- Predicate[] unassignedSpec;
- if (!assignedSoftwareModules.isEmpty()) {
- unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, typeId), unassignedRoot,
- unassignedQuery, cb, cb.not(unassignedRoot.get(JpaSoftwareModule_.id).in(
- assignedSoftwareModules.stream().map(SoftwareModule::getId).collect(Collectors.toList()))));
- } else {
- unassignedSpec = specificationsToPredicate(buildSpecificationList(searchText, typeId), unassignedRoot,
- unassignedQuery, cb);
- }
-
- unassignedQuery.where(unassignedSpec);
- unassignedQuery.orderBy(cb.asc(unassignedRoot.get(JpaSoftwareModule_.name)),
- cb.asc(unassignedRoot.get(JpaSoftwareModule_.version)));
- final List unassignedSoftwareModules = entityManager.createQuery(unassignedQuery)
- .setFirstResult((int) Math.max(0, pageable.getOffset() - assignedSoftwareModules.size()))
- .setMaxResults(pageSize).getResultList();
- // map result
- unassignedSoftwareModules.forEach(sw -> resultList.add(new AssignedSoftwareModule(sw, false)));
-
- return new SliceImpl<>(resultList);
+ return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext);
}
private List> buildSpecificationList(final String searchText, final Long typeId) {
@@ -464,6 +440,15 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
return softwareModuleRepository.findByAssignedToId(pageable, setId);
}
+ @Override
+ public long countByAssignedTo(final long setId) {
+ if (!distributionSetRepository.existsById(setId)) {
+ throw new EntityNotFoundException(DistributionSet.class, setId);
+ }
+
+ return softwareModuleRepository.countByAssignedToId(setId);
+ }
+
@Override
@Transactional
@Retryable(include = {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java
index 4cb23df05..8e1450480 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java
@@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -22,6 +23,7 @@ import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
+import javax.persistence.criteria.MapJoin;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
@@ -293,8 +295,7 @@ public class JpaTargetManagement implements TargetManagement {
public Optional getMetaDataByControllerId(final String controllerId, final String key) {
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
- return targetMetadataRepository.findById(new TargetMetadataCompositeKey(targetId, key))
- .map(t -> t);
+ return targetMetadataRepository.findById(new TargetMetadataCompositeKey(targetId, key)).map(t -> t);
}
@Override
@@ -457,7 +458,7 @@ public class JpaTargetManagement implements TargetManagement {
if ((filterParams.getFilterByStatus() != null) && !filterParams.getFilterByStatus().isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus()));
}
- if (filterParams.getOverdueState() != null) {
+ if (filterParams.getOverdueState() != null && filterParams.getOverdueState()) {
specList.add(TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp()));
}
if (filterParams.getFilterByDistributionId() != null) {
@@ -478,8 +479,11 @@ public class JpaTargetManagement implements TargetManagement {
}
private static boolean hasTagsFilterActive(final FilterParams filterParams) {
- return ((filterParams.getSelectTargetWithNoTag() != null) && filterParams.getSelectTargetWithNoTag())
- || ((filterParams.getFilterByTagNames() != null) && (filterParams.getFilterByTagNames().length > 0));
+ final boolean isNoTagActive = Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTag());
+ final boolean isAtLeastOneTagActive = filterParams.getFilterByTagNames() != null
+ && filterParams.getFilterByTagNames().length > 0;
+
+ return isNoTagActive || isAtLeastOneTagActive;
}
private Slice findByCriteriaAPI(final Pageable pageable, final List> specList) {
@@ -611,16 +615,10 @@ public class JpaTargetManagement implements TargetManagement {
}
// add the order to the multi select first based on the selectCase
query.orderBy(cb.asc(selectCase), cb.desc(targetRoot.get(JpaTarget_.id)));
- // the result is a Object[] due the fact that the selectCase is an extra
- // column, so it cannot
- // be mapped directly to a Target entity because the selectCase is not a
- // attribute of the
- // Target entity, the the Object array contains the Target on the first
- // index of the array and
- // the 2nd contains the selectCase int value.
+
final int pageSize = pageable.getPageSize();
final List resultList = entityManager.createQuery(query).setFirstResult((int) pageable.getOffset())
- .setMaxResults(pageSize + 1).getResultList();
+ .setMaxResults(pageSize).getResultList();
final boolean hasNext = resultList.size() > pageSize;
return new SliceImpl<>(Collections.unmodifiableList(resultList), pageable, hasNext);
}
@@ -785,9 +783,20 @@ public class JpaTargetManagement implements TargetManagement {
@Override
public Map getControllerAttributes(final String controllerId) {
- final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
+ final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
+ final CriteriaQuery