Skip to content

Commit 4123d96

Browse files
pvlissrwinch
authored andcommitted
JdbcUserDetailsManager handles extra UserDetails attributes
Check ResutSetMetaData to see if extra columns are present in order to also handle the UserDetails attributes: accountNonExpired, accountNonLocked and credentialsNonExpired. Fixes gh-4399
1 parent 12f3208 commit 4123d96

File tree

3 files changed

+101
-5
lines changed

3 files changed

+101
-5
lines changed

core/src/main/java/org/springframework/security/core/userdetails/jdbc/JdbcDaoImpl.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ protected UserDetails createUserDetails(String username,
295295
}
296296

297297
return new User(returnUsername, userFromUserQuery.getPassword(),
298-
userFromUserQuery.isEnabled(), true, true, true, combinedAuthorities);
298+
userFromUserQuery.isEnabled(), userFromUserQuery.isAccountNonExpired(),
299+
userFromUserQuery.isCredentialsNonExpired(), userFromUserQuery.isAccountNonLocked(), combinedAuthorities);
299300
}
300301

301302
/**

core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java

+54-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import org.springframework.security.core.authority.AuthorityUtils;
2525
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2626
import org.springframework.security.core.context.SecurityContextHolder;
27+
import org.springframework.security.core.userdetails.User;
2728
import org.springframework.security.core.userdetails.UserCache;
2829
import org.springframework.security.core.userdetails.UserDetails;
2930
import org.springframework.security.core.userdetails.cache.NullUserCache;
@@ -145,15 +146,51 @@ protected void initDao() throws ApplicationContextException {
145146
// ~ UserDetailsManager implementation
146147
// ==============================================================================
147148

149+
/**
150+
* Executes the SQL <tt>usersByUsernameQuery</tt> and returns a list of UserDetails
151+
* objects. There should normally only be one matching user.
152+
*/
153+
protected List<UserDetails> loadUsersByUsername(String username) {
154+
return getJdbcTemplate().query(getUsersByUsernameQuery(), new String[]{username},
155+
(rs, rowNum) -> {
156+
157+
String userName = rs.getString(1);
158+
String password = rs.getString(2);
159+
boolean enabled = rs.getBoolean(3);
160+
161+
boolean accLocked = false;
162+
boolean accExpired = false;
163+
boolean credsExpired = false;
164+
165+
if (rs.getMetaData().getColumnCount() > 3) {
166+
//NOTE: acc_locked, acc_expired and creds_expired are also to be loaded
167+
accLocked = rs.getBoolean(4);
168+
accExpired = rs.getBoolean(5);
169+
credsExpired = rs.getBoolean(6);
170+
}
171+
return new User(userName, password, enabled, !accExpired, !credsExpired, !accLocked,
172+
AuthorityUtils.NO_AUTHORITIES);
173+
});
174+
}
175+
148176
public void createUser(final UserDetails user) {
149177
validateUserDetails(user);
178+
150179
getJdbcTemplate().update(createUserSql, new PreparedStatementSetter() {
180+
@Override
151181
public void setValues(PreparedStatement ps) throws SQLException {
152182
ps.setString(1, user.getUsername());
153183
ps.setString(2, user.getPassword());
154184
ps.setBoolean(3, user.isEnabled());
155-
}
156185

186+
int paramCount = ps.getParameterMetaData().getParameterCount();
187+
if (paramCount > 3) {
188+
//NOTE: acc_locked, acc_expired and creds_expired are also to be inserted
189+
ps.setBoolean(4, !user.isAccountNonLocked());
190+
ps.setBoolean(5, !user.isAccountNonExpired());
191+
ps.setBoolean(6, !user.isCredentialsNonExpired());
192+
}
193+
}
157194
});
158195

