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 }