001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.providers.jaas;
017:
018: import org.acegisecurity.Authentication;
019:
020: import org.acegisecurity.context.SecurityContextHolder;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024:
025: import java.util.Map;
026:
027: import javax.security.auth.Subject;
028: import javax.security.auth.callback.CallbackHandler;
029: import javax.security.auth.login.LoginException;
030: import javax.security.auth.spi.LoginModule;
031:
032: /**
033: * An implementation of {@link LoginModule} that uses an Acegi Security {@link
034: * org.acegisecurity.context.SecurityContext SecurityContext} to provide authentication.<p>This LoginModule
035: * provides opposite functionality to the {@link JaasAuthenticationProvider} API, and should not really be used in
036: * conjunction with it.</p>
037: * <p>The {@link JaasAuthenticationProvider} allows Acegi to authenticate against Jaas.</p>
038: * <p>The SecurityContextLoginModule allows a Jaas based application to authenticate against Acegi. If there is no
039: * Authentication in the {@link SecurityContextHolder} the login() method will throw a LoginException by default.
040: * This functionality can be changed with the <tt>ignoreMissingAuthentication</tt> option by setting it to "true".
041: * Setting ignoreMissingAuthentication=true will tell the SecurityContextLoginModule to simply return false and be
042: * ignored if the authentication is null.</p>
043: *
044: * @author Brian Moseley
045: * @author Ray Krueger
046: */
047: public class SecurityContextLoginModule implements LoginModule {
048: //~ Static fields/initializers =====================================================================================
049:
050: private static final Log log = LogFactory
051: .getLog(SecurityContextLoginModule.class);
052:
053: //~ Instance fields ================================================================================================
054:
055: private Authentication authen;
056: private Subject subject;
057: private boolean ignoreMissingAuthentication = false;
058:
059: //~ Methods ========================================================================================================
060:
061: /**
062: * Abort the authentication process by forgetting the Acegi Security <code>Authentication</code>.
063: *
064: * @return true if this method succeeded, or false if this <code>LoginModule</code> should be ignored.
065: *
066: * @exception LoginException if the abort fails
067: */
068: public boolean abort() throws LoginException {
069: if (authen == null) {
070: return false;
071: }
072:
073: authen = null;
074:
075: return true;
076: }
077:
078: /**
079: * Authenticate the <code>Subject</code> (phase two) by adding the Acegi Security
080: * <code>Authentication</code> to the <code>Subject</code>'s principals.
081: *
082: * @return true if this method succeeded, or false if this <code>LoginModule</code> should be ignored.
083: *
084: * @exception LoginException if the commit fails
085: */
086: public boolean commit() throws LoginException {
087: if (authen == null) {
088: return false;
089: }
090:
091: subject.getPrincipals().add(authen);
092:
093: return true;
094: }
095:
096: Authentication getAuthentication() {
097: return authen;
098: }
099:
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,
115: CallbackHandler callbackHandler, Map sharedState,
116: Map options) {
117: this .subject = subject;
118:
119: if (options != null) {
120: ignoreMissingAuthentication = "true".equals(options
121: .get("ignoreMissingAuthentication"));
122: }
123: }
124:
125: /**
126: * Authenticate the <code>Subject</code> (phase one) by extracting the Acegi Security
127: * <code>Authentication</code> from the current <code>SecurityContext</code>.
128: *
129: * @return true if the authentication succeeded, or false if this <code>LoginModule</code> should be ignored.
130: *
131: * @throws LoginException if the authentication fails
132: */
133: public boolean login() throws LoginException {
134: authen = SecurityContextHolder.getContext().getAuthentication();
135:
136: if (authen == null) {
137: String msg = "Login cannot complete, authentication not found in security context";
138:
139: if (ignoreMissingAuthentication) {
140: log.warn(msg);
141:
142: return false;
143: } else {
144: throw new LoginException(msg);
145: }
146: }
147:
148: return true;
149: }
150:
151: /**
152: * Log out the <code>Subject</code>.
153: *
154: * @return true if this method succeeded, or false if this <code>LoginModule</code> should be ignored.
155: *
156: * @exception LoginException if the logout fails
157: */
158: public boolean logout() throws LoginException {
159: if (authen == null) {
160: return false;
161: }
162:
163: subject.getPrincipals().remove(authen);
164: authen = null;
165:
166: return true;
167: }
168: }
|