View Javadoc

1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.providers.jaas;
17  
18  import org.acegisecurity.Authentication;
19  
20  import org.acegisecurity.context.SecurityContextHolder;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  
25  import java.util.Map;
26  
27  import javax.security.auth.Subject;
28  import javax.security.auth.callback.CallbackHandler;
29  import javax.security.auth.login.LoginException;
30  import javax.security.auth.spi.LoginModule;
31  
32  
33  /**
34   * An implementation of {@link LoginModule} that uses an Acegi Security {@link
35   * org.acegisecurity.context.SecurityContext SecurityContext} to provide authentication.<p>This LoginModule
36   * provides opposite functionality to the {@link JaasAuthenticationProvider} API, and should not really be used in
37   * conjunction with it.</p>
38   *  <p>The {@link JaasAuthenticationProvider} allows Acegi to authenticate against Jaas.</p>
39   *  <p>The SecurityContextLoginModule allows a Jaas based application to authenticate against Acegi. If there is no
40   * Authentication in the  {@link SecurityContextHolder} the login() method will throw a LoginException by default.
41   * This functionality can be changed with the <tt>ignoreMissingAuthentication</tt> option by setting it to "true".
42   * Setting ignoreMissingAuthentication=true will tell the SecurityContextLoginModule to simply return false and be
43   * ignored if the authentication is null.</p>
44   *
45   * @author Brian Moseley
46   * @author Ray Krueger
47   */
48  public class SecurityContextLoginModule implements LoginModule {
49      //~ Static fields/initializers =====================================================================================
50  
51      private static final Log log = LogFactory.getLog(SecurityContextLoginModule.class);
52  
53      //~ Instance fields ================================================================================================
54  
55      private Authentication authen;
56      private Subject subject;
57      private boolean ignoreMissingAuthentication = false;
58  
59      //~ Methods ========================================================================================================
60  
61      /**
62       * Abort the authentication process by forgetting the Acegi Security <code>Authentication</code>.
63       *
64       * @return true if this method succeeded, or false if this <code>LoginModule</code> should be ignored.
65       *
66       * @exception LoginException if the abort fails
67       */
68      public boolean abort() throws LoginException {
69          if (authen == null) {
70              return false;
71          }
72  
73          authen = null;
74  
75          return true;
76      }
77  
78      /**
79       * Authenticate the <code>Subject</code> (phase two) by adding the Acegi Security
80       * <code>Authentication</code> to the <code>Subject</code>'s principals.
81       *
82       * @return true if this method succeeded, or false if this <code>LoginModule</code> should be ignored.
83       *
84       * @exception LoginException if the commit fails
85       */
86      public boolean commit() throws LoginException {
87          if (authen == null) {
88              return false;
89          }
90  
91          subject.getPrincipals().add(authen);
92  
93          return true;
94      }
95  
96      Authentication getAuthentication() {
97          return authen;
98      }
99  
100     Subject getSubject() {
101         return subject;
102     }
103 
104     /**
105      * Initialize this <code>LoginModule</code>. Ignores the callback handler, since the code establishing the
106      * <code>LoginContext</code> likely won't provide one that understands Acegi Security. Also ignores the
107      * <code>sharedState</code> and <code>options</code> parameters, since none are recognized.
108      *
109      * @param subject the <code>Subject</code> to be authenticated. <p>
110      * @param callbackHandler is ignored
111      * @param sharedState is ignored
112      * @param options are ignored
113      */
114     public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
115         this.subject = subject;
116 
117         if (options != null) {
118             ignoreMissingAuthentication = "true".equals(options.get("ignoreMissingAuthentication"));
119         }
120     }
121 
122     /**
123      * Authenticate the <code>Subject</code> (phase one) by extracting the Acegi Security
124      * <code>Authentication</code> from the current <code>SecurityContext</code>.
125      *
126      * @return true if the authentication succeeded, or false if this <code>LoginModule</code> should be ignored.
127      *
128      * @throws LoginException if the authentication fails
129      */
130     public boolean login() throws LoginException {
131         authen = SecurityContextHolder.getContext().getAuthentication();
132 
133         if (authen == null) {
134             String msg = "Login cannot complete, authentication not found in security context";
135 
136             if (ignoreMissingAuthentication) {
137                 log.warn(msg);
138 
139                 return false;
140             } else {
141                 throw new LoginException(msg);
142             }
143         }
144 
145         return true;
146     }
147 
148     /**
149      * Log out the <code>Subject</code>.
150      *
151      * @return true if this method succeeded, or false if this <code>LoginModule</code> should be ignored.
152      *
153      * @exception LoginException if the logout fails
154      */
155     public boolean logout() throws LoginException {
156         if (authen == null) {
157             return false;
158         }
159 
160         subject.getPrincipals().remove(authen);
161         authen = null;
162 
163         return true;
164     }
165 }