Coverage Report - org.acegisecurity.userdetails.jdbc.JdbcDaoImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
JdbcDaoImpl
93% 
100% 
1.294
 
 1  
 /* Copyright 2004, 2005, 2006 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 org.acegisecurity.GrantedAuthority;
 19  
 import org.acegisecurity.GrantedAuthorityImpl;
 20  
 
 21  
 import org.acegisecurity.userdetails.User;
 22  
 import org.acegisecurity.userdetails.UserDetails;
 23  
 import org.acegisecurity.userdetails.UserDetailsService;
 24  
 import org.acegisecurity.userdetails.UsernameNotFoundException;
 25  
 
 26  
 import org.springframework.context.ApplicationContextException;
 27  
 
 28  
 import org.springframework.dao.DataAccessException;
 29  
 
 30  
 import org.springframework.jdbc.core.SqlParameter;
 31  
 import org.springframework.jdbc.core.support.JdbcDaoSupport;
 32  
 import org.springframework.jdbc.object.MappingSqlQuery;
 33  
 
 34  
 import java.sql.ResultSet;
 35  
 import java.sql.SQLException;
 36  
 import java.sql.Types;
 37  
 
 38  
 import java.util.List;
 39  
 
 40  
 import javax.sql.DataSource;
 41  
 
 42  
 
 43  
 /**
 44  
  * <p>Retrieves user details (username, password, enabled flag, and authorities) from a JDBC location.</p>
 45  
  *  <p>A default database structure is assumed, (see {@link #DEF_USERS_BY_USERNAME_QUERY} and {@link
 46  
  * #DEF_AUTHORITIES_BY_USERNAME_QUERY}, which most users of this class will need to override, if using an existing
 47  
  * scheme. This may be done by setting the default query strings used. If this does not provide enough flexibility,
 48  
  * another strategy would be to subclass this class and override the {@link MappingSqlQuery} instances used, via the
 49  
  * {@link #initMappingSqlQueries()} extension point.</p>
 50  
  *  <p>In order to minimise backward compatibility issues, this DAO does not recognise the expiration of user
 51  
  * accounts or the expiration of user credentials. However, it does recognise and honour the user enabled/disabled
 52  
  * column.</p>
 53  
  *
 54  
  * @author Ben Alex
 55  
  * @author colin sampaleanu
 56  
  * @version $Id: JdbcDaoImpl.java 1784 2007-02-24 21:00:24Z luke_t $
 57  
  */
 58  23
 public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService {
 59  
     //~ Static fields/initializers =====================================================================================
 60  
 
 61  
     public static final String DEF_USERS_BY_USERNAME_QUERY =
 62  
             "SELECT username,password,enabled FROM users WHERE username = ?";
 63  
     public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
 64  
             "SELECT username,authority FROM authorities WHERE username = ?";
 65  
 
 66  
     //~ Instance fields ================================================================================================
 67  
 
 68  
     protected MappingSqlQuery authoritiesByUsernameMapping;
 69  
     protected MappingSqlQuery usersByUsernameMapping;
 70  
     private String authoritiesByUsernameQuery;
 71  10
     private String rolePrefix = "";
 72  
     private String usersByUsernameQuery;
 73  10
     private boolean usernameBasedPrimaryKey = true;
 74  
 
 75  
     //~ Constructors ===================================================================================================
 76  
 
 77  10
     public JdbcDaoImpl() {
 78  10
         usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
 79  10
         authoritiesByUsernameQuery = DEF_AUTHORITIES_BY_USERNAME_QUERY;
 80  10
     }
 81  
 
 82  
     //~ Methods ========================================================================================================
 83  
 
 84  
     /**
 85  
      * Allows subclasses to add their own granted authorities to the list to be returned in the
 86  
      * <code>User</code>.
 87  
      *
 88  
      * @param username the username, for use by finder methods
 89  
      * @param authorities the current granted authorities, as populated from the <code>authoritiesByUsername</code>
 90  
      *        mapping
 91  
      */
 92  7
     protected void addCustomAuthorities(String username, List authorities) {}
 93  
 
 94  
     public String getAuthoritiesByUsernameQuery() {
 95  1
         return authoritiesByUsernameQuery;
 96  
     }
 97  
 
 98  
     public String getRolePrefix() {
 99  1
         return rolePrefix;
 100  
     }
 101  
 
 102  
     public String getUsersByUsernameQuery() {
 103  1
         return usersByUsernameQuery;
 104  
     }
 105  
 
 106  
     protected void initDao() throws ApplicationContextException {
 107  7
         initMappingSqlQueries();
 108  7
     }
 109  
 
 110  
     /**
 111  
      * Extension point to allow other MappingSqlQuery objects to be substituted in a subclass
 112  
      */
 113  
     protected void initMappingSqlQueries() {
 114  7
         this.usersByUsernameMapping = new UsersByUsernameMapping(getDataSource());
 115  7
         this.authoritiesByUsernameMapping = new AuthoritiesByUsernameMapping(getDataSource());
 116  7
     }
 117  
 
 118  
     public boolean isUsernameBasedPrimaryKey() {
 119  0
         return usernameBasedPrimaryKey;
 120  
     }
 121  
 
 122  
     public UserDetails loadUserByUsername(String username)
 123  
         throws UsernameNotFoundException, DataAccessException {
 124  8
         List users = usersByUsernameMapping.execute(username);
 125  
 
 126  8
         if (users.size() == 0) {
 127  1
             throw new UsernameNotFoundException("User not found");
 128  
         }
 129  
 
 130  7
         UserDetails user = (UserDetails) users.get(0); // contains no GrantedAuthority[]
 131  
 
 132  7
         List dbAuths = authoritiesByUsernameMapping.execute(user.getUsername());
 133  
 
 134  7
         addCustomAuthorities(user.getUsername(), dbAuths);
 135  
 
 136  7
         if (dbAuths.size() == 0) {
 137  1
             throw new UsernameNotFoundException("User has no GrantedAuthority");
 138  
         }
 139  
 
 140  6
         GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbAuths.toArray(new GrantedAuthority[dbAuths.size()]);
 141  
 
 142  6
         String returnUsername = user.getUsername();
 143  
 
 144  6
         if (!usernameBasedPrimaryKey) {
 145  0
             returnUsername = username;
 146  
         }
 147  
 
 148  6
         return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);
 149  
     }
 150  
 
 151  
     /**
 152  
      * Allows the default query string used to retrieve authorities based on username to be overriden, if
 153  
      * default table or column names need to be changed. The default query is {@link
 154  
      * #DEF_AUTHORITIES_BY_USERNAME_QUERY}; when modifying this query, ensure that all returned columns are mapped
 155  
      * back to the same column names as in the default query.
 156  
      *
 157  
      * @param queryString The query string to set
 158  
      */
 159  
     public void setAuthoritiesByUsernameQuery(String queryString) {
 160  1
         authoritiesByUsernameQuery = queryString;
 161  1
     }
 162  
 
 163  
     /**
 164  
      * Allows a default role prefix to be specified. If this is set to a non-empty value, then it is
 165  
      * automatically prepended to any roles read in from the db. This may for example be used to add the
 166  
      * <code>ROLE_</code> prefix expected to exist in role names (by default) by some other Acegi Security framework
 167  
      * classes, in the case that the prefix is not already present in the db.
 168  
      *
 169  
      * @param rolePrefix the new prefix
 170  
      */
 171  
     public void setRolePrefix(String rolePrefix) {
 172  1
         this.rolePrefix = rolePrefix;
 173  1
     }
 174  
 
 175  
     /**
 176  
      * If <code>true</code> (the default), indicates the {@link #getUsersByUsernameQuery()} returns a username
 177  
      * in response to a query. If <code>false</code>, indicates that a primary key is used instead. If set to
 178  
      * <code>true</code>, the class will use the database-derived username in the returned <code>UserDetails</code>.
 179  
      * If <code>false</code>, the class will use the {@link #loadUserByUsername(String)} derived username in the
 180  
      * returned <code>UserDetails</code>.
 181  
      *
 182  
      * @param usernameBasedPrimaryKey <code>true</code> if the mapping queries return the username <code>String</code>,
 183  
      *        or <code>false</code> if the mapping returns a database primary key.
 184  
      */
 185  
     public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
 186  0
         this.usernameBasedPrimaryKey = usernameBasedPrimaryKey;
 187  0
     }
 188  
 
 189  
     /**
 190  
      * Allows the default query string used to retrieve users based on username to be overriden, if default
 191  
      * table or column names need to be changed. The default query is {@link #DEF_USERS_BY_USERNAME_QUERY}; when
 192  
      * modifying this query, ensure that all returned columns are mapped back to the same column names as in the
 193  
      * default query. If the 'enabled' column does not exist in the source db, a permanent true value for this column
 194  
      * may be returned by using a query similar to <br><pre>
 195  
      * "SELECT username,password,'true' as enabled FROM users WHERE username = ?"</pre>
 196  
      *
 197  
      * @param usersByUsernameQueryString The query string to set
 198  
      */
 199  
     public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
 200  1
         this.usersByUsernameQuery = usersByUsernameQueryString;
 201  1
     }
 202  
 
 203  
     //~ Inner Classes ==================================================================================================
 204  
 
 205  
     /**
 206  
      * Query object to look up a user's authorities.
 207  
      */
 208  
     protected class AuthoritiesByUsernameMapping extends MappingSqlQuery {
 209  7
         protected AuthoritiesByUsernameMapping(DataSource ds) {
 210  7
             super(ds, authoritiesByUsernameQuery);
 211  7
             declareParameter(new SqlParameter(Types.VARCHAR));
 212  7
             compile();
 213  7
         }
 214  
 
 215  
         protected Object mapRow(ResultSet rs, int rownum)
 216  
             throws SQLException {
 217  9
             String roleName = rolePrefix + rs.getString(2);
 218  9
             GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);
 219  
 
 220  9
             return authority;
 221  
         }
 222  
     }
 223  
 
 224  
     /**
 225  
      * Query object to look up a user.
 226  
      */
 227  
     protected class UsersByUsernameMapping extends MappingSqlQuery {
 228  7
         protected UsersByUsernameMapping(DataSource ds) {
 229  7
             super(ds, usersByUsernameQuery);
 230  7
             declareParameter(new SqlParameter(Types.VARCHAR));
 231  7
             compile();
 232  7
         }
 233  
 
 234  
         protected Object mapRow(ResultSet rs, int rownum)
 235  
             throws SQLException {
 236  7
             String username = rs.getString(1);
 237  7
             String password = rs.getString(2);
 238  7
             boolean enabled = rs.getBoolean(3);
 239  7
             UserDetails user = new User(username, password, enabled, true, true, true,
 240  
                     new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});
 241  
 
 242  7
             return user;
 243  
         }
 244  
     }
 245  
 }