/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsServiceConfigurer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.provisioning.UserDetailsManager;
import org.springframework.util.Assert;
/**
* Base class for populating an
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder} with a
* {@link UserDetailsManager}.
*
* @param <B> the type of the {@link SecurityBuilder} that is being configured
* @param <C> the type of {@link UserDetailsManagerConfigurer}
*
* @author Rob Winch
* @since 3.2
*/
public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsManagerConfigurer<B,C>> extends
UserDetailsServiceConfigurer<B,C,UserDetailsManager> {
private final List<UserDetailsBuilder> userBuilders = new ArrayList<UserDetailsBuilder>();
protected UserDetailsManagerConfigurer(UserDetailsManager userDetailsManager) {
super(userDetailsManager);
}
/**
* Populates the users that have been added.
*
* @throws Exception
*/
@Override
protected void initUserDetailsService() throws Exception {
for(UserDetailsBuilder userBuilder : userBuilders) {
getUserDetailsService().createUser(userBuilder.build());
}
}
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This method can be invoked
* multiple times to add multiple users.
*
* @param username the username for the user being added. Cannot be null.
* @return
*/
@SuppressWarnings("unchecked")
public final UserDetailsBuilder withUser(String username) {
UserDetailsBuilder userBuilder = new UserDetailsBuilder((C)this);
userBuilder.username(username);
this.userBuilders.add(userBuilder);
return userBuilder;
}
/**
* Builds the user to be added. At minimum the username, password, and authorities should provided. The remaining
* attributes have reasonable defaults.
*
* @param <T> the type of {@link UserDetailsManagerConfigurer} to return for chaining methods.
*/
public class UserDetailsBuilder {
private String username;
private String password;
private List<GrantedAuthority> authorities;
private boolean accountExpired;
private boolean accountLocked;
private boolean credentialsExpired;
private boolean disabled;
private final C builder;
/**
* Creates a new instance
* @param builder the builder to return
*/
private UserDetailsBuilder(C builder) {
this.builder = builder;
}
/**
* Returns the {@link UserDetailsManagerRegistry} for method chaining (i.e. to add another user)
*
* @return the {@link UserDetailsManagerRegistry} for method chaining (i.e. to add another user)
*/
public C and() {
return builder;
}
/**
* Populates the username. This attribute is required.
*
* @param username the username. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
private UserDetailsBuilder username(String username) {
Assert.notNull(username, "username cannot be null");
this.username = username;
return this;
}
/**
* Populates the password. This attribute is required.
*
* @param password the password. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder password(String password) {
Assert.notNull(password, "password cannot be null");
this.password = password;
return this;
}
/**
* Populates the roles. This method is a shortcut for calling {@link #authorities(String...)}, but automatically
* prefixes each entry with "ROLE_". This means the following:
*
* <code>
* builder.roles("USER","ADMIN");
* </code>
*
* is equivalent to
*
* <code>
* builder.authorities("ROLE_USER","ROLE_ADMIN");
* </code>
*
* <p>This attribute is required, but can also be populated with {@link #authorities(String...)}.</p>
*
* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null, contain null values or start
* with "ROLE_"
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder roles(String... roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(roles.length);
for(String role : roles) {
Assert.isTrue(!role.startsWith("ROLE_"), role + " cannot start with ROLE_ (it is automatically added)");
authorities.add(new SimpleGrantedAuthority("ROLE_"+role));
}
return authorities(authorities);
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain null
* values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(GrantedAuthority...authorities) {
return authorities(Arrays.asList(authorities));
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user. Cannot be null, or contain null
* values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(List<? extends GrantedAuthority> authorities) {
this.authorities = new ArrayList<GrantedAuthority>(authorities);
return this;
}
/**
* Populates the authorities. This attribute is required.
*
* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN, etc). Cannot be null, or contain null
* values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(String... authorities) {
return authorities(AuthorityUtils.createAuthorityList(authorities));
}
/**
* Defines if the account is expired or not. Default is false.
*
* @param accountExpired true if the account is expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder accountExpired(boolean accountExpired) {
this.accountExpired = accountExpired;
return this;
}
/**
* Defines if the account is locked or not. Default is false.
*
* @param accountLocked true if the account is locked, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder accountLocked(boolean accountLocked) {
this.accountLocked = accountLocked;
return this;
}
/**
* Defines if the credentials are expired or not. Default is false.
*
* @param credentialsExpired true if the credentials are expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder credentialsExpired(boolean credentialsExpired) {
this.credentialsExpired = credentialsExpired;
return this;
}
/**
* Defines if the account is disabled or not. Default is false.
*
* @param disabled true if the account is disabled, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate additional attributes for this
* user)
*/
public UserDetailsBuilder disabled(boolean disabled) {
this.disabled = disabled;
return this;
}
private UserDetails build() {
return new User(username, password, !disabled, !accountExpired,
!credentialsExpired, !accountLocked, authorities);
}
}
}