1 /**
2 *
3 * Copyright 2003-2004 The Apache Software Foundation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.security.realm.providers;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.apache.geronimo.common.GeronimoSecurityException;
23 import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
24 import org.apache.geronimo.system.serverinfo.ServerInfo;
25
26 import javax.security.auth.Subject;
27 import javax.security.auth.callback.Callback;
28 import javax.security.auth.callback.CallbackHandler;
29 import javax.security.auth.callback.NameCallback;
30 import javax.security.auth.callback.PasswordCallback;
31 import javax.security.auth.callback.UnsupportedCallbackException;
32 import javax.security.auth.login.FailedLoginException;
33 import javax.security.auth.login.LoginException;
34 import javax.security.auth.spi.LoginModule;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.net.URI;
38 import java.util.Enumeration;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.Iterator;
42 import java.util.Map;
43 import java.util.Properties;
44 import java.util.Set;
45
46
47 /**
48 * A LoginModule that reads a list of users and group from files on disk. The
49 * files should be formatted using standard Java properties syntax. Expects
50 * to be run by a GenericSecurityRealm (doesn't work on its own).
51 *
52 * @version $Rev: 406493 $ $Date: 2006-05-14 18:14:11 -0700 (Sun, 14 May 2006) $
53 */
54 public class PropertiesFileLoginModule implements LoginModule {
55 public final static String USERS_URI = "usersURI";
56 public final static String GROUPS_URI = "groupsURI";
57 private static Log log = LogFactory.getLog(PropertiesFileLoginModule.class);
58 final Properties users = new Properties();
59 final Map groups = new HashMap();
60
61 Subject subject;
62 CallbackHandler handler;
63 String username;
64 String password;
65
66 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
67 this.subject = subject;
68 this.handler = callbackHandler;
69 try {
70 ServerInfo serverInfo = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION);
71 final String users = (String)options.get(USERS_URI);
72 final String groups = (String)options.get(GROUPS_URI);
73 if(users == null || groups == null) {
74 throw new IllegalArgumentException("Both "+USERS_URI+" and "+GROUPS_URI+" must be provided!");
75 }
76 URI usersURI = new URI(users);
77 URI groupsURI = new URI(groups);
78 loadProperties(serverInfo, usersURI, groupsURI);
79 } catch (Exception e) {
80 log.error("Initialization failed", e);
81 throw new IllegalArgumentException("Unable to configure properties file login module: "+e.getMessage());
82 }
83 }
84
85 public void loadProperties(ServerInfo serverInfo, URI userURI, URI groupURI) throws GeronimoSecurityException {
86 try {
87 URI userFile = serverInfo.resolveServer(userURI);
88 URI groupFile = serverInfo.resolveServer(groupURI);
89 InputStream stream = userFile.toURL().openStream();
90 users.load(stream);
91 stream.close();
92
93 Properties temp = new Properties();
94 stream = groupFile.toURL().openStream();
95 temp.load(stream);
96 stream.close();
97
98 Enumeration e = temp.keys();
99 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
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 }