Improve hawkBit user management (#1666)
1. Definded with properties users (static) are configured using property map (no need of indexes) 2. AuthenticationProvider that authenticates them is always registered (if not needed - don't configure them) 3. UserDetailsService (in case of missing - won't be registered) 4. Spring security user (spring.security.username) will be registered together with other users (if any). If any - it will be system-wide, otherwise tenant-scoped. 5. UserPrincipal renamed to TenantAwareUser in order to match its purpose. 6. Some if its fields are removes as not needed - to be closer to spring security user 7. DefaultRolloutApprovalStrategy now use UserAuthoritiesResolver instead of UserDetailsService as the central point of truth Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -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<? extends GrantedAuthority> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<? extends GrantedAuthority> 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;
|
||||
}
|
||||
}
|
||||
@@ -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 <code>null</code>.
|
||||
* @param usernamesToAuthorities The authority map to read from. Must not be <code>null</code>.
|
||||
*/
|
||||
public InMemoryUserAuthoritiesResolver(final Map<String, List<String>> usernamesToAuthorities) {
|
||||
this.usernamesToAuthorities = usernamesToAuthorities;
|
||||
|
||||
@@ -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<? extends GrantedAuthority> 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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user