Skip to content

Commit 2929242

Browse files
committed
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 566bc6a commit 2929242

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)