Skip to content

Gh 7750 implement jdbcuserpassworddetailsmanager #14148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -32,8 +32,11 @@ public abstract class Elements {

public static final String USER_SERVICE = "user-service";

@Deprecated(since = "For removal in 7.0.")
public static final String JDBC_USER_SERVICE = "jdbc-user-service";

public static final String JDBC_USER_PASSWORD_SERVICE = "jdbc-user-password-service";

public static final String FILTER_CHAIN_MAP = "filter-chain-map";

public static final String INTERCEPT_METHODS = "intercept-methods";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2022 the original author or authors.
* Copyright 2009-2023 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.
Expand Down Expand Up @@ -32,6 +32,7 @@
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser;
import org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser;
import org.springframework.security.config.authentication.JdbcUserPasswordDetailsManagerBeanDefinitionParser;
import org.springframework.security.config.authentication.JdbcUserServiceBeanDefinitionParser;
import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
import org.springframework.security.config.http.FilterChainBeanDefinitionParser;
Expand Down Expand Up @@ -173,6 +174,7 @@ private void loadParsers() {
this.parsers.put(Elements.LDAP_USER_SERVICE, new LdapUserServiceBeanDefinitionParser());
this.parsers.put(Elements.USER_SERVICE, new UserServiceBeanDefinitionParser());
this.parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
this.parsers.put(Elements.JDBC_USER_PASSWORD_SERVICE, new JdbcUserPasswordDetailsManagerBeanDefinitionParser());
this.parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
this.parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
this.parsers.put(Elements.METHOD_SECURITY, new MethodSecurityBeanDefinitionParser());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,17 @@ public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemo
* {@link #getDefaultUserDetailsService()} method. Note that additional
* {@link UserDetailsService}'s may override this {@link UserDetailsService} as the
* default. See the <a href=
* "https://docs.spring.io/spring-security/reference/servlet/appendix/database-schema.html"
* "https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#user-schema"
* >User Schema</a> section of the reference for the default schema.
* </p>
* @return a {@link JdbcUserDetailsManagerConfigurer} to allow customization of the
* JDBC authentication
* @throws Exception if an error occurs when adding the JDBC authentication
* @deprecated JdbcUserDetailsManager has been superseded by
* JdbcUserPasswordDetailsManager, and does not support DSL configuration. Please
* declare a bean instead.
*/
@Deprecated(since = "For removal in 7.0.")
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return apply(new JdbcUserDetailsManagerConfigurer<>());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -288,7 +288,13 @@ public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemo
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}

/**
* @deprecated Use JdbcUserPasswordDetailsManager instead, as this keeps the
* password up to date. Please consult the migration documentation as database
* changes might be necessary.
*/
@Override
@Deprecated(since = "For removal in 7.0.")
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* Copyright 2002-2023 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
*
* https://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.List;

import javax.sql.DataSource;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.provisioning.JdbcUserPasswordDetailsManager;

/**
* Configures an
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}
* to have JDBC authentication, with user management that will automatically update the
* encoding of a password if necessary. It also allows easily adding users to the database
* used for authentication and setting up the schema.
*
* <p>
* The only required method is the {@link #dataSource(javax.sql.DataSource)} all other
* methods have reasonable defaults.
*
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
* @author Rob Winch
* @author Geir Hedemark
* @since 6.3
*/
public final class JdbcUserPasswordDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, JdbcUserPasswordDetailsManagerConfigurer<B>> {

private DataSource dataSource;

private List<Resource> initScripts = new ArrayList<>();

public JdbcUserPasswordDetailsManagerConfigurer(JdbcUserPasswordDetailsManager manager) {
super(manager);
}

public JdbcUserPasswordDetailsManagerConfigurer() {
this(new JdbcUserPasswordDetailsManager());
}

/**
* Populates the {@link DataSource} to be used. This is the only required attribute.
* @param dataSource the {@link DataSource} to be used. Cannot be null.
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> dataSource(DataSource dataSource) {
this.dataSource = dataSource;
getUserDetailsService().setDataSource(dataSource);
return this;
}

/**
* Sets the query to be used for finding a user by their username. For example:
*
* <code>
* select username,password,enabled from users where username = ?
* </code>
* @param query The query to use for selecting the username, password, and if the user
* is enabled by username. Must contain a single parameter for the username.
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> usersByUsernameQuery(String query) {
getUserDetailsService().setUsersByUsernameQuery(query);
return this;
}

/**
* Sets the query to be used for updating a password for a user. For example:
*
* <code>
* update users set password = ? where username = ?
* </code>
* @param query The query to use for setting the password for a user. Must contain a
* parameter for the password, and one for the username.
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> changePasswordQuery(String query) {
getUserDetailsService().setChangePasswordQuery(query);
return this;
}

/**
* Sets the query to be used for finding a user's authorities by their username. For
* example:
*
* <code>
* select username,authority from authorities where username = ?
* </code>
* @param query The query to use for selecting the username, authority by username.
* Must contain a single parameter for the username.
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query) {
getUserDetailsService().setAuthoritiesByUsernameQuery(query);
return this;
}

/**
* An SQL statement to query user's group authorities given a username. For example:
*
* <code>
* select
* g.id, g.group_name, ga.authority
* from
* groups g, group_members gm, group_authorities ga
* where
* gm.username = ? and g.id = ga.group_id and g.id = gm.group_id
* </code>
* @param query The query to use for selecting the authorities by group. Must contain
* a single parameter for the username.
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> groupAuthoritiesByUsernameQuery(String query) {
JdbcUserPasswordDetailsManager userDetailsService = getUserDetailsService();
userDetailsService.setEnableGroups(true);
userDetailsService.setGroupAuthoritiesByUsernameQuery(query);
return this;
}

/**
* A non-empty string prefix that will be added to role strings loaded from persistent
* storage (default is "").
* @param rolePrefix
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) {
getUserDetailsService().setRolePrefix(rolePrefix);
return this;
}

/**
* Defines the {@link UserCache} to use
* @param userCache the {@link UserCache} to use
* @return the {@link JdbcUserPasswordDetailsManagerConfigurer} for further
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> userCache(UserCache userCache) {
getUserDetailsService().setUserCache(userCache);
return this;
}

@Override
protected void initUserDetailsService() throws Exception {
if (!this.initScripts.isEmpty()) {
getDataSourceInit().afterPropertiesSet();
}
super.initUserDetailsService();
}

@Override
public JdbcUserPasswordDetailsManager getUserDetailsService() {
return (JdbcUserPasswordDetailsManager) super.getUserDetailsService();
}

/**
* Populates the default schema that allows users and authorities to be stored.
* @return The {@link JdbcUserPasswordDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserPasswordDetailsManagerConfigurer<B> withDefaultSchema() {
this.initScripts.add(new ClassPathResource("org/springframework/security/core/userdetails/jdbc/users.ddl"));
return this;
}

protected DatabasePopulator getDatabasePopulator() {
ResourceDatabasePopulator dbp = new ResourceDatabasePopulator();
dbp.setScripts(this.initScripts.toArray(new Resource[0]));
return dbp;
}

private DataSourceInitializer getDataSourceInit() {
DataSourceInitializer dsi = new DataSourceInitializer();
dsi.setDatabasePopulator(getDatabasePopulator());
dsi.setDataSource(this.dataSource);
return dsi;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,13 @@ public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemo
return super.inMemoryAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}

/**
* @deprecated Use jdbcAuthenticationWithPasswordManagement instead, as this keeps
* the password up to date. Please consult the migration documentation as database
* changes might be necessary.
*/
@Override
@Deprecated(since = "For removal in 7.0.")
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return super.jdbcAuthentication().passwordEncoder(this.defaultPasswordEncoder);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -50,6 +50,9 @@ public BeanDefinition parse(Element element, ParserContext pc) {
authProvider.getPropertyValues().addPropertyValue("passwordEncoder", passwordEncoder);
}
Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
if (userServiceElt == null) {
userServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_PASSWORD_SERVICE);
}
if (userServiceElt == null) {
userServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
}
Expand Down
Loading