159196
if (getEnableAuthorities()) {
@@ -163,11 +200,25 @@ public void setValues(PreparedStatement ps) throws SQLException {
163200

164201
public void updateUser(final UserDetails user) {
165202
validateUserDetails(user);
203+
166204
getJdbcTemplate().update(updateUserSql, new PreparedStatementSetter() {
205+
@Override
167206
public void setValues(PreparedStatement ps) throws SQLException {
168207
ps.setString(1, user.getPassword());
169208
ps.setBoolean(2, user.isEnabled());
170-
ps.setString(3, user.getUsername());
209+
210+
int paramCount = ps.getParameterMetaData().getParameterCount();
211+
if (paramCount == 3) {
212+
ps.setString(3, user.getUsername());
213+
} else {
214+
//NOTE: acc_locked, acc_expired and creds_expired are also updated
215+
ps.setBoolean(3, !user.isAccountNonLocked());
216+
ps.setBoolean(4, !user.isAccountNonExpired());
217+
ps.setBoolean(5, !user.isCredentialsNonExpired());
218+
219+
ps.setString(6, user.getUsername());
220+
}
221+
171222
}
172223
});
173224

core/src/test/java/org/springframework/security/provisioning/JdbcUserDetailsManagerTests.java

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -106,6 +106,19 @@ public void dropTablesAndClearContext() {
106106
SecurityContextHolder.clearContext();
107107
}
108108

109+
private void setUpAccLockingColumns() {
110+
template.execute("alter table users add column acc_locked boolean default false not null");
111+
template.execute("alter table users add column acc_expired boolean default false not null");
112+
template.execute("alter table users add column creds_expired boolean default false not null");
113+
114+
manager.setUsersByUsernameQuery(
115+
"select username,password,enabled, acc_locked, acc_expired, creds_expired from users where username = ?");
116+
manager.setCreateUserSql(
117+
"insert into users (username, password, enabled, acc_locked, acc_expired, creds_expired) values (?,?,?,?,?,?)");
118+
manager.setUpdateUserSql(
119+
"update users set password = ?, enabled = ?, acc_locked=?, acc_expired=?, creds_expired=? where username = ?");
120+
}
121+
109122
@Test
110123
public void createUserInsertsCorrectData() {
111124
manager.createUser(joe);
@@ -115,6 +128,19 @@ public void createUserInsertsCorrectData() {
115128
assertThat(joe2).isEqualTo(joe);
116129
}
117130

131+
@Test
132+
public void createUserInsertsCorrectDataWithLocking() {
133+
setUpAccLockingColumns();
134+
135+
UserDetails user = new User("joe", "pass", true, false, true, false,
136+
AuthorityUtils.createAuthorityList("A", "B"));
137+
manager.createUser(user);
138+
139+
UserDetails user2 = manager.loadUserByUsername(user.getUsername());
140+
141+
assertThat(user2).isEqualToComparingFieldByField(user);
142+
}
143+
118144
@Test
119145
public void deleteUserRemovesUserDataAndAuthoritiesAndClearsCache() {
120146
insertJoe();
@@ -139,6 +165,24 @@ public void updateUserChangesDataCorrectlyAndClearsCache() {
139165
assertThat(cache.getUserMap().containsKey("joe")).isFalse();
140166
}
141167

168+
@Test
169+
public void updateUserChangesDataCorrectlyAndClearsCacheWithLocking() {
170+
setUpAccLockingColumns();
171+
172+
insertJoe();
173+
174+
User newJoe = new User("joe", "newpassword", false, false, false, true,
175+
AuthorityUtils.createAuthorityList("D", "F", "E"));
176+
177+
manager.updateUser(newJoe);
178+
179+
UserDetails joe = manager.loadUserByUsername(newJoe.getUsername());
180+
181+
assertThat(joe).isEqualToComparingFieldByField(newJoe);
182+
assertThat(cache.getUserMap().containsKey(newJoe.getUsername())).isFalse();
183+
}
184+
185+
142186
@Test
143187
public void userExistsReturnsFalseForNonExistentUsername() {
144188
assertThat(manager.userExists("joe")).isFalse();

0 commit comments

Comments
 (0)