001/* LoginContext.java
002   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.security.auth.login;
040
041import gnu.java.security.action.GetSecurityPropertyAction;
042
043import java.security.AccessController;
044
045import java.util.HashMap;
046import java.util.Map;
047
048import javax.security.auth.Subject;
049import javax.security.auth.callback.CallbackHandler;
050import javax.security.auth.spi.LoginModule;
051
052public class LoginContext
053{
054
055  private static final String OTHER = "other";
056
057  private final String name;
058  private final CallbackHandler cbHandler;
059  private final Subject subject;
060  private final AppConfigurationEntry[] entries;
061  private final LoginModule[] modules;
062  private final Map sharedState;
063
064  public LoginContext (final String name) throws LoginException
065  {
066    this (name, new Subject(), defaultHandler());
067  }
068
069  public LoginContext (final String name, final CallbackHandler cbHandler)
070    throws LoginException
071  {
072    this (name, new Subject(), cbHandler);
073  }
074
075  public LoginContext (final String name, final Subject subject)
076    throws LoginException
077  {
078    this (name, subject, defaultHandler());
079  }
080
081  public LoginContext (final String name, final Subject subject,
082                       final CallbackHandler cbHandler)
083    throws LoginException
084  {
085    this (name, subject, cbHandler, null);
086  }
087
088  /** @since 1.5 */
089  public LoginContext (final String name, final Subject subject,
090                       final CallbackHandler cbHandler,
091                       Configuration config)
092    throws LoginException
093  {
094    this.name = name;
095    this.subject = subject;
096    this.cbHandler = cbHandler;
097    if (config == null)
098      config = Configuration.getConfig();
099    AppConfigurationEntry[] entries = config.getAppConfigurationEntry (name);
100    if (entries == null)
101      entries = config.getAppConfigurationEntry (OTHER);
102    if (entries == null)
103      throw new LoginException ("no configured modules for application "
104                                + name);
105    this.entries = entries;
106    modules = new LoginModule[entries.length];
107    sharedState = new HashMap();
108    for (int i = 0; i < entries.length; i++)
109      modules[i] = lookupModule (entries[i], subject, sharedState);
110  }
111
112  /**
113   * Returns the authenticated subject, or the parameter passed to one
114   * of the constructors. <code>null</code> is returned if the previous
115   * login attempt failed and there was no subject provided.
116   *
117   * @return The subject, or null.
118   */
119  public Subject getSubject()
120  {
121    return subject;
122  }
123
124  /**
125   * Logs a subject in, using all login modules configured for this
126   * application. This method will call the {@link LoginModule#login()}
127   * method of each module configured for this application, stopping
128   * if a REQUISITE module fails or if a SUFFICIENT module succeeds. If
129   * the overall login attempt fails, a {@link LoginException} will be
130   * thrown.
131   *
132   * @throws LoginException If logging in fails.
133   */
134  public void login() throws LoginException
135  {
136    boolean failure = false;
137    for (int i = 0; i < modules.length; i++)
138      {
139        try
140          {
141            boolean result = modules[i].login();
142            if (!result)
143              {
144                if (entries[i].getControlFlag() ==
145                    AppConfigurationEntry.LoginModuleControlFlag.REQUISITE)
146                  throw new LoginException ("REQUISITE module " + entries[i].getLoginModuleName()
147                                            + " failed");
148                else if (entries[i].getControlFlag() ==
149                         AppConfigurationEntry.LoginModuleControlFlag.REQUIRED)
150                  failure = true;
151              }
152            else
153              {
154                if (entries[i].getControlFlag() ==
155                    AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT)
156                  break;
157              }
158          }
159        catch (LoginException le)
160          {
161            if (entries[i].getControlFlag() !=
162                AppConfigurationEntry.LoginModuleControlFlag.REQUISITE)
163              continue;
164            for (int j = 0; j < modules.length; j++)
165              modules[i].abort();
166            throw le;
167          }
168      }
169    if (failure)
170      throw new LoginException ("not all REQUIRED modules succeeded");
171
172    for (int i = 0; i < modules.length; i++)
173      modules[i].commit();
174  }
175
176  /**
177   * Logs a subject out, cleaning up any state that may be in memory.
178   *
179   * @throws LoginException If logging out fails.
180   */
181  public void logout() throws LoginException
182  {
183    for (int i = 0; i < modules.length; i++)
184      modules[i].logout();
185  }
186
187  // Own methods.
188
189  /**
190   * Fetch the default callback handler, based on the
191   * auth.login.defaultCallbackHandler property, or null if it is not
192   * set.
193   */
194  private static CallbackHandler defaultHandler()
195  {
196    GetSecurityPropertyAction act =
197      new GetSecurityPropertyAction ("auth.login.defaultCallbackHandler");
198    String classname = (String) AccessController.doPrivileged (act);
199    if (classname != null)
200      {
201        try
202          {
203            return (CallbackHandler) Class.forName (classname).newInstance();
204          }
205        catch (ClassNotFoundException cnfe)
206          {
207            return null;
208          }
209        catch (ClassCastException cce)
210          {
211            return null;
212          }
213        catch (IllegalAccessException iae)
214          {
215            return null;
216          }
217        catch (InstantiationException ie)
218          {
219            return null;
220          }
221      }
222    return null;
223  }
224
225  private LoginModule lookupModule (AppConfigurationEntry entry,
226                                    Subject subject, Map sharedState)
227    throws LoginException
228  {
229    LoginModule module = null;
230    Exception cause = null;
231    try
232      {
233        ClassLoader cl = Thread.currentThread().getContextClassLoader();
234        Class c = Class.forName(entry.getLoginModuleName(), true, cl);
235        module = (LoginModule) c.newInstance();
236      }
237    catch (ClassNotFoundException cnfe)
238      {
239        cause = cnfe;
240      }
241    catch (ClassCastException cce)
242      {
243        cause = cce;
244      }
245    catch (IllegalAccessException iae)
246      {
247        cause = iae;
248      }
249    catch (InstantiationException ie)
250      {
251        cause = ie;
252      }
253
254    if (cause != null)
255      {
256        LoginException le = new LoginException ("could not load module "
257                                                + entry.getLoginModuleName());
258        le.initCause (cause);
259        throw le;
260      }
261
262    module.initialize (subject, cbHandler, sharedState, entry.getOptions());
263    return module;
264  }
265}