View Javadoc

1   /* Copyright 2004, 2005 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  import org.acegisecurity.context.SecurityContextHolder;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import java.util.Map;
25  
26  import javax.security.auth.Subject;
27  import javax.security.auth.callback.CallbackHandler;
28  import javax.security.auth.login.LoginException;
29  import javax.security.auth.spi.LoginModule;
30  
31  
32  /***
33   * An implementation of {@link LoginModule} that uses an Acegi Security
34   * {@link org.acegisecurity.context.SecurityContext SecurityContext}
35   * to provide authentication. <br />
36   * This LoginModule provides opposite functionality to the {@link
37   * JaasAuthenticationProvider} API, and should not really be used in
38   * conjunction with it. <br />
39   * The {@link JaasAuthenticationProvider} allows Acegi to authenticate against
40   * Jaas. <br />
41   * The SecurityContextLoginModule allows a Jaas based application to
42   * authenticate against Acegi. If there is no Authentication in the {@link
43   * SecurityContextHolder} the login() method will throw a LoginException by
44   * default. This functionality can be changed with the
45   * <tt>ignoreMissingAuthentication</tt> option by setting it to "true".
46   * Setting  ignoreMissingAuthentication=true will tell the
47   * SecurityContextLoginModule to simply return false and be ignored if the
48   * authentication is null.
49   *
50   * @author Brian Moseley
51   * @author Ray Krueger
52   */
53  public class SecurityContextLoginModule implements LoginModule {
54      //~ Static fields/initializers =============================================
55  
56      private static final Log log = LogFactory.getLog(SecurityContextLoginModule.class);
57  
58      //~ Instance fields ========================================================
59  
60      private Authentication authen;
61      private Subject subject;
62      private boolean ignoreMissingAuthentication = false;
63  
64      //~ Methods ================================================================
65  
66      /***
67       * Abort the authentication process by forgetting the Acegi Security
68       * <code>Authentication</code>.
69       *
70       * @return true if this method succeeded, or false if this
71       *         <code>LoginModule</code> should be ignored.
72       *
73       * @exception LoginException if the abort fails
74       */
75      public boolean abort() throws LoginException {
76          if (authen == null) {
77              return false;
78          }
79  
80          authen = null;
81  
82          return true;
83      }
84  
85      /***
86       * Authenticate the <code>Subject</code> (phase two) by adding the Acegi
87       * Security <code>Authentication</code> to the <code>Subject</code>'s
88       * principals.
89       *
90       * @return true if this method succeeded, or false if this
91       *         <code>LoginModule</code> should be ignored.
92       *
93       * @exception LoginException if the commit fails
94       */
95      public boolean commit() throws LoginException {
96          if (authen == null) {
97              return false;
98          }
99  
100         subject.getPrincipals().add(authen);
101 
102         return true;
103     }
104 
105     /***
106      * Initialize this <code>LoginModule</code>. Ignores the callback handler,
107      * since the code establishing the <code>LoginContext</code> likely won't
108      * provide one that understands Acegi Security. Also ignores the
109      * <code>sharedState</code> and <code>options</code> parameters, since
110      * none are recognized.
111      *
112      * @param subject the <code>Subject</code> to be authenticated. <p>
113      * @param callbackHandler is ignored
114      * @param sharedState is ignored
115      * @param options are ignored
116      */
117     public void initialize(Subject subject, CallbackHandler callbackHandler,
118         Map sharedState, Map options) {
119         this.subject = subject;
120 
121         if (options != null) {
122             ignoreMissingAuthentication = "true".equals(options.get(
123                         "ignoreMissingAuthentication"));
124         }
125     }
126 
127     /***
128      * Authenticate the <code>Subject</code> (phase one) by extracting the
129      * Acegi Security <code>Authentication</code> from the current
130      * <code>SecurityContext</code>.
131      *
132      * @return true if the authentication succeeded, or false if this
133      *         <code>LoginModule</code> should be ignored.
134      *
135      * @throws LoginException if the authentication fails
136      */
137     public boolean login() throws LoginException {
138         authen = SecurityContextHolder.getContext().getAuthentication();
139 
140         if (authen == null) {
141             String msg = "Login cannot complete, authentication not found in security context";
142 
143             if (ignoreMissingAuthentication) {
144                 log.warn(msg);
145 
146                 return false;
147             } else {
148                 throw new LoginException(msg);
149             }
150         }
151 
152         return true;
153     }
154 
155     /***
156      * Log out the <code>Subject</code>.
157      *
158      * @return true if this method succeeded, or false if this
159      *         <code>LoginModule</code> should be ignored.
160      *
161      * @exception LoginException if the logout fails
162      */
163     public boolean logout() throws LoginException {
164         if (authen == null) {
165             return false;
166         }
167 
168         subject.getPrincipals().remove(authen);
169         authen = null;
170 
171         return true;
172     }
173 
174     Authentication getAuthentication() {
175         return authen;
176     }
177 
178     Subject getSubject() {
179         return subject;
180     }
181 }