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 */ 017 package org.apache.activemq.jaas; 018 019 import java.io.File; 020 import java.io.IOException; 021 import java.security.Principal; 022 import java.util.Enumeration; 023 import java.util.HashSet; 024 import java.util.Map; 025 import java.util.Properties; 026 import java.util.Set; 027 028 import javax.security.auth.Subject; 029 import javax.security.auth.callback.Callback; 030 import javax.security.auth.callback.CallbackHandler; 031 import javax.security.auth.callback.NameCallback; 032 import javax.security.auth.callback.PasswordCallback; 033 import javax.security.auth.callback.UnsupportedCallbackException; 034 import javax.security.auth.login.FailedLoginException; 035 import javax.security.auth.login.LoginException; 036 import javax.security.auth.spi.LoginModule; 037 038 import org.slf4j.Logger; 039 import org.slf4j.LoggerFactory; 040 041 public class PropertiesLoginModule implements LoginModule { 042 043 private static final String USER_FILE = "org.apache.activemq.jaas.properties.user"; 044 private static final String GROUP_FILE = "org.apache.activemq.jaas.properties.group"; 045 046 private static final Logger LOG = LoggerFactory.getLogger(PropertiesLoginModule.class); 047 048 private Subject subject; 049 private CallbackHandler callbackHandler; 050 051 private boolean debug; 052 private boolean reload = false; 053 private static String usersFile; 054 private static String groupsFile; 055 private static Properties users; 056 private static Properties groups; 057 private static long usersReloadTime = 0; 058 private static long groupsReloadTime = 0; 059 private String user; 060 private Set<Principal> principals = new HashSet<Principal>(); 061 private File baseDir; 062 private boolean loginSucceeded; 063 064 @Override 065 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { 066 this.subject = subject; 067 this.callbackHandler = callbackHandler; 068 loginSucceeded = false; 069 070 debug = "true".equalsIgnoreCase((String)options.get("debug")); 071 if (options.get("reload") != null) { 072 reload = "true".equalsIgnoreCase((String)options.get("reload")); 073 } 074 075 if (options.get("baseDir") != null) { 076 baseDir = new File((String)options.get("baseDir")); 077 } 078 079 setBaseDir(); 080 usersFile = (String) options.get(USER_FILE) + ""; 081 File uf = baseDir != null ? new File(baseDir, usersFile) : new File(usersFile); 082 083 if (reload || users == null || uf.lastModified() > usersReloadTime) { 084 if (debug) { 085 LOG.debug("Reloading users from " + uf.getAbsolutePath()); 086 } 087 try { 088 users = new Properties(); 089 java.io.FileInputStream in = new java.io.FileInputStream(uf); 090 users.load(in); 091 in.close(); 092 usersReloadTime = System.currentTimeMillis(); 093 } catch (IOException ioe) { 094 LOG.warn("Unable to load user properties file " + uf); 095 } 096 } 097 098 groupsFile = (String) options.get(GROUP_FILE) + ""; 099 File gf = baseDir != null ? new File(baseDir, groupsFile) : new File(groupsFile); 100 if (reload || groups == null || gf.lastModified() > groupsReloadTime) { 101 if (debug) { 102 LOG.debug("Reloading groups from " + gf.getAbsolutePath()); 103 } 104 try { 105 groups = new Properties(); 106 java.io.FileInputStream in = new java.io.FileInputStream(gf); 107 groups.load(in); 108 in.close(); 109 groupsReloadTime = System.currentTimeMillis(); 110 } catch (IOException ioe) { 111 LOG.warn("Unable to load group properties file " + gf); 112 } 113 } 114 } 115 116 private void setBaseDir() { 117 if (baseDir == null) { 118 if (System.getProperty("java.security.auth.login.config") != null) { 119 baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile(); 120 if (debug) { 121 LOG.debug("Using basedir=" + baseDir.getAbsolutePath()); 122 } 123 } 124 } 125 } 126 127 @Override 128 public boolean login() throws LoginException { 129 Callback[] callbacks = new Callback[2]; 130 131 callbacks[0] = new NameCallback("Username: "); 132 callbacks[1] = new PasswordCallback("Password: ", false); 133 try { 134 callbackHandler.handle(callbacks); 135 } catch (IOException ioe) { 136 throw new LoginException(ioe.getMessage()); 137 } catch (UnsupportedCallbackException uce) { 138 throw new LoginException(uce.getMessage() + " not available to obtain information from user"); 139 } 140 user = ((NameCallback)callbacks[0]).getName(); 141 char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword(); 142 if (tmpPassword == null) { 143 tmpPassword = new char[0]; 144 } 145 if (user == null) { 146 throw new FailedLoginException("user name is null"); 147 } 148 String password = users.getProperty(user); 149 150 if (password == null) { 151 throw new FailedLoginException("User does exist"); 152 } 153 if (!password.equals(new String(tmpPassword))) { 154 throw new FailedLoginException("Password does not match"); 155 } 156 loginSucceeded = true; 157 158 if (debug) { 159 LOG.debug("login " + user); 160 } 161 return loginSucceeded; 162 } 163 164 @Override 165 public boolean commit() throws LoginException { 166 boolean result = loginSucceeded; 167 if (result) { 168 principals.add(new UserPrincipal(user)); 169 170 for (Enumeration<?> enumeration = groups.keys(); enumeration.hasMoreElements();) { 171 String name = (String)enumeration.nextElement(); 172 String[] userList = ((String)groups.getProperty(name) + "").split(","); 173 for (int i = 0; i < userList.length; i++) { 174 if (user.equals(userList[i])) { 175 principals.add(new GroupPrincipal(name)); 176 break; 177 } 178 } 179 } 180 181 subject.getPrincipals().addAll(principals); 182 } 183 184 // will whack loginSucceeded 185 clear(); 186 187 if (debug) { 188 LOG.debug("commit, result: " + result); 189 } 190 return result; 191 } 192 193 @Override 194 public boolean abort() throws LoginException { 195 clear(); 196 197 if (debug) { 198 LOG.debug("abort"); 199 } 200 return true; 201 } 202 203 @Override 204 public boolean logout() throws LoginException { 205 subject.getPrincipals().removeAll(principals); 206 principals.clear(); 207 clear(); 208 if (debug) { 209 LOG.debug("logout"); 210 } 211 return true; 212 } 213 214 private void clear() { 215 user = null; 216 loginSucceeded = false; 217 } 218 }