Clover coverage report - Acegi Security System for Spring - 1.0.0-RC1
Coverage timestamp: Mon Dec 5 2005 09:05:15 EST
file stats: LOC: 271   Methods: 17
NCLOC: 117   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
JdbcDaoImpl.java 83.3% 92.7% 88.2% 90.6%
coverage coverage
 1    /* Copyright 2004, 2005 Acegi Technology Pty Limited
 2    *
 3    * Licensed under the Apache License, Version 2.0 (the "License");
 4    * you may not use this file except in compliance with the License.
 5    * You may obtain a copy of the License at
 6    *
 7    * http://www.apache.org/licenses/LICENSE-2.0
 8    *
 9    * Unless required by applicable law or agreed to in writing, software
 10    * distributed under the License is distributed on an "AS IS" BASIS,
 11    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    * See the License for the specific language governing permissions and
 13    * limitations under the License.
 14    */
 15   
 16    package org.acegisecurity.userdetails.jdbc;
 17   
 18    import java.sql.ResultSet;
 19    import java.sql.SQLException;
 20    import java.sql.Types;
 21    import java.util.List;
 22   
 23    import javax.sql.DataSource;
 24   
 25    import org.acegisecurity.GrantedAuthority;
 26    import org.acegisecurity.GrantedAuthorityImpl;
 27    import org.acegisecurity.userdetails.User;
 28    import org.acegisecurity.userdetails.UserDetails;
 29    import org.acegisecurity.userdetails.UserDetailsService;
 30    import org.acegisecurity.userdetails.UsernameNotFoundException;
 31    import org.apache.commons.logging.Log;
 32    import org.apache.commons.logging.LogFactory;
 33    import org.springframework.context.ApplicationContextException;
 34    import org.springframework.dao.DataAccessException;
 35    import org.springframework.jdbc.core.SqlParameter;
 36    import org.springframework.jdbc.core.support.JdbcDaoSupport;
 37    import org.springframework.jdbc.object.MappingSqlQuery;
 38   
 39   
 40    /**
 41    * <p>
 42    * Retrieves user details (username, password, enabled flag, and authorities)
 43    * from a JDBC location.
 44    * </p>
 45    *
 46    * <p>
 47    * A default database structure is assumed, (see {@link
 48    * #DEF_USERS_BY_USERNAME_QUERY} and {@link
 49    * #DEF_AUTHORITIES_BY_USERNAME_QUERY}, which most users of this class will
 50    * need to override, if using an existing scheme. This may be done by setting
 51    * the default query strings used. If this does not provide enough
 52    * flexibility, another strategy would be to subclass this class and override
 53    * the {@link MappingSqlQuery} instances used, via the {@link
 54    * #initMappingSqlQueries()} extension point.
 55    * </p>
 56    *
 57    * <p>
 58    * In order to minimise backward compatibility issues, this DAO does not
 59    * recognise the expiration of user accounts or the expiration of user
 60    * credentials. However, it does recognise and honour the user
 61    * enabled/disabled column.
 62    * </p>
 63    *
 64    * @author Ben Alex
 65    * @author colin sampaleanu
 66    * @version $Id: JdbcDaoImpl.java,v 1.17 2005/12/04 10:48:33 benalex Exp $
 67    */
 68    public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
 69    //~ Static fields/initializers =============================================
 70   
 71    public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT username,password,enabled FROM users WHERE username = ?";
 72    public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT username,authority FROM authorities WHERE username = ?";
 73    private static final Log logger = LogFactory.getLog(JdbcDaoImpl.class);
 74   
 75    //~ Instance fields ========================================================
 76   
 77    protected MappingSqlQuery authoritiesByUsernameMapping;
 78    protected MappingSqlQuery usersByUsernameMapping;
 79    private String authoritiesByUsernameQuery;
 80    private String rolePrefix = "";
 81    private String usersByUsernameQuery;
 82    private boolean usernameBasedPrimaryKey = true;
 83   
 84    //~ Constructors ===========================================================
 85   
 86  10 public JdbcDaoImpl() {
 87  10 usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
 88  10 authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
 89    }
 90   
 91    //~ Methods ================================================================
 92   
 93    /**
 94    * Allows the default query string used to retrieve authorities based on
 95    * username to be overriden, if default table or column names need to be
 96    * changed. The default query is {@link
 97    * #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure
 98    * that all returned columns are mapped back to the same column names as
 99    * in the default query.
 100    *
 101    * @param queryString The query string to set
 102    */
 103  1 public void setAuthoritiesByUsernameQuery(String queryString) {
 104  1 authoritiesByUsernameQuery = queryString;
 105    }
 106   
 107  1 public String getAuthoritiesByUsernameQuery() {
 108  1 return authoritiesByUsernameQuery;
 109    }
 110   
 111    /**
 112    * Allows a default role prefix to be specified. If this is set to a
 113    * non-empty value, then it is automatically prepended to any roles read
 114    * in from the db. This may for example be used to add the
 115    * <code>ROLE_</code> prefix expected to exist in role names (by default)
 116    * by some other Acegi Security framework classes, in the case that the
 117    * prefix is not already present in the db.
 118    *
 119    * @param rolePrefix the new prefix
 120    */
 121  1 public void setRolePrefix(String rolePrefix) {
 122  1 this.rolePrefix = rolePrefix;
 123    }
 124   
 125  1 public String getRolePrefix() {
 126  1 return rolePrefix;
 127    }
 128   
 129    /**
 130    * If <code>true</code> (the default), indicates the {@link
 131    * #getUsersByUsernameMapping()} returns a username in response to a
 132    * query. If <code>false</code>, indicates that a primary key is used
 133    * instead. If set to <code>true</code>, the class will use the
 134    * database-derived username in the returned <code>UserDetails</code>. If
 135    * <code>false</code>, the class will use the {@link
 136    * #loadUserByUsername(String)} derived username in the returned
 137    * <code>UserDetails</code>.
 138    *
 139    * @param usernameBasedPrimaryKey <code>true</code> if the mapping queries
 140    * return the username <code>String</code>, or <code>false</code>
 141    * if the mapping returns a database primary key.
 142    */
 143  0 public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
 144  0 this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
 145    }
 146   
 147  0 public boolean isUsernameBasedPrimaryKey() {
 148  0 return usernameBasedPrimaryKey;
 149    }
 150   
 151    /**
 152    * Allows the default query string used to retrieve users based on username
 153    * to be overriden, if default table or column names need to be changed.
 154    * The default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when
 155    * modifying this query, ensure that all returned columns are mapped back
 156    * to the same column names as in the default query. If the 'enabled'
 157    * column does not exist in the source db, a permanent true value for this
 158    * column may be returned by using a query similar to <br>
 159    * <pre>
 160    * "SELECT username,password,'true' as enabled FROM users WHERE username = ?"
 161    * </pre>
 162    *
 163    * @param usersByUsernameQueryString The query string to set
 164    */
 165  1 public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
 166  1 this.usersByUsernameQuery = usersByUsernameQueryString;
 167    }
 168   
 169  1 public String getUsersByUsernameQuery() {
 170  1 return usersByUsernameQuery;
 171    }
 172   
 173  8 public UserDetails loadUserByUsername(String username)
 174    throws UsernameNotFoundException, DataAccessException {
 175  8 List users = usersByUsernameMapping.execute(username);
 176   
 177  8 if (users.size() == 0) {
 178  1 throw new UsernameNotFoundException("User not found");
 179    }
 180   
 181  7 UserDetails user = (UserDetails) users.get(0); // contains no GrantedAuthority[]
 182   
 183  7 List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());
 184   
 185  7 if (dbAuths.size() == 0) {
 186  1 throw new UsernameNotFoundException("User has no GrantedAuthority");
 187    }
 188   
 189  6 GrantedAuthority[] arrayAuths = {};
 190   
 191  6 addCustomAuthorities(user.getUsername(), dbAuths);
 192   
 193  6 arrayAuths = (GrantedAuthority[]) dbAuths.toArray(arrayAuths);
 194   
 195  6 String returnUsername = user.getUsername();
 196   
 197  6 if (!usernameBasedPrimaryKey) {
 198  0 returnUsername = username;
 199    }
 200   
 201  6 return new User(returnUsername, user.getPassword(), user.isEnabled(),
 202    true, true, true, arrayAuths);
 203    }
 204   
 205    /**
 206    * Allows subclasses to add their own granted authorities to the list to be
 207    * returned in the <code>User</code>.
 208    *
 209    * @param username the username, for use by finder methods
 210    * @param authorities the current granted authorities, as populated from
 211    * the <code>authoritiesByUsername</code> mapping
 212    */
 213  6 protected void addCustomAuthorities(String username, List authorities) {}
 214   
 215  7 protected void initDao() throws ApplicationContextException {
 216  7 initMappingSqlQueries();
 217    }
 218   
 219    /**
 220    * Extension point to allow other MappingSqlQuery objects to be substituted
 221    * in a subclass
 222    */
 223  7 protected void initMappingSqlQueries() {
 224  7 this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());
 225  7 this.authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
 226    }
 227   
 228    //~ Inner Classes ==========================================================
 229   
 230    /**
 231    * Query object to look up a user's authorities.
 232    */
 233    protected class AuthoritiesByUsernameMapping extends MappingSqlQuery {
 234  7 protected AuthoritiesByUsernameMapping(DataSource ds) {
 235  7 super(ds, authoritiesByUsernameQuery);
 236  7 declareParameter(new SqlParameter(Types.VARCHAR));
 237  7 compile();
 238    }
 239   
 240  9 protected Object mapRow(ResultSet rs, int rownum)
 241    throws SQLException {
 242  9 String roleName = rolePrefix + rs.getString(2);
 243  9 GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
 244   
 245  9 return authority;
 246    }
 247    }
 248   
 249    /**
 250    * Query object to look up a user.
 251    */
 252    protected class UsersByUsernameMapping extends MappingSqlQuery {
 253  7 protected UsersByUsernameMapping(DataSource ds) {
 254  7 super(ds, usersByUsernameQuery);
 255  7 declareParameter(new SqlParameter(Types.VARCHAR));
 256  7 compile();
 257    }
 258   
 259  7 protected Object mapRow(ResultSet rs, int rownum)
 260    throws SQLException {
 261  7 String username = rs.getString(1);
 262  7 String password = rs.getString(2);
 263  7 boolean enabled = rs.getBoolean(3);
 264  7 UserDetails user = new User(username, password, enabled, true,
 265    true, true,
 266    new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});
 267   
 268  7 return user;
 269    }
 270    }
 271    }