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 4b4fca00c..27a06d387 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 @@ -11,18 +11,24 @@ package org.eclipse.hawkbit.autoconfigure.security; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.regex.Pattern; import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator; import org.eclipse.hawkbit.im.authentication.PermissionUtils; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; -import org.eclipse.hawkbit.im.authentication.UserPrincipal; +import org.eclipse.hawkbit.im.authentication.UserTenantAware; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -30,110 +36,130 @@ import org.springframework.security.config.annotation.authentication.configurati import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.ObjectUtils; /** - * Auto-configuration for the in-memory-user-management. - * + * Autoconfiguration for the in-memory-user-management. */ @Configuration -@ConditionalOnMissingBean(UserDetailsService.class) -@EnableConfigurationProperties({ MultiUserProperties.class }) +@Order(Ordered.HIGHEST_PRECEDENCE) +@EnableConfigurationProperties({ TenantAwareUserProperties.class }) public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticationConfigurerAdapter { private static final String DEFAULT_TENANT = "DEFAULT"; - private final SecurityProperties securityProperties; + private final UserDetailsService userDetailsService; - private final MultiUserProperties multiUserProperties; - - InMemoryUserManagementAutoConfiguration(final SecurityProperties securityProperties, - final MultiUserProperties multiUserProperties) { - this.securityProperties = securityProperties; - this.multiUserProperties = multiUserProperties; + InMemoryUserManagementAutoConfiguration( + final SecurityProperties securityProperties, + final TenantAwareUserProperties userTenantAwareProperties, + final Optional passwordEncoder) { + userDetailsService = userDetailsService( + securityProperties, userTenantAwareProperties, passwordEncoder.orElse(null)); } @Override - public void configure(final AuthenticationManagerBuilder auth) throws Exception { + public void configure(final AuthenticationManagerBuilder auth) { final DaoAuthenticationProvider userDaoAuthenticationProvider = new TenantDaoAuthenticationProvider(); - userDaoAuthenticationProvider.setUserDetailsService(userDetailsService()); + userDaoAuthenticationProvider.setUserDetailsService(userDetailsService); auth.authenticationProvider(userDaoAuthenticationProvider); } - /** - * @return the user details service to load a user from memory user manager. - */ - @Bean - @ConditionalOnMissingBean - UserDetailsService userDetailsService() { - - final List userPrincipals = new ArrayList<>(); - for (final MultiUserProperties.User user : multiUserProperties.getUsers()) { - final List permissions = user.getPermissions(); - List authorityList; - // Allows ALL as a shorthand for all permissions - if (permissions.size() == 1 && "ALL".equals(permissions.get(0))) { - authorityList = PermissionUtils.createAllAuthorityList(); - } else { - authorityList = createAuthoritiesFromList(permissions); - } - - final UserPrincipal userPrincipal = new UserPrincipal(user.getUsername(), user.getPassword(), - user.getFirstname(), user.getLastname(), user.getUsername(), user.getEmail(), DEFAULT_TENANT, - authorityList); + private static UserDetailsService userDetailsService( + final SecurityProperties securityProperties, + final TenantAwareUserProperties userTenantAwareProperties, + final PasswordEncoder passwordEncoder) { + final List userPrincipals = new ArrayList<>(); + userTenantAwareProperties.getUsers().forEach((username, user) -> { + final UserTenantAware userPrincipal = new UserTenantAware( + username, password(user.getPassword(), passwordEncoder), + createAuthorities(user.getRoles(), Collections::emptyList), + ObjectUtils.isEmpty(user.getTenant()) ? DEFAULT_TENANT : user.getTenant()); userPrincipals.add(userPrincipal); - } + }); - // If no users are configured through the multi user properties, set up - // the default user from security properties + // If no tenant users are configured through the tenant user properties, set up + // the default user from spring security properties as super DEFAULT tenant user if (userPrincipals.isEmpty()) { - final String name = securityProperties.getUser().getName(); - final String password = securityProperties.getUser().getPassword(); - final List roles = securityProperties.getUser().getRoles(); - final List authorityList = roles.isEmpty() ? PermissionUtils.createAllAuthorityList() - : createAuthoritiesFromList(roles); userPrincipals - .add(new UserPrincipal(name, password, name, name, name, null, DEFAULT_TENANT, authorityList)); + .add(new UserTenantAware( + securityProperties.getUser().getName(), + password(securityProperties.getUser().getPassword(), passwordEncoder), + createAuthorities( + securityProperties.getUser().getRoles(), PermissionUtils::createAllAuthorityList), + DEFAULT_TENANT)); + } else if (securityProperties != null && securityProperties.getUser() != null && + !securityProperties.getUser().isPasswordGenerated()) { + // otherwise if the security user is explicitly setup (no autogenerated password) + // set it up as generic non tenant user + userPrincipals + .add(new User( + securityProperties.getUser().getName(), + password(securityProperties.getUser().getPassword(), passwordEncoder), + createAuthorities( + securityProperties.getUser().getRoles(), PermissionUtils::createAllAuthorityList))); } - return new FixedInMemoryUserPrincipalUserDetailsService(userPrincipals); + return new FixedInMemoryTenantAwareUserDetailsService(userPrincipals); } - private static List createAuthoritiesFromList(final List userAuthorities) { - final List grantedAuthorityList = new ArrayList<>(userAuthorities.size()); - for (final String permission : userAuthorities) { + private static String password(final String password, final PasswordEncoder passwordEncoder) { + return passwordEncoder == null && !Pattern.compile("^\\{.+}.*$").matcher(password).matches() ? + "{noop}" + password : password; + } + + private static List createAuthorities( + final List userPermissions, final Supplier> defaultRolesSupplier) { + if (ObjectUtils.isEmpty(userPermissions)) { + return defaultRolesSupplier.get(); + } + + // Allows ALL as a shorthand for all permissions + if (userPermissions.size() == 1 && "ALL".equals(userPermissions.get(0))) { + return PermissionUtils.createAllAuthorityList(); + } + + final List grantedAuthorityList = new ArrayList<>(userPermissions.size()); + for (final String permission : userPermissions) { grantedAuthorityList.add(new SimpleGrantedAuthority(permission)); grantedAuthorityList.add(new SimpleGrantedAuthority("ROLE_" + permission)); } return grantedAuthorityList; } - private static class FixedInMemoryUserPrincipalUserDetailsService implements UserDetailsService { - private final HashMap userPrincipalMap = new HashMap<>(); + private static class FixedInMemoryTenantAwareUserDetailsService implements UserDetailsService { - public FixedInMemoryUserPrincipalUserDetailsService(final Collection userPrincipals) { - for (final UserPrincipal user : userPrincipals) { - userPrincipalMap.put(user.getUsername(), user); + private final HashMap userMap = new HashMap<>(); + + private FixedInMemoryTenantAwareUserDetailsService(final Collection userPrincipals) { + for (final User user : userPrincipals) { + userMap.put(user.getUsername(), user); } } - private static UserPrincipal clone(final UserPrincipal a) { - return new UserPrincipal(a.getUsername(), a.getPassword(), a.getFirstname(), a.getLastname(), - a.getLoginname(), a.getEmail(), a.getTenant(), a.getAuthorities()); - } - @Override public UserDetails loadUserByUsername(final String username) { - final UserPrincipal userPrincipal = userPrincipalMap.get(username); - if (userPrincipal == null) { + final User user = userMap.get(username); + if (user == null) { throw new UsernameNotFoundException("No such user"); } // Spring mutates the data, so we must return a copy here - return clone(userPrincipal); + return clone(user); } + private static User clone(final User user) { + if (user instanceof UserTenantAware) { + return new UserTenantAware(user.getUsername(), user.getPassword(), user.getAuthorities(), + ((UserTenantAware)user).getTenant()); + } else { + return new User(user.getUsername(), user.getPassword(), user.getAuthorities()); + } + } } /** @@ -156,4 +182,4 @@ public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticatio return result; } } -} +} \ No newline at end of file diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/MultiUserProperties.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/MultiUserProperties.java deleted file mode 100644 index 6d6af6765..000000000 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/MultiUserProperties.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2019 devolo AG 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.autoconfigure.security; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -@ConfigurationProperties("hawkbit.server.im") -public class MultiUserProperties { - private List users = new ArrayList<>(); - - public List getUsers() { - return users; - } - - public void setUsers(List users) { - this.users = users; - } - - public static class User { - private String username; - private String password; - private String firstname; - private String lastname; - private String email; - private List permissions = new ArrayList<>(); - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public List getPermissions() { - return permissions; - } - - public void setPermissions(List permissions) { - this.permissions = permissions; - } - } -} diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/OidcUserManagementAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/OidcUserManagementAutoConfiguration.java index f5833dd5b..ddcf5031c 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/OidcUserManagementAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/OidcUserManagementAutoConfiguration.java @@ -46,7 +46,6 @@ import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java index 53b462033..24b593d30 100644 --- a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/SecurityAutoConfiguration.java @@ -15,7 +15,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.eclipse.hawkbit.ContextAware; -import org.eclipse.hawkbit.autoconfigure.security.MultiUserProperties.User; +import org.eclipse.hawkbit.autoconfigure.security.TenantAwareUserProperties.User; import org.eclipse.hawkbit.im.authentication.PermissionService; import org.eclipse.hawkbit.security.DdiSecurityProperties; import org.eclipse.hawkbit.security.InMemoryUserAuthoritiesResolver; @@ -47,18 +47,17 @@ import org.springframework.util.CollectionUtils; * {@link EnableAutoConfiguration Auto-configuration} for security. */ @Configuration -@EnableConfigurationProperties({ SecurityProperties.class, DdiSecurityProperties.class, HawkbitSecurityProperties.class, - MultiUserProperties.class }) +@EnableConfigurationProperties({ + SecurityProperties.class, + DdiSecurityProperties.class, HawkbitSecurityProperties.class, TenantAwareUserProperties.class }) public class SecurityAutoConfiguration { /** * Creates a {@link ContextAware} (hence {@link TenantAware}) bean based on the given * {@link UserAuthoritiesResolver} and {@link SecurityContextSerializer}. * - * @param authoritiesResolver - * The user authorities/roles resolver - * @param securityContextSerializer - * The security context serializer. + * @param authoritiesResolver The user authorities/roles resolver + * @param securityContextSerializer The security context serializer. * * @return the {@link ContextAware} singleton bean. */ @@ -74,21 +73,19 @@ public class SecurityAutoConfiguration { * Creates a {@link UserAuthoritiesResolver} bean that is responsible for * resolving user authorities/roles. * - * @param securityProperties - * The Spring {@link SecurityProperties} for the security user - * @param multiUserProperties - * The {@link MultiUserProperties} for the managed users - * + * @param securityProperties The Spring {@link SecurityProperties} for the security user + * @param userTenantAwareProperties The {@link TenantAwareUserProperties} for the managed users * @return an {@link InMemoryUserAuthoritiesResolver} bean */ @Bean @ConditionalOnMissingBean public UserAuthoritiesResolver inMemoryAuthoritiesResolver(final SecurityProperties securityProperties, - final MultiUserProperties multiUserProperties) { - final List multiUsers = multiUserProperties.getUsers(); + final TenantAwareUserProperties userTenantAwareProperties) { + final Map userTenantAwares = userTenantAwareProperties.getUsers(); final Map> usersToPermissions; - if (!CollectionUtils.isEmpty(multiUsers)) { - usersToPermissions = multiUsers.stream().collect(Collectors.toMap(User::getUsername, User::getPermissions)); + if (!CollectionUtils.isEmpty(userTenantAwares)) { + usersToPermissions = userTenantAwares.entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getRoles())); } else { usersToPermissions = Collections.singletonMap(securityProperties.getUser().getName(), securityProperties.getUser().getRoles()); @@ -108,7 +105,7 @@ public class SecurityAutoConfiguration { /** * Creates the auditor aware. - * + * * @return the spring security auditor aware */ @Bean 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 01a9a01b9..f18e55fb7 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 @@ -98,18 +98,15 @@ import org.springframework.web.cors.CorsConfigurationSource; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ, proxyTargetClass = true, securedEnabled = true) -@Order(value = Ordered.HIGHEST_PRECEDENCE) -@PropertySource("classpath:/hawkbit-security-defaults.properties") +@Order(Ordered.HIGHEST_PRECEDENCE) +@PropertySource("classpath:hawkbit-security-defaults.properties") public class SecurityManagedConfiguration { private static final int DOS_FILTER_ORDER = -200; /** - * @return the {@link UserAuthenticationFilter} to include into the hawkBit - * security configuration. - * @throws Exception - * lazy bean exception maybe if the authentication manager - * cannot be instantiated + * @return the {@link UserAuthenticationFilter} to include into the hawkBit security configuration. + * @throws Exception lazy bean exception maybe if the authentication manager cannot be instantiated */ @Bean @ConditionalOnMissingBean diff --git a/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/TenantAwareUserProperties.java b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/TenantAwareUserProperties.java new file mode 100644 index 000000000..700f0063a --- /dev/null +++ b/hawkbit-autoconfigure/src/main/java/org/eclipse/hawkbit/autoconfigure/security/TenantAwareUserProperties.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2019 devolo AG 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.autoconfigure.security; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.Data; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration for hawwkBit static users. + */ +@Data +@ToString +@ConfigurationProperties("hawkbit.security.user") +public class TenantAwareUserProperties { + + private Map users = new HashMap<>(); + + @Data + @ToString + public static class User { + + @ToString.Exclude + private String password; + private List roles = new ArrayList<>(); + private String tenant; + } +} \ No newline at end of file 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 d01f8d018..b2c6e8511 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 @@ -12,18 +12,17 @@ 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.UserAuthoritiesResolver; 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.security.core.userdetails.User; import org.springframework.util.StringUtils; /** @@ -34,16 +33,17 @@ import org.springframework.util.StringUtils; */ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { - private final UserDetailsService userDetailsService; + private final UserAuthoritiesResolver userAuthoritiesResolver; private final TenantConfigurationManagement tenantConfigurationManagement; private final SystemSecurityContext systemSecurityContext; - DefaultRolloutApprovalStrategy(final UserDetailsService userDetailsService, + DefaultRolloutApprovalStrategy( + final UserAuthoritiesResolver userAuthoritiesResolver, final TenantConfigurationManagement tenantConfigurationManagement, final SystemSecurityContext systemSecurityContext) { - this.userDetailsService = userDetailsService; + this.userAuthoritiesResolver = userAuthoritiesResolver; this.tenantConfigurationManagement = tenantConfigurationManagement; this.systemSecurityContext = systemSecurityContext; } @@ -54,7 +54,7 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { */ @Override public boolean isApprovalNeeded(final Rollout rollout) { - return isApprovalEnabled() && hasNoApproveRolloutPermission(getActor(rollout).getAuthorities()); + return isApprovalEnabled() && hasNoApproveRolloutPermission(getActorAuthorities(rollout)); } private boolean isApprovalEnabled() { @@ -62,7 +62,7 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { .getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue()); } - private UserDetails getActor(final Rollout rollout) { + private Collection getActorAuthorities(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 @@ -70,20 +70,21 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy { final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() : rollout.getCreatedBy(); if (!StringUtils.isEmpty(actor)) { - return systemSecurityContext.runAsSystem(() -> userDetailsService.loadUserByUsername(actor)); + return systemSecurityContext.runAsSystem( + () -> userAuthoritiesResolver.getUserAuthorities(rollout.getTenant(), actor)); } } - return (UserPrincipal) getCurrentAuthentication().getPrincipal(); + return ((User) getCurrentAuthentication().getPrincipal()).getAuthorities().stream() + .map(GrantedAuthority::getAuthority).toList(); } private static Authentication getCurrentAuthentication() { return SecurityContextHolder.getContext().getAuthentication(); } - private static boolean hasNoApproveRolloutPermission(final Collection authorities) { - return authorities.stream() - .noneMatch(authority -> SpPermission.APPROVE_ROLLOUT.equals(authority.getAuthority())); + private static boolean hasNoApproveRolloutPermission(final Collection authorities) { + return authorities.stream().noneMatch(SpPermission.APPROVE_ROLLOUT::equals); } /*** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index f2f6be525..ca083249a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -163,6 +163,7 @@ import org.eclipse.hawkbit.security.HawkbitSecurityProperties; import org.eclipse.hawkbit.security.SecurityTokenGenerator; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; +import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.eclipse.persistence.config.PersistenceUnitProperties; import org.hibernate.validator.BaseHibernateValidatorConfiguration; import org.springframework.beans.BeansException; @@ -193,7 +194,6 @@ import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect; import org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter; import org.springframework.retry.annotation.EnableRetry; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; @@ -778,10 +778,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { */ @Bean @ConditionalOnMissingBean - RolloutApprovalStrategy rolloutApprovalStrategy(final UserDetailsService userDetailsService, + RolloutApprovalStrategy rolloutApprovalStrategy(final UserAuthoritiesResolver userAuthoritiesResolver, final TenantConfigurationManagement tenantConfigurationManagement, final SystemSecurityContext systemSecurityContext) { - return new DefaultRolloutApprovalStrategy(userDetailsService, tenantConfigurationManagement, + return new DefaultRolloutApprovalStrategy(userAuthoritiesResolver, tenantConfigurationManagement, systemSecurityContext); } diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/SecurityContextSwitch.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/SecurityContextSwitch.java index e341fdbc2..bef7bbf05 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/SecurityContextSwitch.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/SecurityContextSwitch.java @@ -18,7 +18,7 @@ import java.util.concurrent.Callable; import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; -import org.eclipse.hawkbit.im.authentication.UserPrincipal; +import org.eclipse.hawkbit.im.authentication.UserTenantAware; import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; @@ -163,8 +163,7 @@ public class SecurityContextSwitch { authorities = annotation.authorities(); } final TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken( - new UserPrincipal(annotation.principal(), annotation.principal(), annotation.principal(), - annotation.principal(), null, annotation.tenantId()), + new UserTenantAware(annotation.principal(), annotation.tenantId()), annotation.credentials(), authorities); testingAuthenticationToken.setDetails( new TenantAwareAuthenticationDetails(annotation.tenantId(), annotation.controller())); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/WithUser.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/WithUser.java index fa97a47e0..afe42300e 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/WithUser.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/WithUser.java @@ -25,7 +25,7 @@ import java.lang.annotation.Target; */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) -@WithSecurityContext(factory = WithUser.WithUserPrincipalSecurityContextFactory.class) +@WithSecurityContext(factory = WithUser.WithTenantAwareUserSecurityContextFactory.class) @Inherited public @interface WithUser { @@ -80,10 +80,10 @@ public @interface WithUser { boolean controller() default false; - class WithUserPrincipalSecurityContextFactory implements WithSecurityContextFactory { + class WithTenantAwareUserSecurityContextFactory implements WithSecurityContextFactory { @Override - public SecurityContext createSecurityContext(final WithUser withUserPrincipal) { - return new SecurityContextSwitch.WithUserSecurityContext(withUserPrincipal); + public SecurityContext createSecurityContext(final WithUser withTenantAwareUser) { + return new SecurityContextSwitch.WithUserSecurityContext(withTenantAwareUser); } } } \ No newline at end of file diff --git a/hawkbit-runtime/hawkbit-mgmt-server/src/main/resources/application.properties b/hawkbit-runtime/hawkbit-mgmt-server/src/main/resources/application.properties index 0edce0d7f..104f13a04 100644 --- a/hawkbit-runtime/hawkbit-mgmt-server/src/main/resources/application.properties +++ b/hawkbit-runtime/hawkbit-mgmt-server/src/main/resources/application.properties @@ -34,12 +34,10 @@ spring.rabbitmq.virtual-host=/ spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 -# Define own users instead of default "admin" user: -#hawkbit.server.im.users[0].username=hawkbit -#hawkbit.server.im.users[0].password={noop}isAwesome! -#hawkbit.server.im.users[0].firstname=Eclipse -#hawkbit.server.im.users[0].lastname=HawkBit -#hawkbit.server.im.users[0].permissions=ALL +# Define own (my_user) users instead together default "admin" (system-wide) user: +#hawkbit.security.user.my_user.password={noop}isAwesome! +#hawkbit.security.user.my_user.roles=ALL +#hawkbit.security.user.my_user.tenant=DEFAULT # Enable CORS and specify the allowed origins: #hawkbit.server.security.cors.enabled=true diff --git a/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties b/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties index 1b01e0814..c6418aa21 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties +++ b/hawkbit-runtime/hawkbit-update-server/src/main/resources/application.properties @@ -35,12 +35,10 @@ spring.rabbitmq.virtual-host=/ spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 -# Define own users instead of default "admin" user: -#hawkbit.server.im.users[0].username=hawkbit -#hawkbit.server.im.users[0].password={noop}isAwesome! -#hawkbit.server.im.users[0].firstname=Eclipse -#hawkbit.server.im.users[0].lastname=HawkBit -#hawkbit.server.im.users[0].permissions=ALL +# Define own (my_user) users instead together default "admin" (system-wide) user: +#hawkbit.security.user.my_user.password={noop}isAwesome! +#hawkbit.security.user.my_user.roles=ALL +#hawkbit.security.user.my_user.tenant=DEFAULT # Enable CORS and specify the allowed origins: #hawkbit.server.security.cors.enabled=true diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/UserPrincipal.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/UserPrincipal.java deleted file mode 100644 index 5b88fcc13..000000000 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/UserPrincipal.java +++ /dev/null @@ -1,110 +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.im.authentication; - -import java.util.Collection; -import java.util.Collections; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.userdetails.User; - -/** - * A software provisioning user principal definition stored in the - * {@link SecurityContext} which contains the user specific attributes. - * - */ -@Getter -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class UserPrincipal extends User { - - private static final long serialVersionUID = 1L; - - private final String firstname; - private final String lastname; - private final String loginname; - private final String tenant; - private final String email; - - /** - * @param username - * the user name of the user - * @param firstname - * the first name of the user - * @param lastname - * the last name of the user - * @param loginname - * the login name of user - * @param tenant - * the tenant of the user - * @param email - * address of the user - */ - public UserPrincipal(final String username, final String firstname, final String lastname, final String loginname, - final String email, final String tenant) { - this(username, "***", firstname, lastname, loginname, email, tenant, Collections.emptyList()); - } - - /** - * @param username - * the user name of the user - * @param password - * the password of the user - * @param firstname - * the first name of the user - * @param lastname - * the last name of the user - * @param loginname - * the login name of user - * @param tenant - * the tenant of the user - * @param email - * address of the user - * @param authorities - * the authorities which the user has - */ - // too many parameters, builder pattern wouldn't work easy due the super - // constructor. - @SuppressWarnings("squid:S00107") - public UserPrincipal(final String username, final String password, final String firstname, final String lastname, - final String loginname, final String email, final String tenant, - final Collection authorities) { - super(username, password, authorities); - this.firstname = firstname; - this.lastname = lastname; - this.loginname = loginname; - this.tenant = tenant; - this.email = email; - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } -} diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/UserTenantAware.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/UserTenantAware.java new file mode 100644 index 000000000..eff26e14e --- /dev/null +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/im/authentication/UserTenantAware.java @@ -0,0 +1,78 @@ +/** + * 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.im.authentication; + +import java.io.Serial; +import java.util.Collection; +import java.util.Collections; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.userdetails.User; + +/** + * A software provisioning user principal definition stored in the + * {@link SecurityContext} which contains the user specific attributes. + */ +@Getter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class UserTenantAware extends User { + + @Serial + private static final long serialVersionUID = 1L; + + private final String tenant; + + /** + * @param username the username of the user + * @param password the password of the user + * @param authorities the authorities which the user has + * @param tenant the tenant of the user + */ + public UserTenantAware(final String username, final String password, + final Collection authorities, final String tenant) { + super(username, password, authorities == null ? Collections.emptyList() : authorities); + this.tenant = tenant; + } + + /** + * Create user without password and any credentials. For test purposes only. + * + * @param username the username of the user + * @param tenant the tenant of the user + */ + public UserTenantAware(final String username, String tenant) { + this(username, "***", null, tenant); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} \ No newline at end of file diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/InMemoryUserAuthoritiesResolver.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/InMemoryUserAuthoritiesResolver.java index 81ec809f8..2e6e5fc9f 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/InMemoryUserAuthoritiesResolver.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/InMemoryUserAuthoritiesResolver.java @@ -27,8 +27,7 @@ public class InMemoryUserAuthoritiesResolver implements UserAuthoritiesResolver /** * Constructs the resolver based on the given authority lookup map. * - * @param usernamesToAuthorities - * The authority map to read from. Must not be null. + * @param usernamesToAuthorities The authority map to read from. Must not be null. */ public InMemoryUserAuthoritiesResolver(final Map> usernamesToAuthorities) { this.usernamesToAuthorities = usernamesToAuthorities; diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextTenantAware.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextTenantAware.java index 6eb407570..8ce9f9e9a 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextTenantAware.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/security/SecurityContextTenantAware.java @@ -21,7 +21,7 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.ContextAware; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails; -import org.eclipse.hawkbit.im.authentication.UserPrincipal; +import org.eclipse.hawkbit.im.authentication.UserTenantAware; import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.springframework.lang.Nullable; @@ -30,6 +30,7 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; import org.springframework.security.oauth2.core.oidc.user.OidcUser; /** @@ -84,8 +85,8 @@ public class SecurityContextTenantAware implements ContextAware { final Object principal = context.getAuthentication().getPrincipal(); if (context.getAuthentication().getDetails() instanceof TenantAwareAuthenticationDetails) { return ((TenantAwareAuthenticationDetails) context.getAuthentication().getDetails()).getTenant(); - } else if (principal instanceof UserPrincipal) { - return ((UserPrincipal) principal).getTenant(); + } else if (principal instanceof UserTenantAware) { + return ((UserTenantAware) principal).getTenant(); } } return null; @@ -96,12 +97,12 @@ public class SecurityContextTenantAware implements ContextAware { final SecurityContext context = SecurityContextHolder.getContext(); if (context.getAuthentication() != null) { final Object principal = context.getAuthentication().getPrincipal(); - if (principal instanceof UserPrincipal) { - return ((UserPrincipal) principal).getUsername(); - } if (principal instanceof OidcUser) { return ((OidcUser) principal).getPreferredUsername(); } + if (principal instanceof User) { + return ((User) principal).getUsername(); + } } return null; } @@ -184,19 +185,20 @@ public class SecurityContextTenantAware implements ContextAware { * a specific tenant and user. */ private static final class AuthenticationDelegate implements Authentication { + @Serial private static final long serialVersionUID = 1L; private final Authentication delegate; - private final UserPrincipal principal; + private final UserTenantAware principal; private final TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails; private AuthenticationDelegate(final Authentication delegate, final String tenant, final String username, final Collection authorities) { this.delegate = delegate; - this.principal = new UserPrincipal(username, username, null, null, username, null, tenant, authorities); + this.principal = new UserTenantAware(username, username, authorities, tenant); tenantAwareAuthenticationDetails = new TenantAwareAuthenticationDetails(tenant, false); }