KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > security > realm > providers > SQLLoginModule


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. 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 java.io.IOException JavaDoc;
21 import java.security.MessageDigest JavaDoc;
22 import java.security.NoSuchAlgorithmException JavaDoc;
23 import java.sql.Connection JavaDoc;
24 import java.sql.Driver JavaDoc;
25 import java.sql.PreparedStatement JavaDoc;
26 import java.sql.ResultSet JavaDoc;
27 import java.sql.SQLException JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Properties JavaDoc;
32 import java.util.Set JavaDoc;
33 import javax.security.auth.Subject JavaDoc;
34 import javax.security.auth.callback.Callback JavaDoc;
35 import javax.security.auth.callback.CallbackHandler JavaDoc;
36 import javax.security.auth.callback.NameCallback JavaDoc;
37 import javax.security.auth.callback.PasswordCallback JavaDoc;
38 import javax.security.auth.callback.UnsupportedCallbackException JavaDoc;
39 import javax.security.auth.login.FailedLoginException JavaDoc;
40 import javax.security.auth.login.LoginException JavaDoc;
41 import javax.security.auth.spi.LoginModule JavaDoc;
42 import javax.sql.DataSource JavaDoc;
43
44 import org.apache.commons.logging.Log;
45 import org.apache.commons.logging.LogFactory;
46 import org.apache.geronimo.gbean.AbstractName;
47 import org.apache.geronimo.gbean.AbstractNameQuery;
48 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
49 import org.apache.geronimo.kernel.GBeanNotFoundException;
50 import org.apache.geronimo.kernel.Kernel;
51 import org.apache.geronimo.kernel.KernelRegistry;
52 import org.apache.geronimo.management.geronimo.JCAManagedConnectionFactory;
53 import org.apache.geronimo.security.jaas.JaasLoginModuleUse;
54 import org.apache.geronimo.util.encoders.HexTranslator;
55
56
57 /**
58  * A login module that loads security information from a SQL database. Expects
59  * to be run by a GenericSecurityRealm (doesn't work on its own).
60  * <p>
61  * This requires database connectivity information (either 1: a dataSourceName and
62  * optional dataSourceApplication or 2: a JDBC driver, URL, username, and password)
63  * and 2 SQL queries.
64  * <p>
65  * The userSelect query should return 2 values, the username and the password in
66  * that order. It should include one PreparedStatement parameter (a ?) which
67  * will be filled in with the username. In other words, the query should look
68  * like: <tt>SELECT user, password FROM users WHERE username=?</tt>
69  * <p>
70  * The groupSelect query should return 2 values, the username and the group name in
71  * that order (but it may return multiple rows, one per group). It should include
72  * one PreparedStatement parameter (a ?) which will be filled in with the username.
73  * In other words, the query should look like:
74  * <tt>SELECT user, role FROM user_roles WHERE username=?</tt>
75  *
76  * @version $Rev: 478545 $ $Date: 2006-11-23 07:20:40 -0500 (Thu, 23 Nov 2006) $
77  */

