Add multi-user support (#829)

This allows optionally configuring multiple static users with varying
permissions. If used, Spring Security user/password are ignored.
Otherwise, the old behavior is retained.

Signed-off-by: Stefan Schake <stefan.schake@devolo.de>
This commit is contained in:
Stefan Schake
2019-05-21 11:09:58 +02:00
committed by Dominic Schabel
parent d34e7f35c5
commit 7c04ca1967
5 changed files with 179 additions and 30 deletions

View File

@@ -9,6 +9,8 @@
package org.eclipse.hawkbit.autoconfigure.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import org.eclipse.hawkbit.im.authentication.MultitenancyIndicator;
@@ -17,6 +19,7 @@ import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
import org.eclipse.hawkbit.im.authentication.UserPrincipal;
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.security.authentication.UsernamePasswordAuthenticationToken;
@@ -24,11 +27,11 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.core.GrantedAuthority;
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.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* Auto-configuration for the in-memory-user-management.
@@ -36,12 +39,17 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
*/
@Configuration
@ConditionalOnMissingBean(UserDetailsService.class)
@EnableConfigurationProperties({ MultiUserProperties.class })
public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticationConfigurerAdapter {
private final SecurityProperties securityProperties;
InMemoryUserManagementAutoConfiguration(final SecurityProperties securityProperties) {
private final MultiUserProperties multiUserProperties;
InMemoryUserManagementAutoConfiguration(final SecurityProperties securityProperties,
final MultiUserProperties multiUserProperties) {
this.securityProperties = securityProperties;
this.multiUserProperties = multiUserProperties;
}
@Override
@@ -57,17 +65,62 @@ public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticatio
@Bean
@ConditionalOnMissingBean
UserDetailsService userDetailsService() {
final InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserPrincipalDetailsManager();
inMemoryUserDetailsManager.setAuthenticationManager(null);
final SecurityProperties.User user = securityProperties.getUser();
final UserBuilder userBuilder = User.builder().username(user.getName()).password(user.getPassword())
.authorities(PermissionUtils.createAllAuthorityList());
final List<String> roles = user.getRoles();
if (!roles.isEmpty()) {
userBuilder.roles(roles.toArray(new String[roles.size()]));
final String defaultTenant = "DEFAULT";
final List<UserPrincipal> userPrincipals = new ArrayList<>();
for (MultiUserProperties.User user : multiUserProperties.getUsers()) {
List<GrantedAuthority> authorityList;
// Allows ALL as a shorthand for all permissions
if (user.getPermissions().size() == 1 && user.getPermissions().get(0).equals("ALL")) {
authorityList = PermissionUtils.createAllAuthorityList();
} else {
authorityList = new ArrayList<>(user.getPermissions().size());
for (final String permission : user.getPermissions()) {
authorityList.add(new SimpleGrantedAuthority(permission));
authorityList.add(new SimpleGrantedAuthority("ROLE_" + permission));
}
}
final UserPrincipal userPrincipal = new UserPrincipal(user.getUsername(), user.getPassword(),
user.getFirstname(), user.getLastname(), user.getUsername(), user.getEmail(), defaultTenant,
authorityList);
userPrincipals.add(userPrincipal);
}
inMemoryUserDetailsManager.createUser(userBuilder.build());
return inMemoryUserDetailsManager;
// If no users are configured through the multi user properties, set up
// the default user from security properties
if (userPrincipals.isEmpty()) {
final String name = securityProperties.getUser().getName();
final String password = securityProperties.getUser().getPassword();
userPrincipals.add(new UserPrincipal(name, password, name, name, name, null, defaultTenant,
PermissionUtils.createAllAuthorityList()));
}
return new FixedInMemoryUserPrincipalUserDetailsService(userPrincipals);
}
private static class FixedInMemoryUserPrincipalUserDetailsService implements UserDetailsService {
private final HashMap<String, UserPrincipal> userPrincipalMap = new HashMap<>();
public FixedInMemoryUserPrincipalUserDetailsService(Collection<UserPrincipal> userPrincipals) {
for (UserPrincipal user : userPrincipals) {
userPrincipalMap.put(user.getUsername(), user);
}
}
private static UserPrincipal clone(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(String username) throws UsernameNotFoundException {
UserPrincipal userPrincipal = userPrincipalMap.get(username);
if (userPrincipal == null)
throw new UsernameNotFoundException("No such user");
// Spring mutates the data, so we must return a copy here
return clone(userPrincipal);
}
}
/**
@@ -90,19 +143,4 @@ public class InMemoryUserManagementAutoConfiguration extends GlobalAuthenticatio
return result;
}
}
private static final class InMemoryUserPrincipalDetailsManager extends InMemoryUserDetailsManager {
private InMemoryUserPrincipalDetailsManager() {
super(new ArrayList<>());
}
@Override
public UserDetails loadUserByUsername(final String username) {
final UserDetails loadUserByUsername = super.loadUserByUsername(username);
return new UserPrincipal(loadUserByUsername.getUsername(), loadUserByUsername.getPassword(),
loadUserByUsername.getUsername(), loadUserByUsername.getUsername(),
loadUserByUsername.getUsername(), null, "DEFAULT", loadUserByUsername.getAuthorities());
}
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2019 devolo AG 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.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<User> users = new ArrayList<>();
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> 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<String> 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<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
}
}