001 /**
002 *
003 * Copyright 2003-2004 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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
018 package org.apache.geronimo.security.realm.providers;
019
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import org.apache.geronimo.common.GeronimoSecurityException;
023 import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
024 import org.apache.geronimo.system.serverinfo.ServerInfo;
025
026 import javax.security.auth.Subject;
027 import javax.security.auth.callback.Callback;
028 import javax.security.auth.callback.CallbackHandler;
029 import javax.security.auth.callback.NameCallback;
030 import javax.security.auth.callback.PasswordCallback;
031 import javax.security.auth.callback.UnsupportedCallbackException;
032 import javax.security.auth.login.FailedLoginException;
033 import javax.security.auth.login.LoginException;
034 import javax.security.auth.spi.LoginModule;
035 import java.io.IOException;
036 import java.io.InputStream;
037 import java.net.URI;
038 import java.util.Enumeration;
039 import java.util.HashMap;
040 import java.util.HashSet;
041 import java.util.Iterator;
042 import java.util.Map;
043 import java.util.Properties;
044 import java.util.Set;
045
046
047 /**
048 * A LoginModule that reads a list of users and group from files on disk. The
049 * files should be formatted using standard Java properties syntax. Expects
050 * to be run by a GenericSecurityRealm (doesn't work on its own).
051 *
052 * @version $Rev: 406493 $ $Date: 2006-05-14 18:14:11 -0700 (Sun, 14 May 2006) $
053 */
054 public class PropertiesFileLoginModule implements LoginModule {
055 public final static String USERS_URI = "usersURI";
056 public final static String GROUPS_URI = "groupsURI";
057 private static Log log = LogFactory.getLog(PropertiesFileLoginModule.class);
058 final Properties users = new Properties();
059 final Map groups = new HashMap();
060
061 Subject subject;
062 CallbackHandler handler;
063 String username;
064 String password;
065
066 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
067 this.subject = subject;
068 this.handler = callbackHandler;
069 try {
070 ServerInfo serverInfo = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION);
071 final String users = (String)options.get(USERS_URI);
072 final String groups = (String)options.get(GROUPS_URI);
073 if(users == null || groups == null) {
074 throw new IllegalArgumentException("Both "+USERS_URI+" and "+GROUPS_URI+" must be provided!");
075 }
076 URI usersURI = new URI(users);
077 URI groupsURI = new URI(groups);
078 loadProperties(serverInfo, usersURI, groupsURI);
079 } catch (Exception e) {
080 log.error("Initialization failed", e);
081 throw new IllegalArgumentException("Unable to configure properties file login module: "+e.getMessage());
082 }
083 }
084
085 public void loadProperties(ServerInfo serverInfo, URI userURI, URI groupURI) throws GeronimoSecurityException {
086 try {
087 URI userFile = serverInfo.resolveServer(userURI);
088 URI groupFile = serverInfo.resolveServer(groupURI);
089 InputStream stream = userFile.toURL().openStream();
090 users.load(stream);
091 stream.close();
092
093 Properties temp = new Properties();
094 stream = groupFile.toURL().openStream();
095 temp.load(stream);
096 stream.close();
097
098 Enumeration e = temp.keys();
099 while (e.hasMoreElements()) {
100 String groupName = (String) e.nextElement();
101 String[] userList = ((String) temp.get(groupName)).split(",");
102
103 Set userset = (Set) groups.get(groupName);
104 if (userset == null) {
105 userset = new HashSet();
106 groups.put(groupName, userset);
107 }
108
109 for (int i = 0; i < userList.length; i++) {
110 userset.add(userList[i]);
111 }
112 }
113
114 } catch (Exception e) {
115 log.error("Properties File Login Module - data load failed", e);
116 throw new GeronimoSecurityException(e);
117 }
118 }
119
120
121 public boolean login() throws LoginException {
122 Callback[] callbacks = new Callback[2];
123
124 callbacks[0] = new NameCallback("User name");
125 callbacks[1] = new PasswordCallback("Password", false);
126 try {
127 handler.handle(callbacks);
128 } catch (IOException ioe) {
129 throw (LoginException) new LoginException().initCause(ioe);
130 } catch (UnsupportedCallbackException uce) {
131 throw (LoginException) new LoginException().initCause(uce);
132 }
133 assert callbacks.length == 2;
134 username = ((NameCallback) callbacks[0]).getName();
135 if(username == null || username.equals("")) {
136 return false;
137 }
138 String realPassword = users.getProperty(username);
139 char[] entered = ((PasswordCallback) callbacks[1]).getPassword();
140 password = entered == null ? null : new String(entered);
141 boolean result = (realPassword == null && password == null) ||
142 (realPassword != null && password != null && realPassword.equals(password));
143 if(!result) {
144 throw new FailedLoginException();
145 }
146 return true;
147 }
148
149 public boolean commit() throws LoginException {
150 Set principals = subject.getPrincipals();
151
152 principals.add(new GeronimoUserPrincipal(username));
153
154 Iterator e = groups.keySet().iterator();
155 while (e.hasNext()) {
156 String groupName = (String) e.next();
157 Set users = (Set) groups.get(groupName);
158 Iterator iter = users.iterator();
159 while (iter.hasNext()) {
160 String user = (String) iter.next();
161 if (username.equals(user)) {
162 principals.add(new GeronimoGroupPrincipal(groupName));
163 break;
164 }
165 }
166 }
167
168 return true;
169 }
170
171 public boolean abort() throws LoginException {
172 username = null;
173 password = null;
174
175 return true;
176 }
177
178 public boolean logout() throws LoginException {
179 username = null;
180 password = null;
181 //todo: should remove principals added by commit
182 return true;
183 }
184
185 /**
186 * Gets the names of all principal classes that may be populated into
187 * a Subject.
188 */
189 public String[] getPrincipalClassNames() {
190 return new String[]{GeronimoUserPrincipal.class.getName(), GeronimoGroupPrincipal.class.getName()};
191 }
192
193 /**
194 * Gets a list of all the principals of a particular type (identified by
195 * the principal class). These are available for manual role mapping.
196 */
197 public String[] getPrincipalsOfClass(String className) {
198 Set s;
199 if(className.equals(GeronimoGroupPrincipal.class.getName())) {
200 s = groups.keySet();
201 } else if(className.equals(GeronimoUserPrincipal.class.getName())) {
202 s = users.keySet();
203 } else {
204 throw new IllegalArgumentException("No such principal class "+className);
205 }
206 return (String[]) s.toArray(new String[s.size()]);
207 }
208 }