/*
* Copyright 2010 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
*
* http://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 com.springsource.greenhouse.account;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.springsource.greenhouse.utils.EmailUtils;
/**
* AccountRepository implementation that stores Accounts in a relational database using the JDBC API.
* @author Keith Donald
*/
@Repository
public class JdbcAccountRepository implements AccountRepository {
private final JdbcTemplate jdbcTemplate;
private final PasswordEncoder passwordEncoder;
private final AccountMapper accountMapper;
@Autowired
public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder, AccountMapper accountMapper) {
this.jdbcTemplate = jdbcTemplate;
this.passwordEncoder = passwordEncoder;
this.accountMapper = accountMapper;
}
@Transactional
public Account createAccount(Person person) throws EmailAlreadyOnFileException {
try {
jdbcTemplate.update("insert into Member (firstName, lastName, email, password, gender, birthdate) values (?, ?, ?, ?, ?, ?)",
person.getFirstName(), person.getLastName(), person.getEmail(), passwordEncoder.encode(person.getPassword()), person.getGender().code(), person.getBirthdate().toString());
Long accountId = jdbcTemplate.queryForLong("call identity()");
return accountMapper.newAccount(accountId, person);
} catch (DuplicateKeyException e) {
throw new EmailAlreadyOnFileException(person.getEmail());
}
}
public Account authenticate(String signin, String password) throws SignInNotFoundException, InvalidPasswordException {
try {
return jdbcTemplate.queryForObject(passwordProtectedAccountQuery(signin), passwordProtectedAccountMapper, signin).accessAccount(password, passwordEncoder);
} catch (EmptyResultDataAccessException e) {
throw new SignInNotFoundException(signin);
}
}
public void changePassword(Long accountId, String password) {
jdbcTemplate.update("update Member set password = ? where id = ?", passwordEncoder.encode(password), accountId);
}
public Account findById(Long id) {
return jdbcTemplate.queryForObject(AccountMapper.SELECT_ACCOUNT + " where id = ?", accountMapper, id);
}
public List<ProfileReference> findProfileReferencesByIds(List<Long> accountIds) {
NamedParameterJdbcTemplate namedTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
Map<String, Object> params = new HashMap<String, Object>(2, 1);
params.put("accountIds", accountIds);
return namedTemplate.query(AccountMapper.SELECT_ACCOUNT_REFERENCE + " where id in ( :accountIds )", params,
accountMapper.getReferenceMapper());
}
public Account findBySignin(String signin) throws SignInNotFoundException {
try {
return jdbcTemplate.queryForObject(accountQuery(signin), accountMapper, signin);
} catch (EmptyResultDataAccessException e) {
throw new SignInNotFoundException(signin);
}
}
// internal helpers
private String accountQuery(String signin) {
return EmailUtils.isEmail(signin) ? AccountMapper.SELECT_ACCOUNT + " where email = ?" : AccountMapper.SELECT_ACCOUNT + " where username = ?";
}
private String passwordProtectedAccountQuery(String signin) {
return EmailUtils.isEmail(signin) ? SELECT_PASSWORD_PROTECTED_ACCOUNT + " where email = ?" : SELECT_PASSWORD_PROTECTED_ACCOUNT + " where username = ?";
}
private RowMapper<PasswordProtectedAccount> passwordProtectedAccountMapper = new RowMapper<PasswordProtectedAccount>() {
public PasswordProtectedAccount mapRow(ResultSet rs, int row) throws SQLException {
Account account = accountMapper.mapRow(rs, row);
return new PasswordProtectedAccount(account, rs.getString("password"));
}
};
private static class PasswordProtectedAccount {
private Account account;
private String encodedPassword;
public PasswordProtectedAccount(Account account, String encodedPassword) {
this.account = account;
this.encodedPassword = encodedPassword;
}
public Account accessAccount(String password, PasswordEncoder passwordEncoder) throws InvalidPasswordException {
if (passwordEncoder.matches(password, encodedPassword)) {
return account;
} else {
throw new InvalidPasswordException();
}
}
}
private static final String SELECT_PASSWORD_PROTECTED_ACCOUNT = "select id, firstName, lastName, email, password, username, gender, pictureSet from Member";
}