001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.jaas; 018 019import java.io.File; 020import java.io.IOException; 021import java.security.Principal; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025 026import javax.security.auth.Subject; 027import javax.security.auth.callback.Callback; 028import javax.security.auth.callback.CallbackHandler; 029import javax.security.auth.callback.NameCallback; 030import javax.security.auth.callback.PasswordCallback; 031import javax.security.auth.callback.UnsupportedCallbackException; 032import javax.security.auth.login.FailedLoginException; 033import javax.security.auth.login.LoginException; 034import javax.security.auth.spi.LoginModule; 035 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039public class PropertiesLoginModule implements LoginModule { 040 041 private static final String USER_FILE = "org.apache.activemq.jaas.properties.user"; 042 private static final String GROUP_FILE = "org.apache.activemq.jaas.properties.group"; 043 044 private static final Logger LOG = LoggerFactory.getLogger(PropertiesLoginModule.class); 045 046 private Subject subject; 047 private CallbackHandler callbackHandler; 048 049 private boolean debug; 050 private boolean reload = false; 051 private static volatile PrincipalProperties users; 052 private static volatile PrincipalProperties groups; 053 private String user; 054 private final Set<Principal> principals = new HashSet<Principal>(); 055 private File baseDir; 056 private boolean loginSucceeded; 057 private boolean decrypt = true; 058 059 @Override 060 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { 061 this.subject = subject; 062 this.callbackHandler = callbackHandler; 063 loginSucceeded = false; 064 065 debug = "true".equalsIgnoreCase((String) options.get("debug")); 066 if (options.get("reload") != null) { 067 reload = "true".equalsIgnoreCase((String) options.get("reload")); 068 } 069 070 if (options.get("baseDir") != null) { 071 baseDir = new File((String) options.get("baseDir")); 072 } 073 074 setBaseDir(); 075 String usersFile = (String) options.get(USER_FILE) + ""; 076 File uf = baseDir != null ? new File(baseDir, usersFile) : new File(usersFile); 077 078 if (reload || users == null || uf.lastModified() > users.getReloadTime()) { 079 if (debug) { 080 LOG.debug("Reloading users from " + uf.getAbsolutePath()); 081 } 082 users = new PrincipalProperties("user", uf, LOG); 083 if( decrypt ) { 084 try { 085 EncryptionSupport.decrypt(users.getPrincipals()); 086 } catch(NoClassDefFoundError e) { 087 // this Happens whe jasypt is not on the classpath.. 088 decrypt = false; 089 LOG.info("jasypt is not on the classpath: password decryption disabled."); 090 } 091 } 092 } 093 094 String groupsFile = (String) options.get(GROUP_FILE) + ""; 095 File gf = baseDir != null ? new File(baseDir, groupsFile) : new File(groupsFile); 096 if (reload || groups == null || gf.lastModified() > groups.getReloadTime()) { 097 if (debug) { 098 LOG.debug("Reloading groups from " + gf.getAbsolutePath()); 099 } 100 groups = new PrincipalProperties("group", gf, LOG); 101 } 102 } 103 104 private void setBaseDir() { 105 if (baseDir == null) { 106 if (System.getProperty("java.security.auth.login.config") != null) { 107 baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile(); 108 if (debug) { 109 LOG.debug("Using basedir=" + baseDir.getAbsolutePath()); 110 } 111 } 112 } 113 } 114 115 @Override 116 public boolean login() throws LoginException { 117 Callback[] callbacks = new Callback[2]; 118 119 callbacks[0] = new NameCallback("Username: "); 120 callbacks[1] = new PasswordCallback("Password: ", false); 121 try { 122 callbackHandler.handle(callbacks); 123 } catch (IOException ioe) { 124 throw new LoginException(ioe.getMessage()); 125 } catch (UnsupportedCallbackException uce) { 126 throw new LoginException(uce.getMessage() + " not available to obtain information from user"); 127 } 128 user = ((NameCallback) callbacks[0]).getName(); 129 char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword(); 130 if (tmpPassword == null) { 131 tmpPassword = new char[0]; 132 } 133 if (user == null) { 134 throw new FailedLoginException("user name is null"); 135 } 136 String password = users.getProperty(user); 137 138 if (password == null) { 139 throw new FailedLoginException("User does exist"); 140 } 141 if (!password.equals(new String(tmpPassword))) { 142 throw new FailedLoginException("Password does not match"); 143 } 144 loginSucceeded = true; 145 146 if (debug) { 147 LOG.debug("login " + user); 148 } 149 return loginSucceeded; 150 } 151 152 @Override 153 public boolean commit() throws LoginException { 154 boolean result = loginSucceeded; 155 if (result) { 156 principals.add(new UserPrincipal(user)); 157 158 for (Map.Entry<String, String> entry : groups.entries()) { 159 String name = entry.getKey(); 160 String[] userList = entry.getValue().split(","); 161 for (int i = 0; i < userList.length; i++) { 162 if (user.equals(userList[i])) { 163 principals.add(new GroupPrincipal(name)); 164 break; 165 } 166 } 167 } 168 169 subject.getPrincipals().addAll(principals); 170 } 171 172 // will whack loginSucceeded 173 clear(); 174 175 if (debug) { 176 LOG.debug("commit, result: " + result); 177 } 178 return result; 179 } 180 181 @Override 182 public boolean abort() throws LoginException { 183 clear(); 184 185 if (debug) { 186 LOG.debug("abort"); 187 } 188 return true; 189 } 190 191 @Override 192 public boolean logout() throws LoginException { 193 subject.getPrincipals().removeAll(principals); 194 principals.clear(); 195 clear(); 196 if (debug) { 197 LOG.debug("logout"); 198 } 199 return true; 200 } 201 202 private void clear() { 203 user = null; 204 loginSucceeded = false; 205 } 206 207 /** 208 * For test-usage only. 209 */ 210 static void resetUsersAndGroupsCache() { 211 users = null; 212 groups = null; 213 } 214}