78 public class SQLLoginModule implements LoginModule JavaDoc {
79     private static Log log = LogFactory.getLog(SQLLoginModule.class);
80     public final static String JavaDoc USER_SELECT = "userSelect";
81     public final static String JavaDoc GROUP_SELECT = "groupSelect";
82     public final static String JavaDoc CONNECTION_URL = "jdbcURL";
83     public final static String JavaDoc USER = "jdbcUser";
84     public final static String JavaDoc PASSWORD = "jdbcPassword";
85     public final static String JavaDoc DRIVER = "jdbcDriver";
86     public final static String JavaDoc DATABASE_POOL_NAME = "dataSourceName";
87     public final static String JavaDoc DATABASE_POOL_APP_NAME = "dataSourceApplication";
88     public final static String JavaDoc DIGEST = "digest";
89     private String JavaDoc connectionURL;
90     private Properties JavaDoc properties;
91     private Driver JavaDoc driver;
92     private JCAManagedConnectionFactory factory;
93     private String JavaDoc userSelect;
94     private String JavaDoc groupSelect;
95     private String JavaDoc digest;
96
97     private Subject JavaDoc subject;
98     private CallbackHandler JavaDoc handler;
99     private String JavaDoc cbUsername;
100     private String JavaDoc cbPassword;
101     private final Set JavaDoc groups = new HashSet JavaDoc();
102
103     public void initialize(Subject JavaDoc subject, CallbackHandler JavaDoc callbackHandler, Map JavaDoc sharedState, Map JavaDoc options) {
104         this.subject = subject;
105         this.handler = callbackHandler;
106         userSelect = (String JavaDoc) options.get(USER_SELECT);
107         groupSelect = (String JavaDoc) options.get(GROUP_SELECT);
108
109         digest = (String JavaDoc) options.get(DIGEST);
110         if(digest != null && !digest.equals("")) {
111             // Check if the digest algorithm is available
112
try {
113                 MessageDigest.getInstance(digest);
114             } catch(NoSuchAlgorithmException JavaDoc e) {
115                 log.error("Initialization failed. Digest algorithm "+digest+" is not available.", e);
116                 throw new IllegalArgumentException JavaDoc("Unable to configure SQL login module: "+e.getMessage());
117             }
118         }
119
120         String JavaDoc dataSourceName = (String JavaDoc) options.get(DATABASE_POOL_NAME);
121         if(dataSourceName != null) {
122             dataSourceName = dataSourceName.trim();
123             String JavaDoc dataSourceAppName = (String JavaDoc) options.get(DATABASE_POOL_APP_NAME);
124             if(dataSourceAppName == null || dataSourceAppName.trim().equals("")) {
125                 dataSourceAppName = "null";
126             } else {
127                 dataSourceAppName = dataSourceAppName.trim();
128             }
129             String JavaDoc kernelName = (String JavaDoc) options.get(JaasLoginModuleUse.KERNEL_NAME_LM_OPTION);
130             Kernel kernel = KernelRegistry.getKernel(kernelName);
131             Set JavaDoc set = kernel.listGBeans(new AbstractNameQuery(JCAManagedConnectionFactory.class.getName()));
132             JCAManagedConnectionFactory factory;
133             for (Iterator JavaDoc it = set.iterator(); it.hasNext();) {
134                 AbstractName name = (AbstractName) it.next();
135                 if(name.getName().get(NameFactory.J2EE_APPLICATION).equals(dataSourceAppName) &&
136                     name.getName().get(NameFactory.J2EE_NAME).equals(dataSourceName)) {
137                     try {
138                         factory = (JCAManagedConnectionFactory) kernel.getGBean(name);
139                         String JavaDoc type = factory.getConnectionFactoryInterface();
140                         if(type.equals(DataSource JavaDoc.class.getName())) {
141                             this.factory = factory;
142                             break;
143                         }
144                     } catch (GBeanNotFoundException e) {
145                         // ignore... GBean was unregistered
146
}
147                 }
148             }
149         } else {
150             connectionURL = (String JavaDoc) options.get(CONNECTION_URL);
151             properties = new Properties JavaDoc();
152             if(options.get(USER) != null) {
153                 properties.put("user", options.get(USER));
154             }
155             if(options.get(PASSWORD) != null) {
156                 properties.put("password", options.get(PASSWORD));
157             }
158             ClassLoader JavaDoc cl = (ClassLoader JavaDoc) options.get(JaasLoginModuleUse.CLASSLOADER_LM_OPTION);
159             try {
160                 driver = (Driver JavaDoc) cl.loadClass((String JavaDoc) options.get(DRIVER)).newInstance();
161             } catch (ClassNotFoundException JavaDoc e) {
162                 throw new IllegalArgumentException JavaDoc("Driver class " + options.get(DRIVER) + " is not available. Perhaps you need to add it as a dependency in your deployment plan?");
163             } catch (Exception JavaDoc e) {
164                 throw new IllegalArgumentException JavaDoc("Unable to load, instantiate, register driver " + options.get(DRIVER) + ": " + e.getMessage());
165             }
166         }
167     }
168
169     public boolean login() throws LoginException JavaDoc {
170         Callback JavaDoc[] callbacks = new Callback JavaDoc[2];
171
172         callbacks[0] = new NameCallback JavaDoc("User name");
173         callbacks[1] = new PasswordCallback JavaDoc("Password", false);
174         try {
175             handler.handle(callbacks);
176         } catch (IOException JavaDoc ioe) {
177             throw (LoginException JavaDoc) new LoginException JavaDoc().initCause(ioe);
178         } catch (UnsupportedCallbackException JavaDoc uce) {
179             throw (LoginException JavaDoc) new LoginException JavaDoc().initCause(uce);
180         }
181         assert callbacks.length == 2;
182         cbUsername = ((NameCallback JavaDoc) callbacks[0]).getName();
183         if (cbUsername == null || cbUsername.equals("")) {
184             return false;
185         }
186         char[] provided = ((PasswordCallback JavaDoc) callbacks[1]).getPassword();
187         cbPassword = provided == null ? null : new String JavaDoc(provided);
188
189         boolean found = false;
190         try {
191             Connection JavaDoc conn;
192             if(factory != null) {
193                 DataSource JavaDoc ds = (DataSource JavaDoc) factory.getConnectionFactory();
194                 conn = ds.getConnection();
195             } else {
196                 conn = driver.connect(connectionURL, properties);
197             }
198
199             try {
200                 PreparedStatement JavaDoc statement = conn.prepareStatement(userSelect);
201                 try {
202                     int count = countParameters(userSelect);
203                     for(int i=0; i<count; i++) {
204                         statement.setObject(i+1, cbUsername);
205                     }
206                     ResultSet JavaDoc result = statement.executeQuery();
207
208                     try {
209                         while (result.next()) {
210                             String JavaDoc userName = result.getString(1);
211                             String JavaDoc userPassword = result.getString(2);
212
213                             if (cbUsername.equals(userName)) {
214                                 found = (cbPassword == null && userPassword == null) ||
215                                         (cbPassword != null && userPassword != null && checkPassword(userPassword, cbPassword));
216                                 break;
217                             }
218                         }
219                     } finally {
220                         result.close();
221                     }
222                 } finally {
223                     statement.close();
224                 }
225
226                 if (!found) {
227                     throw new FailedLoginException JavaDoc();
228                 }
229
230                 statement = conn.prepareStatement(groupSelect);
231                 try {
232                     int count = countParameters(groupSelect);
233                     for(int i=0; i<count; i++) {
234                         statement.setObject(i+1, cbUsername);
235                     }
236                     ResultSet JavaDoc result = statement.executeQuery();
237
238                     try {
239                         while (result.next()) {
240                             String JavaDoc userName = result.getString(1);
241                             String JavaDoc groupName = result.getString(2);
242
243                             if (cbUsername.equals(userName)) {
244                                 groups.add(new GeronimoGroupPrincipal(groupName));
245                             }
246                         }
247                     } finally {
248                         result.close();
249                     }
250                 } finally {
251                     statement.close();
252                 }
253             } finally {
254                 conn.close();
255             }
256         } catch (SQLException JavaDoc sqle) {
257             throw (LoginException JavaDoc) new LoginException JavaDoc("SQL error").initCause(sqle);
258         }
259
260         return true;
261     }
262
263     public boolean commit() throws LoginException JavaDoc {
264         Set JavaDoc principals = subject.getPrincipals();
265         principals.add(new GeronimoUserPrincipal(cbUsername));
266         Iterator JavaDoc iter = groups.iterator();
267         while (iter.hasNext()) {
268             principals.add(iter.next());
269         }
270
271         return true;
272     }
273
274     public boolean abort() throws LoginException JavaDoc {
275         cbUsername = null;
276         cbPassword = null;
277
278         return true;
279     }
280
281     public boolean logout() throws LoginException JavaDoc {
282         cbUsername = null;
283         cbPassword = null;
284         //todo: should remove principals put in by commit
285
return true;
286     }
287
288     private static int countParameters(String JavaDoc sql) {
289         int count = 0;
290         int pos = -1;
291         while((pos = sql.indexOf('?', pos+1)) != -1) {
292             ++count;
293         }
294         return count;
295     }
296
297     /**
298      * This method checks if the provided password is correct. The original password may have been digested.
299      * @param real Original password in digested form if applicable
300      * @param provided User provided password in clear text
301      * @return true If the password is correct
302      */

303     private boolean checkPassword(String JavaDoc real, String JavaDoc provided){
304         if(digest == null || digest.equals("")) {
305             // No digest algorithm is used
306
return real.equals(provided);
307         }
308         try {
309             // Digest the user provided password
310
MessageDigest JavaDoc md = MessageDigest.getInstance(digest);
311             byte[] data = md.digest(provided.getBytes());
312             // Convert bytes to hex digits
313
byte[] hexData = new byte[data.length * 2];
314             HexTranslator ht = new HexTranslator();
315             ht.encode(data, 0, data.length, hexData, 0);
316             // Compare the digested provided password with the actual one
317
return real.equalsIgnoreCase(new String JavaDoc(hexData));
318         } catch (NoSuchAlgorithmException JavaDoc e) {
319             // Should not occur. Availability of algorithm has been checked at initialization
320
log.error("Should not occur. Availability of algorithm has been checked at initialization.", e);
321         }
322         return false;
323     }
324 }
325
Popular Tags