1 23 24 package com.sun.enterprise.security.auth.realm.jdbc; 25 26 import java.io.UnsupportedEncodingException ; 27 import java.security.MessageDigest ; 28 import java.security.NoSuchAlgorithmException ; 29 import java.sql.Connection ; 30 import java.sql.PreparedStatement ; 31 import java.sql.ResultSet ; 32 import java.sql.SQLException ; 33 import java.util.ArrayList ; 34 import java.util.Enumeration ; 35 import java.util.HashMap ; 36 import java.util.List ; 37 import java.util.Map ; 38 import java.util.Properties ; 39 import java.util.Vector ; 40 import java.util.logging.Logger ; 41 import java.util.logging.Level ; 42 import javax.sql.DataSource ; 43 44 import sun.misc.BASE64Encoder; 45 46 import com.sun.enterprise.connectors.ConnectorRuntime; 47 import com.sun.enterprise.security.LoginException; 48 import com.sun.enterprise.security.auth.realm.IASRealm; 49 import com.sun.enterprise.security.auth.realm.BadRealmException; 50 import com.sun.enterprise.security.auth.realm.NoSuchUserException; 51 import com.sun.enterprise.security.auth.realm.NoSuchRealmException; 52 import com.sun.enterprise.security.auth.realm.InvalidOperationException; 53 54 55 77 public final class JDBCRealm extends IASRealm { 78 public static final String AUTH_TYPE = "jdbc"; 80 81 public static final String PARAM_DATASOURCE_JNDI = "datasource-jndi"; 82 public static final String PARAM_DB_USER = "db-user"; 83 public static final String PARAM_DB_PASSWORD = "db-password"; 84 85 public static final String PARAM_DIGEST_ALGORITHM = "digest-algorithm"; 86 public static final String DEFAULT_DIGEST_ALGORITHM = "MD5"; 87 public static final String NONE = "none"; 88 89 public static final String PARAM_ENCODING = "encoding"; 90 public static final String HEX = "hex"; 91 public static final String BASE64 = "base64"; 92 public static final String DEFAULT_ENCODING = HEX; 94 public static final String PARAM_CHARSET = "charset"; 95 public static final String PARAM_USER_TABLE = "user-table"; 96 public static final String PARAM_USER_NAME_COLUMN = "user-name-column"; 97 public static final String PARAM_PASSWORD_COLUMN = "password-column"; 98 public static final String PARAM_GROUP_TABLE = "group-table"; 99 public static final String PARAM_GROUP_NAME_COLUMN = "group-name-column"; 100 101 private static final char[] HEXADECIMAL = { '0', '1', '2', '3', 102 '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 103 104 private Map <String , Vector > groupCache; 105 private Vector <String > emptyVector; 106 private String passwordQuery = null; 107 private String groupQuery = null; 108 private MessageDigest md = null; 109 110 121 public synchronized void init(Properties props) 122 throws BadRealmException, NoSuchRealmException{ 123 String jaasCtx = props.getProperty(IASRealm.JAAS_CONTEXT_PARAM); 124 String dbUser = props.getProperty(PARAM_DB_USER); 125 String dbPassword = props.getProperty(PARAM_DB_PASSWORD); 126 String dsJndi = props.getProperty(PARAM_DATASOURCE_JNDI); 127 String digestAlgorithm = props.getProperty(PARAM_DIGEST_ALGORITHM, 128 DEFAULT_DIGEST_ALGORITHM); 129 String encoding = props.getProperty(PARAM_ENCODING); 130 String charset = props.getProperty(PARAM_CHARSET); 131 String userTable = props.getProperty(PARAM_USER_TABLE); 132 String userNameColumn = props.getProperty(PARAM_USER_NAME_COLUMN); 133 String passwordColumn = props.getProperty(PARAM_PASSWORD_COLUMN); 134 String groupTable = props.getProperty(PARAM_GROUP_TABLE); 135 String groupNameColumn = props.getProperty(PARAM_GROUP_NAME_COLUMN); 136 137 if (jaasCtx == null) { 138 String msg = sm.getString( 139 "realm.missingprop", IASRealm.JAAS_CONTEXT_PARAM, "JDBCRealm"); 140 throw new BadRealmException(msg); 141 } 142 143 if (dsJndi == null) { 144 String msg = sm.getString( 145 "realm.missingprop", PARAM_DATASOURCE_JNDI, "JDBCRealm"); 146 throw new BadRealmException(msg); 147 } 148 if (userTable == null) { 149 String msg = sm.getString( 150 "realm.missingprop", PARAM_USER_TABLE, "JDBCRealm"); 151 throw new BadRealmException(msg); 152 } 153 if (groupTable == null) { 154 String msg = sm.getString( 155 "realm.missingprop", PARAM_GROUP_TABLE, "JDBCRealm"); 156 throw new BadRealmException(msg); 157 } 158 if (userNameColumn == null) { 159 String msg = sm.getString( 160 "realm.missingprop", PARAM_USER_NAME_COLUMN, "JDBCRealm"); 161 throw new BadRealmException(msg); 162 } 163 if (passwordColumn == null) { 164 String msg = sm.getString( 165 "realm.missingprop", PARAM_PASSWORD_COLUMN, "JDBCRealm"); 166 throw new BadRealmException(msg); 167 } 168 if (groupNameColumn == null) { 169 String msg = sm.getString( 170 "realm.missingprop", PARAM_GROUP_NAME_COLUMN, "JDBCRealm"); 171 throw new BadRealmException(msg); 172 } 173 174 passwordQuery = "SELECT " + passwordColumn + " FROM " + userTable + 175 " WHERE " + userNameColumn + " = ?"; 176 177 groupQuery = "SELECT " + groupNameColumn + " FROM " + groupTable + 178 " WHERE " + userNameColumn + " = ? "; 179 180 if (!NONE.equalsIgnoreCase(digestAlgorithm)) { 181 try { 182 md = MessageDigest.getInstance(digestAlgorithm); 183 } catch(NoSuchAlgorithmException e) { 184 String msg = sm.getString("jdbcrealm.notsupportdigestalg", 185 digestAlgorithm); 186 throw new BadRealmException(msg); 187 } 188 } 189 if (md != null && encoding == null) { 190 encoding = DEFAULT_ENCODING; 191 } 192 193 this.setProperty(IASRealm.JAAS_CONTEXT_PARAM, jaasCtx); 194 if (dbUser != null && dbPassword != null) { 195 this.setProperty(PARAM_DB_USER, dbUser); 196 this.setProperty(PARAM_DB_PASSWORD, dbPassword); 197 } 198 this.setProperty(PARAM_DATASOURCE_JNDI, dsJndi); 199 this.setProperty(PARAM_DIGEST_ALGORITHM, digestAlgorithm); 200 if (encoding != null) { 201 this.setProperty(PARAM_ENCODING, encoding); 202 } 203 if (charset != null) { 204 this.setProperty(PARAM_CHARSET, charset); 205 } 206 207 if (_logger.isLoggable(Level.FINEST)) { 208 _logger.finest("JDBCRealm : " + 209 IASRealm.JAAS_CONTEXT_PARAM + "= " + jaasCtx + ", " + 210 PARAM_DATASOURCE_JNDI + " = " + dsJndi + ", " + 211 PARAM_DB_USER + " = " + dbUser + ", " + 212 PARAM_DIGEST_ALGORITHM + " = " + digestAlgorithm + ", " + 213 PARAM_ENCODING + " = " + encoding + ", " + 214 PARAM_CHARSET + " = " + charset); 215 } 216 217 groupCache = new HashMap <String , Vector >(); 218 emptyVector = new Vector <String >(); 219 } 220 221 228 public String getAuthType(){ 229 return AUTH_TYPE; 230 } 231 232 245 public Enumeration getGroupNames(String username) 246 throws InvalidOperationException, NoSuchUserException { 247 Vector vector = groupCache.get(username); 248 if (vector == null) { 249 String [] grps = findGroups(username); 250 setGroupNames(username, grps); 251 vector = groupCache.get(username); 252 } 253 return vector.elements(); 254 } 255 256 private void setGroupNames(String username, String [] groups) { 257 Vector <String > v = null; 258 259 if (groups == null) { 260 v = emptyVector; 261 262 } else { 263 v = new Vector <String >(groups.length + 1); 264 for (int i=0; i<groups.length; i++) { 265 v.add(groups[i]); 266 } 267 } 268 269 synchronized (this) { 270 groupCache.put(username, v); 271 } 272 } 273 274 275 283 public String [] authenticate(String username, String password) { 284 String [] groups = null; 285 if (isUserValid(username, password)) { 286 groups = findGroups(username); 287 setGroupNames(username, groups); 288 } 289 return groups; 290 } 291 292 298 private boolean isUserValid(String user, String password) { 299 Connection connection = null; 300 PreparedStatement statement = null; 301 ResultSet rs = null; 302 boolean valid = false; 303 304 try { 305 String hpwd = hashPassword(password); 306 connection = getConnection(); 307 statement = connection.prepareStatement(passwordQuery); 308 statement.setString(1,user); 309 rs = statement.executeQuery(); 310 String pwd = null; 311 if (rs.next()) { 312 pwd = rs.getString(1); 313 if (HEX.equalsIgnoreCase(getProperty(PARAM_ENCODING))) { 314 valid = pwd.equalsIgnoreCase(hpwd); 315 } else { 316 valid = pwd.equals(hpwd); 317 } 318 } 319 } catch(Exception ex) { 320 _logger.log(Level.SEVERE, "jdbcrealm.invaliduser", user); 321 if (_logger.isLoggable(Level.FINE)) { 322 _logger.log(Level.FINE, "Cannot validate user", ex); 323 } 324 } finally { 325 close(connection, statement, rs); 326 } 327 return valid; 328 } 329 330 private String hashPassword(String password) 331 throws UnsupportedEncodingException { 332 String result = null; 333 byte[] bytes = null; 334 String charSet = getProperty(PARAM_CHARSET); 335 if (charSet != null) { 336 bytes = password.getBytes(charSet); 337 } else { 338 bytes = password.getBytes(); 339 } 340 if (md != null) { 341 synchronized(md) { 342 md.reset(); 343 bytes = md.digest(bytes); 344 } 345 } 346 347 String encoding = getProperty(PARAM_ENCODING); 348 if (HEX.equalsIgnoreCase(encoding)) { 349 result = hexEncode(bytes); 350 } else if (BASE64.equalsIgnoreCase(encoding)) { 351 result = base64Encode(bytes); 352 } else { result = new String (bytes); 354 } 355 return result; 356 } 357 358 private String hexEncode(byte[] bytes) { 359 StringBuilder sb = new StringBuilder (2 * bytes.length); 360 for (int i = 0; i < bytes.length; i++) { 361 int low = (int)(bytes[i] & 0x0f); 362 int high = (int)((bytes[i] & 0xf0) >> 4); 363 sb.append(HEXADECIMAL[high]); 364 sb.append(HEXADECIMAL[low]); 365 } 366 return sb.toString(); 367 } 368 369 private String base64Encode(byte[] bytes) { 370 BASE64Encoder encoder = new BASE64Encoder(); 371 return encoder.encode(bytes); 372 } 373 374 379 private String [] findGroups(String user) { 380 Connection connection = null; 381 PreparedStatement statement = null; 382 ResultSet rs = null; 383 try{ 384 connection = getConnection(); 385 statement = connection.prepareStatement(groupQuery); 386 statement.setString(1,user); 387 rs = statement.executeQuery(); 388 final List <String > groups = new ArrayList <String >(); 389 while (rs.next()) { 390 groups.add(rs.getString(1)); 391 } 392 final String [] groupArray = new String [groups.size()]; 393 return groups.toArray(groupArray); 394 } catch(Exception ex) { 395 _logger.log(Level.SEVERE, "jdbcrealm.grouperror", user); 396 if (_logger.isLoggable(Level.FINE)) { 397 _logger.log(Level.FINE, "Cannot load group", ex); 398 } 399 return null; 400 } finally { 401 close(connection, statement, rs); 402 } 403 } 404 405 private void close(Connection conn, PreparedStatement stmt, 406 ResultSet rs) { 407 if (rs != null) { 408 try { 409 rs.close(); 410 } catch(Exception ex) { 411 } 412 } 413 414 if (stmt != null) { 415 try { 416 stmt.close(); 417 } catch(Exception ex) { 418 } 419 } 420 421 if (conn != null) { 422 try { 423 conn.close(); 424 } catch(Exception ex) { 425 } 426 } 427 } 428 429 433 private Connection getConnection() throws LoginException { 434 435 final String dsJndi = this.getProperty(PARAM_DATASOURCE_JNDI); 436 final String dbUser = this.getProperty(PARAM_DB_USER); 437 final String dbPassword = this.getProperty(PARAM_DB_PASSWORD); 438 try{ 439 final DataSource dataSource = 440 (DataSource )ConnectorRuntime.getRuntime().lookupNonTxResource(dsJndi); 441 442 Connection connection = null; 443 if (dbUser != null && dbPassword != null) { 444 connection = dataSource.getConnection(dbUser, dbPassword); 445 } else { 446 connection = dataSource.getConnection(); 447 } 448 return connection; 449 } catch(Exception ex) { 450 String msg = sm.getString("jdbcrealm.cantconnect", dsJndi, dbUser); 451 LoginException loginEx = new LoginException(msg); 452 loginEx.initCause(ex); 453 throw loginEx; 454 } 455 } 456 } 457 | Popular Tags |