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