View Javadoc
1   package cn.home1.cloud.config.server.security;
2   
3   import static cn.home1.cloud.config.server.util.Consts.DOT_ENV;
4   import static cn.home1.cloud.config.server.util.Consts.PRIVILEGE_ENV_PROFILE_;
5   import static cn.home1.cloud.config.server.util.Consts.PRIVILEGE_ENV_PROFILE_WILDCARD;
6   import static cn.home1.cloud.config.server.util.EnvironmentUtils.getConfigPassword;
7   import static java.util.stream.Collectors.toList;
8   
9   import com.google.common.collect.ImmutableList;
10  
11  import lombok.Setter;
12  import lombok.extern.slf4j.Slf4j;
13  
14  import org.springframework.cloud.config.environment.Environment;
15  import org.springframework.cloud.config.server.environment.EnvironmentController;
16  import org.springframework.security.authentication.BadCredentialsException;
17  import org.springframework.security.core.GrantedAuthority;
18  import org.springframework.security.core.authority.SimpleGrantedAuthority;
19  import org.springframework.security.core.userdetails.User;
20  import org.springframework.security.core.userdetails.UserDetails;
21  import org.springframework.security.core.userdetails.UserDetailsService;
22  import org.springframework.security.core.userdetails.UsernameNotFoundException;
23  
24  import java.util.Collection;
25  import java.util.Iterator;
26  import java.util.Optional;
27  import java.util.regex.Pattern;
28  
29  /**
30   * config-server-client use the password configured in GIT. the user name must be the app name.
31   */
32  @Slf4j
33  public class GitFileConfigUserDetailsService implements UserDetailsService {
34  
35      private static final Pattern PATTERN_AT = Pattern.compile("@");
36  
37      private static final Collection<? extends GrantedAuthority> ANONYMOUS_AUTHORITY = ImmutableList.of();
38  
39      private static final Collection<? extends GrantedAuthority> HOOK_AUTHORITY =
40          ImmutableList.of(new SimpleGrantedAuthority("ROLE_" + Role.HOOK.toString()));
41  
42      private static final GrantedAuthority USER_AUTHORITY = new SimpleGrantedAuthority( //
43          "ROLE_" + Role.USER.toString());
44  
45      @Setter
46      private ConfigSecurity configSecurity;
47  
48      @Setter
49      private String defaultLabel;
50  
51      @Setter
52      private EnvironmentController environmentController;
53  
54      @Setter
55      private PrivilegedUserProperties privilegedUserProperties;
56  
57      @Override
58      public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
59          if (username == null) {
60              return new User("anonymous", "", ANONYMOUS_AUTHORITY);
61          }
62  
63          if (username.equals(this.privilegedUserProperties.getAdminName())) {
64              final Collection<? extends GrantedAuthority> adminAuthority = this.privilegedUserProperties.getAdminRoles() //
65                  .stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(toList());
66              return new User(username, this.privilegedUserProperties.getAdminPassword(), adminAuthority);
67          }
68  
69          if (username.equals(this.privilegedUserProperties.getHookName())) {
70              return new User(username, this.privilegedUserProperties.getHookPassword(), HOOK_AUTHORITY);
71          }
72  
73          // username is `spring.application.name` or `spring.application.name@{profile.env}`
74          final String application;
75          final String profileToAuth;
76          final Optional<GrantedAuthority> privilege; // privilege of environment profile: can be specific one, none or anyone
77          if (username.contains("@") && username.endsWith(DOT_ENV)) {
78              final Iterator<String> parts = PATTERN_AT.splitAsStream(username).iterator();
79              application = parts.next();
80              profileToAuth = parts.next();
81              privilege = Optional.of(new SimpleGrantedAuthority(PRIVILEGE_ENV_PROFILE_ + profileToAuth));
82          } else if (username.endsWith("@default")) {
83              final Iterator<String> parts = PATTERN_AT.splitAsStream(username).iterator();
84              application = parts.next();
85              profileToAuth = parts.next();
86              privilege = Optional.empty();
87          } else {
88              application = username;
89              profileToAuth = "default";
90              privilege = Optional.of(new SimpleGrantedAuthority(PRIVILEGE_ENV_PROFILE_WILDCARD));
91          }
92          final Collection<GrantedAuthority> authorities = privilege.isPresent() ? //
93              ImmutableList.of(USER_AUTHORITY, privilege.get()) : ImmutableList.of(USER_AUTHORITY);
94  
95  
96          final String expectedPassword = this.findExpectedPassword(application, profileToAuth);
97          if (expectedPassword == null) {
98              throw new BadCredentialsException(
99                  "can not find 'spring.cloud.config.password' application: " + application + ", profile: " + profileToAuth);
100         }
101         final String rawExpectedPassword = this.configSecurity.decryptProperty(expectedPassword);
102 
103         return new User(application, rawExpectedPassword, authorities);
104     }
105 
106     String findExpectedPassword(final String application, final String profile) {
107         final String result;
108 
109         if (this.defaultLabel != null) {
110             final String found = this.findExpectedPassword(application, profile, this.defaultLabel);
111             result = found != null ? found : this.findExpectedPassword(application, profile, null);
112         } else {
113             result = this.findExpectedPassword(application, profile, null);
114         }
115 
116         return result;
117     }
118 
119     String findExpectedPassword(final String application, final String profile, final String label) {
120         final Environment environment;
121 
122         try {
123             environment = this.environmentController.labelled(application, profile, label);
124         } catch (final Exception exception) {
125             log.warn("error on loadUserByUsername from git configuration file, username: {}", //
126                 application, exception);
127             throw exception;
128         }
129 
130         if (environment == null) {
131             throw new UsernameNotFoundException("can not find the project by username: " + application);
132         }
133 
134         return getConfigPassword(environment);
135     }
136 }