1 18 package org.apache.activemq.jaas; 19 20 import java.io.IOException ; 21 import java.text.MessageFormat ; 22 import java.util.ArrayList ; 23 import java.util.HashSet ; 24 import java.util.Hashtable ; 25 import java.util.Iterator ; 26 import java.util.Map ; 27 import java.util.Set ; 28 import javax.naming.AuthenticationException ; 29 import javax.naming.CommunicationException ; 30 import javax.naming.Context ; 31 import javax.naming.Name ; 32 import javax.naming.NameParser ; 33 import javax.naming.NamingEnumeration ; 34 import javax.naming.NamingException ; 35 import javax.naming.directory.Attribute ; 36 import javax.naming.directory.Attributes ; 37 import javax.naming.directory.DirContext ; 38 import javax.naming.directory.InitialDirContext ; 39 import javax.naming.directory.SearchControls ; 40 import javax.naming.directory.SearchResult ; 41 import javax.security.auth.Subject ; 42 import javax.security.auth.callback.Callback ; 43 import javax.security.auth.callback.CallbackHandler ; 44 import javax.security.auth.callback.NameCallback ; 45 import javax.security.auth.callback.PasswordCallback ; 46 import javax.security.auth.callback.UnsupportedCallbackException ; 47 import javax.security.auth.login.LoginException ; 48 import javax.security.auth.login.FailedLoginException ; 49 import javax.security.auth.spi.LoginModule ; 50 51 import org.apache.commons.logging.Log; 52 import org.apache.commons.logging.LogFactory; 53 54 55 58 public class LDAPLoginModule implements LoginModule { 59 60 private static Log log = LogFactory.getLog(LDAPLoginModule.class); 61 62 private Subject subject; 63 private CallbackHandler handler; 64 65 private static final String INITIAL_CONTEXT_FACTORY = "initialContextFactory"; 66 private static final String CONNECTION_URL = "connectionURL"; 67 private static final String CONNECTION_USERNAME = "connectionUsername"; 68 private static final String CONNECTION_PASSWORD = "connectionPassword"; 69 private static final String CONNECTION_PROTOCOL = "connectionProtocol"; 70 private static final String AUTHENTICATION = "authentication"; 71 private static final String USER_BASE = "userBase"; 72 private static final String USER_SEARCH_MATCHING = "userSearchMatching"; 73 private static final String USER_SEARCH_SUBTREE = "userSearchSubtree"; 74 private static final String ROLE_BASE = "roleBase"; 75 private static final String ROLE_NAME = "roleName"; 76 private static final String ROLE_SEARCH_MATCHING = "roleSearchMatching"; 77 private static final String ROLE_SEARCH_SUBTREE = "roleSearchSubtree"; 78 private static final String USER_ROLE_NAME = "userRoleName"; 79 80 private String initialContextFactory; 81 private String connectionURL; 82 private String connectionUsername; 83 private String connectionPassword; 84 private String connectionProtocol; 85 private String authentication; 86 private String userBase; 87 private String roleBase; 88 private String roleName; 89 private String userRoleName; 90 91 private String username; 92 93 protected DirContext context = null; 94 95 private MessageFormat userSearchMatchingFormat; 96 private MessageFormat roleSearchMatchingFormat; 97 98 private boolean userSearchSubtreeBool = false; 99 private boolean roleSearchSubtreeBool = false; 100 101 Set groups = new HashSet (); 102 103 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { 104 this.subject = subject; 105 this.handler = callbackHandler; 106 initialContextFactory = (String ) options.get(INITIAL_CONTEXT_FACTORY); 107 connectionURL = (String ) options.get(CONNECTION_URL); 108 connectionUsername = (String ) options.get(CONNECTION_USERNAME); 109 connectionPassword = (String ) options.get(CONNECTION_PASSWORD); 110 connectionProtocol = (String ) options.get(CONNECTION_PROTOCOL); 111 authentication = (String ) options.get(AUTHENTICATION); 112 userBase = (String ) options.get(USER_BASE); 113 String userSearchMatching = (String ) options.get(USER_SEARCH_MATCHING); 114 String userSearchSubtree = (String ) options.get(USER_SEARCH_SUBTREE); 115 roleBase = (String ) options.get(ROLE_BASE); 116 roleName = (String ) options.get(ROLE_NAME); 117 String roleSearchMatching = (String ) options.get(ROLE_SEARCH_MATCHING); 118 String roleSearchSubtree = (String ) options.get(ROLE_SEARCH_SUBTREE); 119 userRoleName = (String ) options.get(USER_ROLE_NAME); 120 userSearchMatchingFormat = new MessageFormat (userSearchMatching); 121 roleSearchMatchingFormat = new MessageFormat (roleSearchMatching); 122 userSearchSubtreeBool = new Boolean (userSearchSubtree).booleanValue(); 123 roleSearchSubtreeBool = new Boolean (roleSearchSubtree).booleanValue(); 124 } 125 126 public boolean login() throws LoginException { 127 Callback [] callbacks = new Callback [2]; 128 129 callbacks[0] = new NameCallback ("User name"); 130 callbacks[1] = new PasswordCallback ("Password", false); 131 try { 132 handler.handle(callbacks); 133 } catch (IOException ioe) { 134 throw (LoginException ) new LoginException ().initCause(ioe); 135 } catch (UnsupportedCallbackException uce) { 136 throw (LoginException ) new LoginException ().initCause(uce); 137 } 138 username = ((NameCallback ) callbacks[0]).getName(); 139 String password = new String (((PasswordCallback ) callbacks[1]).getPassword()); 140 141 if (username == null || "".equals(username) || password == null || "".equals(password)) { 142 return false; 143 } 144 145 try { 146 boolean result = authenticate(username, password); 147 if(!result) { 148 throw new FailedLoginException (); 149 } else { 150 return true; 151 } 152 } catch (Exception e) { 153 throw (LoginException ) new LoginException ("LDAP Error").initCause(e); 154 } 155 } 156 157 public boolean logout() throws LoginException { 158 username = null; 159 return true; 160 } 161 162 public boolean commit() throws LoginException { 163 Set principals = subject.getPrincipals(); 164 principals.add(new UserPrincipal(username)); 165 Iterator iter = groups.iterator(); 166 while (iter.hasNext()) { 167 principals.add(iter.next()); 168 } 169 return true; 170 } 171 172 public boolean abort() throws LoginException { 173 username = null; 174 return true; 175 } 176 177 protected void close(DirContext context) { 178 try { 179 context.close(); 180 } catch (Exception e) { 181 log.error(e); 182 } 183 } 184 185 protected boolean authenticate(String username, String password) throws Exception { 186 187 DirContext context = null; 188 context = open(); 189 190 try { 191 192 String filter = userSearchMatchingFormat.format(new String []{username}); 193 SearchControls constraints = new SearchControls (); 194 if (userSearchSubtreeBool) { 195 constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 196 } else { 197 constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); 198 } 199 200 ArrayList list = new ArrayList (); 202 if (userRoleName != null) { 203 list.add(userRoleName); 204 } 205 String [] attribs = new String [list.size()]; 206 list.toArray(attribs); 207 constraints.setReturningAttributes(attribs); 208 209 210 NamingEnumeration results = context.search(userBase, filter, constraints); 211 212 if (results == null || !results.hasMore()) { 213 return false; 214 } 215 216 SearchResult result = (SearchResult ) results.next(); 217 218 if (results.hasMore()) { 219 } 221 NameParser parser = context.getNameParser(""); 222 Name contextName = parser.parse(context.getNameInNamespace()); 223 Name baseName = parser.parse(userBase); 224 Name entryName = parser.parse(result.getName()); 225 Name name = contextName.addAll(baseName); 226 name = name.addAll(entryName); 227 String dn = name.toString(); 228 229 Attributes attrs = result.getAttributes(); 230 if (attrs == null) { 231 return false; 232 } 233 ArrayList roles = null; 234 if (userRoleName != null) { 235 roles = addAttributeValues(userRoleName, attrs, roles); 236 } 237 238 if (bindUser(context, dn, password)) { 240 roles = getRoles(context, dn, username, roles); 242 for (int i = 0; i < roles.size(); i++) { 243 groups.add(new GroupPrincipal((String ) roles.get(i))); 244 } 245 } else { 246 return false; 247 } 248 } catch (CommunicationException e) { 249 250 } catch (NamingException e) { 251 if (context != null) { 252 close(context); 253 } 254 return false; 255 } 256 257 258 return true; 259 } 260 261 protected ArrayList getRoles(DirContext context, String dn, String username, ArrayList currentRoles) throws NamingException { 262 ArrayList list = currentRoles; 263 if (list == null) { 264 list = new ArrayList (); 265 } 266 if (roleName == null || "".equals(roleName)) { 267 return list; 268 } 269 String filter = roleSearchMatchingFormat.format(new String []{doRFC2254Encoding(dn), username}); 270 271 SearchControls constraints = new SearchControls (); 272 if (roleSearchSubtreeBool) { 273 constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); 274 } else { 275 constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); 276 } 277 NamingEnumeration results = 278 context.search(roleBase, filter, constraints); 279 while (results.hasMore()) { 280 SearchResult result = (SearchResult ) results.next(); 281 Attributes attrs = result.getAttributes(); 282 if (attrs == null) { 283 continue; 284 } 285 list = addAttributeValues(roleName, attrs, list); 286 } 287 return list; 288 289 } 290 291 292 protected String doRFC2254Encoding(String inputString) { 293 StringBuffer buf = new StringBuffer (inputString.length()); 294 for (int i = 0; i < inputString.length(); i++) { 295 char c = inputString.charAt(i); 296 switch (c) { 297 case '\\': 298 buf.append("\\5c"); 299 break; 300 case '*': 301 buf.append("\\2a"); 302 break; 303 case '(': 304 buf.append("\\28"); 305 break; 306 case ')': 307 buf.append("\\29"); 308 break; 309 case '\0': 310 buf.append("\\00"); 311 break; 312 default: 313 buf.append(c); 314 break; 315 } 316 } 317 return buf.toString(); 318 } 319 320 protected boolean bindUser(DirContext context, String dn, String password) throws NamingException { 321 boolean isValid = false; 322 323 context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn); 324 context.addToEnvironment(Context.SECURITY_CREDENTIALS, password); 325 try { 326 context.getAttributes("", null); 327 isValid = true; 328 } catch (AuthenticationException e) { 329 isValid = false; 330 log.debug("Authentication failed for dn=" + dn); 331 } 332 333 if (connectionUsername != null) { 334 context.addToEnvironment(Context.SECURITY_PRINCIPAL, 335 connectionUsername); 336 } else { 337 context.removeFromEnvironment(Context.SECURITY_PRINCIPAL); 338 } 339 340 if (connectionPassword != null) { 341 context.addToEnvironment(Context.SECURITY_CREDENTIALS, 342 connectionPassword); 343 } else { 344 context.removeFromEnvironment(Context.SECURITY_CREDENTIALS); 345 } 346 347 return isValid; 348 } 349 350 private ArrayList addAttributeValues(String attrId, Attributes attrs, ArrayList values) 351 throws NamingException 352 { 353 354 if (attrId == null || attrs == null) { 355 return values; 356 } 357 if (values == null) { 358 values = new ArrayList (); 359 } 360 Attribute attr = attrs.get(attrId); 361 if (attr == null) { 362 return (values); 363 } 364 NamingEnumeration e = attr.getAll(); 365 while (e.hasMore()) { 366 String value = (String ) e.next(); 367 values.add(value); 368 } 369 return values; 370 } 371 372 protected DirContext open() throws NamingException { 373 if (context != null) { 374 return context; 375 } 376 377 try { 378 Hashtable env = new Hashtable (); 379 env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory); 380 if (connectionUsername != null || !"".equals(connectionUsername)) { 381 env.put(Context.SECURITY_PRINCIPAL, connectionUsername); 382 } 383 if (connectionPassword != null || !"".equals(connectionPassword)) { 384 env.put(Context.SECURITY_CREDENTIALS, connectionPassword); 385 } 386 env.put(Context.SECURITY_PROTOCOL, connectionProtocol); 387 env.put(Context.PROVIDER_URL, connectionURL); 388 env.put(Context.SECURITY_AUTHENTICATION, authentication); 389 context = new InitialDirContext (env); 390 391 } catch (NamingException e) { 392 log.error(e); 393 throw e; 394 } 395 return context; 396 } 397 398 } 399 | Popular Tags |