View Javadoc

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         //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 }