001: /*
002: jGuard is a security framework based on top of jaas (java authentication and authorization security).
003: it is written for web applications, to resolve simply, access control problems.
004: version $Name: $
005: http://sourceforge.net/projects/jguard/
006:
007: Copyright (C) 2004 Charles GAY
008:
009: This library is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: This library is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023:
024: jGuard project home page:
025: http://sourceforge.net/projects/jguard/
026:
027: */
028: package net.sf.jguard.core.authentication.configuration;
029:
030: import java.util.ArrayList;
031: import java.util.Arrays;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Map;
036:
037: import javax.security.auth.Subject;
038: import javax.security.auth.callback.CallbackHandler;
039: import javax.security.auth.login.AppConfigurationEntry;
040: import javax.security.auth.login.Configuration;
041: import javax.security.auth.login.LoginContext;
042: import javax.security.auth.login.LoginException;
043: import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
044: import javax.security.auth.spi.LoginModule;
045:
046: import org.apache.commons.logging.Log;
047: import org.apache.commons.logging.LogFactory;
048:
049: /**
050: * LoginContext only 'local' to this application.
051: * @author <a href="mailto:diabolo512@users.sourceforge.net">Charles Gay</a>
052: * @since 1.0
053: */
054: public class LocalLoginContext {
055: private static final Log logger = LogFactory
056: .getLog(LocalLoginContext.class);
057: private static final String OTHER = "other";
058: private List appEntriesList = null;
059: private CallbackHandler cbHandler = null;
060: private Subject subject = null;
061: private Map flags = null;
062: private boolean loginSucceed = true;
063: private boolean subjectNotProvided;
064: private List loginModules = null;
065:
066: /**
067: * constructor which mimics {@link LoginContext} constructor.
068: * @param name
069: * @param cbHandler
070: * @throws LoginException
071: */
072: public LocalLoginContext(String name, CallbackHandler cbHandler)
073: throws LoginException {
074: appEntriesList = getAppConfigurationEntry(Configuration
075: .getConfiguration(), name);
076: this .cbHandler = cbHandler;
077: subject = new Subject();
078: flags = new HashMap();
079: subjectNotProvided = true;
080: }
081:
082: /**
083: * constructor which mimics {@link LoginContext} constructor.
084: * @param name
085: * @param cbHandler
086: * @throws LoginException
087: */
088: public LocalLoginContext(String name, CallbackHandler cbHandler,
089: Configuration configuration) throws LoginException {
090: appEntriesList = getAppConfigurationEntry(configuration, name);
091: this .cbHandler = cbHandler;
092: subject = new Subject();
093: flags = new HashMap();
094: subjectNotProvided = true;
095: }
096:
097: /**
098: * constructor which mimics {@link LoginContext} constructor.
099: * @param name
100: * @param subject
101: * @param cbHandler
102: * @throws LoginException
103: */
104: public LocalLoginContext(String name, Subject subject,
105: CallbackHandler cbHandler) throws LoginException {
106: appEntriesList = getAppConfigurationEntry(Configuration
107: .getConfiguration(), name);
108: this .cbHandler = cbHandler;
109: this .subject = subject;
110: flags = new HashMap();
111: }
112:
113: /**
114: * constructor which permits to have for java SE 4 this constructor prsent only on java SE 5.
115: * @param name
116: * @param subject
117: * @param cbHandler
118: * @param configuration
119: * @throws LoginException
120: */
121: public LocalLoginContext(String name, Subject subject,
122: CallbackHandler cbHandler, Configuration configuration)
123: throws LoginException {
124: appEntriesList = getAppConfigurationEntry(configuration, name);
125: this .cbHandler = cbHandler;
126: this .subject = subject;
127: flags = new HashMap();
128: }
129:
130: private List getAppConfigurationEntry(Configuration config,
131: String name) {
132: AppConfigurationEntry[] appEntries = null;
133: appEntries = config.getAppConfigurationEntry(name);
134: if (appEntries == null) {
135: appEntries = config.getAppConfigurationEntry(OTHER);
136: }
137: return Arrays.asList(appEntries);
138: }
139:
140: /**
141: * perform the authentication in a <strong>local</strong> manner,
142: * i.e, not bound to the JVM's Configuration,
143: * and if successful, associate Principals and
144: * credentials with the Authenticated Subject.
145: * @throws LoginException
146: */
147: public void login() throws LoginException {
148:
149: loginModules = initializeLoginModules(appEntriesList, subject,
150: cbHandler);
151:
152: //login phase
153: Iterator itLoginModules = loginModules.iterator();
154: //first overall authentication exception
155: LoginException exception = null;
156: while (itLoginModules.hasNext()) {
157: LoginModule module = (LoginModule) itLoginModules.next();
158: LoginModuleControlFlag flag = (LoginModuleControlFlag) flags
159: .get(module);
160:
161: //loginModule Flag check
162: if ((!LoginModuleControlFlag.OPTIONAL.equals(flag))
163: && (!LoginModuleControlFlag.REQUIRED.equals(flag))
164: && (!LoginModuleControlFlag.REQUISITE.equals(flag))
165: && (!LoginModuleControlFlag.SUFFICIENT.equals(flag))) {
166:
167: logger.error(" loginModule=" + module.getClass()
168: + " has got an invalid flag=" + flag);
169: logger
170: .error(" this loginModule is skipped in the authentication process ");
171: continue;
172: }
173:
174: //login phase
175: try {
176: boolean loginModuleSucceed = module.login();
177: //loginModule should be ignored => we skip it and continue to the next one
178: //according to the Loginmodule javadoc
179: if (!loginModuleSucceed) {
180: logger.debug(" loginModule " + module.getClass()
181: + " in 'login' phase is ignored ");
182: continue;
183: }
184:
185: logger.debug(" loginModule " + module.getClass()
186: + " in 'login' phase succeed ");
187:
188: //login succeed
189: if (LoginModuleControlFlag.REQUIRED.equals(flag)) {
190: continue;
191: } else if (LoginModuleControlFlag.REQUISITE
192: .equals(flag)) {
193: continue;
194: } else if (LoginModuleControlFlag.SUFFICIENT
195: .equals(flag)) {
196: break;
197: } else if (LoginModuleControlFlag.OPTIONAL.equals(flag)) {
198: continue;
199: }
200: } catch (LoginException e) {
201: //we store only the first exception in the overall authentication process
202: if (exception == null) {
203: exception = e;
204: }
205:
206: logger.debug(" loginModule " + module.getClass()
207: + " in 'login' phase failed ");
208: //login fails
209: logger.info(" authentication fails " + e.getMessage());
210: if (LoginModuleControlFlag.REQUIRED.equals(flag)) {
211: loginSucceed = false;
212: continue;
213: } else if (LoginModuleControlFlag.REQUISITE
214: .equals(flag)) {
215: loginSucceed = false;
216: break;
217: } else if (LoginModuleControlFlag.SUFFICIENT
218: .equals(flag)) {
219: continue;
220: } else if (LoginModuleControlFlag.OPTIONAL.equals(flag)) {
221: continue;
222: }
223: }
224:
225: }
226: Iterator itLoginModules2 = loginModules.iterator();
227:
228: if (loginSucceed) {
229: //commit phase
230: while (itLoginModules2.hasNext()) {
231: LoginModule module = (LoginModule) itLoginModules2
232: .next();
233: try {
234: boolean moduleCommitSucceed = module.commit();
235: if (moduleCommitSucceed) {
236: logger.debug(" loginModule "
237: + module.getClass()
238: + " in 'commit' phase succeeed");
239: } else {
240: logger.debug(" loginModule "
241: + module.getClass()
242: + " in 'commit' phase is ignored ");
243: }
244: } catch (LoginException e) {
245: logger.debug(" loginModule " + module.getClass()
246: + " in 'commit' phase failed ");
247: abort(loginModules, e);
248: throw e;
249: }
250: }
251:
252: } else {
253: abort(loginModules, exception);
254: }
255:
256: }
257:
258: private void abort(List loginModules, LoginException exception)
259: throws LoginException {
260: Iterator itLoginModules = loginModules.iterator();
261: //abort phase
262: while (itLoginModules.hasNext()) {
263: LoginModule module = (LoginModule) itLoginModules.next();
264: try {
265: boolean moduleAbortSucceed = module.abort();
266: if (moduleAbortSucceed) {
267: logger.debug(" loginModule " + module.getClass()
268: + " in 'abort' phase succeeed");
269: } else {
270: logger.debug(" loginModule " + module.getClass()
271: + " in 'abort' phase is ignored ");
272: }
273: } catch (LoginException e) {
274: logger.debug(" loginModule " + module.getClass()
275: + " in 'abort' phase failed ");
276: logger.warn(e.getMessage());
277: throw exception;
278: }
279: }
280: //we throw the initial exception which causes abort phase
281: throw exception;
282: }
283:
284: private List initializeLoginModules(List appConfigurationEntries,
285: Subject subject, CallbackHandler cbHandler) {
286: Map sharedState = new HashMap();
287: List loginModules = new ArrayList();
288: Iterator itAppEntries = appConfigurationEntries.iterator();
289: //we initialize loginmodules
290: while (itAppEntries.hasNext()) {
291: AppConfigurationEntry entry = (AppConfigurationEntry) itAppEntries
292: .next();
293: LoginModuleControlFlag flag = entry.getControlFlag();
294: String loginModuleName = entry.getLoginModuleName();
295: Map options = entry.getOptions();
296: Class loginModuleClass = null;
297: LoginModule module = null;
298: //grab loginModule Class
299: try {
300: loginModuleClass = (Class) Thread.currentThread()
301: .getContextClassLoader().loadClass(
302: loginModuleName);
303: } catch (ClassNotFoundException e) {
304: logger.fatal(" loginModule Class " + loginModuleName
305: + " not found ");
306: throw new RuntimeException("loginModule "
307: + loginModuleName + " is not found "
308: + e.getMessage());
309: }
310: //instantiate it
311: try {
312: module = (LoginModule) loginModuleClass.newInstance();
313: } catch (InstantiationException e) {
314: logger.fatal(" loginModule Class " + loginModuleName
315: + " cannot be instantiated ");
316: throw new RuntimeException(e.getMessage());
317: } catch (IllegalAccessException e) {
318: logger.fatal(" loginModule Class " + loginModuleName
319: + " cannot be accessed ");
320: throw new RuntimeException(e.getMessage());
321: }
322:
323: //initialize it
324: module.initialize(subject, cbHandler, sharedState, options);
325: flags.put(module, flag);
326: loginModules.add(module);
327: }
328:
329: return loginModules;
330: }
331:
332: public void logout() throws LoginException {
333: LoginException exception = null;
334: Iterator itLoginModules = loginModules.iterator();
335: while (itLoginModules.hasNext()) {
336: LoginModule module = (LoginModule) itLoginModules.next();
337: try {
338: boolean moduleLogoutSucceed = module.logout();
339: if (moduleLogoutSucceed) {
340: logger.debug(" loginModule " + module.getClass()
341: + " in 'logout' phase succeeed");
342: } else {
343: logger.debug(" loginModule " + module.getClass()
344: + " in 'logout' phase is ignored ");
345: }
346: } catch (LoginException e) {
347: logger.debug(" loginModule " + module.getClass()
348: + " in 'logout' phase failed ");
349: logger.warn(e.getMessage());
350: if (exception == null) {
351: exception = e;
352: }
353: }
354: }
355:
356: //we throw the first exception raised by loginModules
357: //but we try to logout before all the loginModules
358: if (exception != null) {
359: throw exception;
360: }
361: }
362:
363: public Subject getSubject() {
364: if (!loginSucceed && subjectNotProvided) {
365: return null;
366: }
367: return subject;
368: }
369: }
|