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
018 package org.apache.geronimo.security.realm.providers;
019
020 import java.io.IOException;
021 import java.io.InputStream;
022 import java.net.URI;
023 import java.security.cert.X509Certificate;
024 import java.util.Enumeration;
025 import java.util.HashMap;
026 import java.util.HashSet;
027 import java.util.Iterator;
028 import java.util.Map;
029 import java.util.Properties;
030 import java.util.Set;
031 import java.util.Collection;
032 import javax.security.auth.Subject;
033 import javax.security.auth.callback.Callback;
034 import javax.security.auth.callback.CallbackHandler;
035 import javax.security.auth.callback.UnsupportedCallbackException;
036 import javax.security.auth.login.LoginException;
037 import javax.security.auth.login.FailedLoginException;
038 import javax.security.auth.spi.LoginModule;
039 import javax.security.auth.x500.X500Principal;
040
041 import org.apache.commons.logging.Log;
042 import org.apache.commons.logging.LogFactory;
043 import org.apache.geronimo.common.GeronimoSecurityException;
044 import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
045 import org.apache.geronimo.system.serverinfo.ServerInfo;
046
047
048 /**
049 * An example LoginModule that reads a list of credentials and group from a file on disk.
050 * Authentication is provided by the SSL layer supplying the client certificate.
051 * All we check is that it is present. The
052 * file should be formatted using standard Java properties syntax. Expects
053 * to be run by a GenericSecurityRealm (doesn't work on its own).
054 *
055 * The usersURI property file should have lines of the form token=certificatename
056 * where certificate name is X509Certificate.getSubjectX500Principal().getName()
057 *
058 * The groupsURI property file should have lines of the form group=token1,token2,...
059 * where the tokens were associated to the certificate names in the usersURI properties file.
060 *
061 * This login module checks security credentials so the lifecycle methods must return true to indicate success
062 * or throw LoginException to indicate failure.
063 *
064 * @version $Rev: 565912 $ $Date: 2007-08-14 17:03:11 -0400 (Tue, 14 Aug 2007) $
065 */
066 public class CertificatePropertiesFileLoginModule implements LoginModule {
067 private static Log log = LogFactory.getLog(CertificatePropertiesFileLoginModule.class);
068 public final static String USERS_URI = "usersURI";
069 public final static String GROUPS_URI = "groupsURI";
070
071 private final Map users = new HashMap();
072 final Map groups = new HashMap();
073
074 private Subject subject;
075 private CallbackHandler handler;
076 private X500Principal principal;
077
078 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
079 this.subject = subject;
080 this.handler = callbackHandler;
081 try {
082 ServerInfo serverInfo = (ServerInfo) options.get(JaasLoginModuleUse.SERVERINFO_LM_OPTION);
083 URI usersURI = new URI((String)options.get(USERS_URI));
084 URI groupsURI = new URI((String)options.get(GROUPS_URI));
085 loadProperties(serverInfo, usersURI, groupsURI);
086 } catch (Exception e) {
087 log.error(e);
088 throw new IllegalArgumentException("Unable to configure properties file login module: "+e.getMessage(), e);
089 }
090 }
091
092 public void loadProperties(ServerInfo serverInfo, URI usersURI, URI groupURI) throws GeronimoSecurityException {
093 try {
094 URI userFile = serverInfo.resolve(usersURI);
095 URI groupFile = serverInfo.resolve(groupURI);
096 InputStream stream = userFile.toURL().openStream();
097 Properties tmpUsers = new Properties();
098 tmpUsers.load(stream);
099 stream.close();
100
101 for (Iterator iterator = tmpUsers.entrySet().iterator(); iterator.hasNext();) {
102 Map.Entry entry = (Map.Entry) iterator.next();
103 users.put(entry.getValue(), entry.getKey());
104 }
105
106 Properties temp = new Properties();
107 stream = groupFile.toURL().openStream();
108 temp.load(stream);
109 stream.close();
110
111 Enumeration e = temp.keys();
112 while (e.hasMoreElements()) {
113 String groupName = (String) e.nextElement();
114 String[] userList = ((String) temp.get(groupName)).split(",");
115
116 Set userset = (Set) groups.get(groupName);
117 if (userset == null) {
118 userset = new HashSet();
119 groups.put(groupName, userset);
120 }
121
122 for (int i = 0; i < userList.length; i++) {
123 String userName = userList[i];
124 userset.add(userName);
125 }
126 }
127
128 } catch (Exception e) {
129 log.error("Properties File Login Module - data load failed", e);
130 throw new GeronimoSecurityException(e);
131 }
132 }
133
134
135 public boolean login() throws LoginException {
136 Callback[] callbacks = new Callback[1];
137
138 callbacks[0] = new CertificateCallback();
139 try {
140 handler.handle(callbacks);
141 } catch (IOException ioe) {
142 throw (LoginException) new LoginException().initCause(ioe);
143 } catch (UnsupportedCallbackException uce) {
144 throw (LoginException) new LoginException().initCause(uce);
145 }
146 assert callbacks.length == 1;
147 X509Certificate certificate = ((CertificateCallback)callbacks[0]).getCertificate();
148 if (certificate == null) {
149 throw new FailedLoginException();
150 }
151 principal = certificate.getSubjectX500Principal();
152
153 if(!users.containsKey(principal.getName())) {
154 throw new FailedLoginException();
155 }
156 return true;
157 }
158
159 public boolean commit() throws LoginException {
160 Set principals = subject.getPrincipals();
161
162 principals.add(principal);
163 String userName = (String) users.get(principal.getName());
164 principals.add(new GeronimoUserPrincipal(userName));
165
166 Iterator e = groups.keySet().iterator();
167 while (e.hasNext()) {
168 String groupName = (String) e.next();
169 Set users = (Set) groups.get(groupName);
170 Iterator iter = users.iterator();
171 while (iter.hasNext()) {
172 String user = (String) iter.next();
173 if (userName.equals(user)) {
174 principals.add(new GeronimoGroupPrincipal(groupName));
175 break;
176 }
177 }
178 }
179
180 return true;
181 }
182
183 public boolean abort() throws LoginException {
184 principal = null;
185
186 return true;
187 }
188
189 public boolean logout() throws LoginException {
190 principal = null;
191 //todo: should remove principals added by commit
192 return true;
193 }
194
195 }