All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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> results;
            if(memberIdList.size() == 0) {
                return;
            }
            StringBuffer dynamicAuthoritiesQuery = new StringBuffer("select g.id,g.displayName from groups g, group_membership m where g.id = m.group_id  and g.identity_zone_id=? and m.member_id in (");
            for (int i = 0; i < memberIdList.size() - 1; i++) {
                dynamicAuthoritiesQuery.append("?,");
            }
            dynamicAuthoritiesQuery.append("?);");

            Object[] parameterList = ArrayUtils.addAll(new Object[]{IdentityZoneHolder.get().getId()},memberIdList.toArray());

            results = jdbcTemplate.queryForList(dynamicAuthoritiesQuery.toString(), parameterList);
            List newMemberIdList = new ArrayList<>();

            for(int i=0;i record = results.get(i);
                String displayName = (String) record.get("displayName");
                String groupId = (String) record.get("id");
                if (!authorities.contains(displayName)) {
                    authorities.add(displayName);
                    newMemberIdList.add(groupId);
                }
            }
            getAuthorities(authorities,newMemberIdList);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy