org.cloudfoundry.identity.uaa.user.JdbcUaaUserDatabase Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2016] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.user;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.util.TimeService;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static org.springframework.util.StringUtils.hasText;
/**
* @author Luke Taylor
* @author Dave Syer
* @author Vidya Valmikinathan
*/
public class JdbcUaaUserDatabase implements UaaUserDatabase {
private static Log logger = LogFactory.getLog(JdbcUaaUserDatabase.class);
public static final String USER_FIELDS = "id,username,password,email,givenName,familyName,created,lastModified,authorities,origin,external_id,verified,identity_zone_id,salt,passwd_lastmodified,phoneNumber,legacy_verification_behavior,passwd_change_required,last_logon_success_time,previous_logon_success_time ";
public static final String PRE_DEFAULT_USER_BY_USERNAME_QUERY = "select " + USER_FIELDS + "from users where %s = ? and active=? and origin=? and identity_zone_id=?";
public static final String DEFAULT_CASE_SENSITIVE_USER_BY_USERNAME_QUERY = String.format(PRE_DEFAULT_USER_BY_USERNAME_QUERY, "lower(username)");
public static final String DEFAULT_CASE_INSENSITIVE_USER_BY_USERNAME_QUERY = String.format(PRE_DEFAULT_USER_BY_USERNAME_QUERY, "username");
public static final String PRE_DEFAULT_USER_BY_EMAIL_AND_ORIGIN_QUERY = "select " + USER_FIELDS + "from users where %s=? and active=? and origin=? and identity_zone_id=?";
public static final String DEFAULT_CASE_SENSITIVE_USER_BY_EMAIL_AND_ORIGIN_QUERY = String.format(PRE_DEFAULT_USER_BY_EMAIL_AND_ORIGIN_QUERY, "lower(email)");
public static final String DEFAULT_CASE_INSENSITIVE_USER_BY_EMAIL_AND_ORIGIN_QUERY = String.format(PRE_DEFAULT_USER_BY_EMAIL_AND_ORIGIN_QUERY, "email");
public static final String DEFAULT_UPDATE_USER_LAST_LOGON = "update users set previous_logon_success_time = last_logon_success_time, last_logon_success_time = ? where id = ? and identity_zone_id=?";
public static final String DEFAULT_USER_BY_ID_QUERY = "select " + USER_FIELDS + "from users where id = ? and active=? and identity_zone_id=?";
private final TimeService timeService;
private JdbcTemplate jdbcTemplate;
private final RowMapper mapper = new UaaUserRowMapper();
private final RowMapper userInfoMapper = new UserInfoRowMapper();
private boolean caseInsensitive = false;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public boolean isCaseInsensitive() {
return caseInsensitive;
}
public void setCaseInsensitive(boolean caseInsensitive) {
this.caseInsensitive = caseInsensitive;
}
public RowMapper getMapper() {
return mapper;
}
public JdbcUaaUserDatabase(JdbcTemplate jdbcTemplate, TimeService timeService) {
Assert.notNull(jdbcTemplate);
this.jdbcTemplate = jdbcTemplate;
this.timeService = timeService;
}
@Override
public UaaUser retrieveUserByName(String username, String origin) throws UsernameNotFoundException {
try {
String sql = isCaseInsensitive() ? DEFAULT_CASE_INSENSITIVE_USER_BY_USERNAME_QUERY : DEFAULT_CASE_SENSITIVE_USER_BY_USERNAME_QUERY;
return jdbcTemplate.queryForObject(sql, mapper, username.toLowerCase(Locale.US), true, origin, IdentityZoneHolder.get().getId());
} catch (EmptyResultDataAccessException e) {
throw new UsernameNotFoundException(username);
}
}
@Override
public UaaUser retrieveUserById(String id) throws UsernameNotFoundException {
try {
return jdbcTemplate.queryForObject(DEFAULT_USER_BY_ID_QUERY, mapper, id, true, IdentityZoneHolder.get().getId());
} catch (EmptyResultDataAccessException e) {
throw new UsernameNotFoundException(id);
}
}
@Override
public UaaUser retrieveUserByEmail(String email, String origin) throws UsernameNotFoundException {
String sql = isCaseInsensitive() ? DEFAULT_CASE_INSENSITIVE_USER_BY_EMAIL_AND_ORIGIN_QUERY : DEFAULT_CASE_SENSITIVE_USER_BY_EMAIL_AND_ORIGIN_QUERY;
List results = jdbcTemplate.query(sql, mapper, email.toLowerCase(Locale.US), true, origin, IdentityZoneHolder.get().getId());
if(results.size() == 0) {
return null;
}
else if(results.size() == 1) {
return results.get(0);
}
else {
throw new IncorrectResultSizeDataAccessException(String.format("Multiple users match email=%s origin=%s", email, origin), 1, results.size());
}
}
@Override
public UserInfo getUserInfo(String id) {
try {
return jdbcTemplate.queryForObject("select user_id, info from user_info where user_id = ?", userInfoMapper, id);
} catch (EmptyResultDataAccessException e) {
logger.debug("No custom attributes stored for user:"+id);
return null;
}
}
@Override
public UserInfo storeUserInfo(String id, UserInfo info) {
if (StringUtils.isEmpty(id)) {
throw new NullPointerException("id is a required field");
}
final String insertUserInfoSQL = "insert into user_info(user_id, info) values (?,?)";
final String updateUserInfoSQL = "update user_info set info = ? where user_id = ?";
if (info == null) {
info = new UserInfo();
}
String json = JsonUtils.writeValueAsString(info);
int count = jdbcTemplate.update(updateUserInfoSQL, json, id);
if (count == 0) {
jdbcTemplate.update(insertUserInfoSQL, id, json);
}
return getUserInfo(id);
}
@Override
public void updateLastLogonTime(String userId) {
int update = jdbcTemplate.update(DEFAULT_UPDATE_USER_LAST_LOGON, timeService.getCurrentTimeMillis(), userId, IdentityZoneHolder.get().getId());
}
private final class UserInfoRowMapper implements RowMapper {
@Override
public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
String id = rs.getString(1);
String info = rs.getString(2);
UserInfo userInfo = hasText(info) ? JsonUtils.readValue(info, UserInfo.class) : new UserInfo();
return userInfo;
}
}
private final class UaaUserRowMapper implements RowMapper {
@Override
public UaaUser mapRow(ResultSet rs, int rowNum) throws SQLException {
String id = rs.getString("id");
UaaUserPrototype prototype = new UaaUserPrototype().withId(id)
.withUsername(rs.getString("username"))
.withPassword(rs.getString("password"))
.withEmail(rs.getString("email"))
.withGivenName(rs.getString("givenName"))
.withFamilyName(rs.getString("familyName"))
.withCreated(rs.getTimestamp("created"))
.withModified(rs.getTimestamp("lastModified"))
.withOrigin(rs.getString("origin"))
.withExternalId(rs.getString("external_id"))
.withVerified(rs.getBoolean("verified"))
.withZoneId(rs.getString("identity_zone_id"))
.withSalt(rs.getString("salt"))
.withPasswordLastModified(rs.getTimestamp("passwd_lastmodified"))
.withPhoneNumber(rs.getString("phoneNumber"))
.withLegacyVerificationBehavior(rs.getBoolean("legacy_verification_behavior"))
.withPasswordChangeRequired(rs.getBoolean("passwd_change_required"))
;
Long lastLogon = rs.getLong("last_logon_success_time");
if (rs.wasNull()) {
lastLogon = null;
}
Long previousLogon = rs.getLong("previous_logon_success_time");
if (rs.wasNull()) {
previousLogon = null;
}
prototype.withLastLogonSuccess(lastLogon)
.withPreviousLogonSuccess(previousLogon);
List authorities =
AuthorityUtils.commaSeparatedStringToAuthorityList(getAuthorities(id));
return new UaaUser(prototype.withAuthorities(authorities));
}
private String getAuthorities(final String userId) {
Set authorities = new HashSet<>();
getAuthorities(authorities, Arrays.asList(userId));
authorities.addAll(IdentityZoneHolder.get().getConfig().getUserConfig().getDefaultGroups());
return StringUtils.collectionToCommaDelimitedString(new HashSet<>(authorities));
}
protected void getAuthorities(Set authorities, final List memberIdList) {
List