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 }