001: // ========================================================================
002: // Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
003: // ------------------------------------------------------------------------
004: // Licensed under the Apache License, Version 2.0 (the "License");
005: // you may not use this file except in compliance with the License.
006: // You may obtain a copy of the License at
007: // http://www.apache.org/licenses/LICENSE-2.0
008: // Unless required by applicable law or agreed to in writing, software
009: // distributed under the License is distributed on an "AS IS" BASIS,
010: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: // See the License for the specific language governing permissions and
012: // limitations under the License.
013: // ========================================================================
014:
015: package org.mortbay.jetty.security;
016:
017: import java.io.IOException;
018: import java.io.Serializable;
019: import java.security.Principal;
020:
021: import javax.servlet.http.HttpServletRequest;
022: import javax.servlet.http.HttpServletResponse;
023: import javax.servlet.http.HttpSession;
024: import javax.servlet.http.HttpSessionBindingEvent;
025: import javax.servlet.http.HttpSessionBindingListener;
026:
027: import org.mortbay.jetty.Request;
028: import org.mortbay.jetty.Response;
029: import org.mortbay.log.Log;
030: import org.mortbay.util.URIUtil;
031:
032: /* ------------------------------------------------------------ */
033: /** FORM Authentication Authenticator.
034: * The HTTP Session is used to store the authentication status of the
035: * user, which can be distributed.
036: * If the realm implements SSORealm, SSO is supported.
037: *
038: * @author Greg Wilkins (gregw)
039: * @author dan@greening.name
040: */
041: public class FormAuthenticator implements Authenticator {
042: /* ------------------------------------------------------------ */
043: public final static String __J_URI = "org.mortbay.jetty.URI";
044: public final static String __J_AUTHENTICATED = "org.mortbay.jetty.Auth";
045: public final static String __J_SECURITY_CHECK = "/j_security_check";
046: public final static String __J_USERNAME = "j_username";
047: public final static String __J_PASSWORD = "j_password";
048:
049: private String _formErrorPage;
050: private String _formErrorPath;
051: private String _formLoginPage;
052: private String _formLoginPath;
053:
054: /* ------------------------------------------------------------ */
055: public String getAuthMethod() {
056: return HttpServletRequest.FORM_AUTH;
057: }
058:
059: /* ------------------------------------------------------------ */
060: public void setLoginPage(String path) {
061: if (!path.startsWith("/")) {
062: Log.warn("form-login-page must start with /");
063: path = "/" + path;
064: }
065: _formLoginPage = path;
066: _formLoginPath = path;
067: if (_formLoginPath.indexOf('?') > 0)
068: _formLoginPath = _formLoginPath.substring(0, _formLoginPath
069: .indexOf('?'));
070: }
071:
072: /* ------------------------------------------------------------ */
073: public String getLoginPage() {
074: return _formLoginPage;
075: }
076:
077: /* ------------------------------------------------------------ */
078: public void setErrorPage(String path) {
079: if (path == null || path.trim().length() == 0) {
080: _formErrorPath = null;
081: _formErrorPage = null;
082: } else {
083: if (!path.startsWith("/")) {
084: Log.warn("form-error-page must start with /");
085: path = "/" + path;
086: }
087: _formErrorPage = path;
088: _formErrorPath = path;
089:
090: if (_formErrorPath != null
091: && _formErrorPath.indexOf('?') > 0)
092: _formErrorPath = _formErrorPath.substring(0,
093: _formErrorPath.indexOf('?'));
094: }
095: }
096:
097: /* ------------------------------------------------------------ */
098: public String getErrorPage() {
099: return _formErrorPage;
100: }
101:
102: /* ------------------------------------------------------------ */
103: /** Perform form authentication.
104: * Called from SecurityHandler.
105: * @return UserPrincipal if authenticated else null.
106: */
107: public Principal authenticate(UserRealm realm,
108: String pathInContext, Request request, Response response)
109: throws IOException {
110: // Handle paths
111: String uri = pathInContext;
112:
113: // Setup session
114: HttpSession session = request.getSession(response != null);
115: if (session == null)
116: return null;
117:
118: // Handle a request for authentication.
119: // TODO perhaps j_securitycheck can be uri suffix?
120: if (uri.endsWith(__J_SECURITY_CHECK)) {
121: // Check the session object for login info.
122: FormCredential form_cred = new FormCredential();
123: form_cred.authenticate(realm, request
124: .getParameter(__J_USERNAME), request
125: .getParameter(__J_PASSWORD), request);
126:
127: String nuri = (String) session.getAttribute(__J_URI);
128: if (nuri == null || nuri.length() == 0) {
129: nuri = request.getContextPath();
130: if (nuri.length() == 0)
131: nuri = "/";
132: }
133:
134: if (form_cred._userPrincipal != null) {
135: // Authenticated OK
136: if (Log.isDebugEnabled())
137: Log.debug("Form authentication OK for "
138: + form_cred._jUserName);
139: session.removeAttribute(__J_URI); // Remove popped return URI.
140: request.setAuthType(Constraint.__FORM_AUTH);
141: request.setUserPrincipal(form_cred._userPrincipal);
142: session.setAttribute(__J_AUTHENTICATED, form_cred);
143:
144: // Sign-on to SSO mechanism
145: if (realm instanceof SSORealm)
146: ((SSORealm) realm).setSingleSignOn(request,
147: response, form_cred._userPrincipal,
148: new Password(form_cred._jPassword));
149:
150: // Redirect to original request
151: response.setContentLength(0);
152: response.sendRedirect(response.encodeRedirectURL(nuri));
153: } else {
154: if (Log.isDebugEnabled())
155: Log.debug("Form authentication FAILED for "
156: + form_cred._jUserName);
157: if (_formErrorPage == null)
158: response
159: .sendError(HttpServletResponse.SC_FORBIDDEN);
160: else {
161: response.setContentLength(0);
162: response
163: .sendRedirect(response
164: .encodeRedirectURL(URIUtil
165: .addPaths(request
166: .getContextPath(),
167: _formErrorPage)));
168: }
169: }
170:
171: // Security check is always false, only true after final redirection.
172: return null;
173: }
174:
175: // Check if the session is already authenticated.
176: FormCredential form_cred = (FormCredential) session
177: .getAttribute(__J_AUTHENTICATED);
178:
179: if (form_cred != null) {
180: // We have a form credential. Has it been distributed?
181: if (form_cred._userPrincipal == null) {
182: // This form_cred appears to have been distributed. Need to reauth
183: form_cred.authenticate(realm, request);
184:
185: // Sign-on to SSO mechanism
186: if (form_cred._userPrincipal != null
187: && realm instanceof SSORealm) {
188: ((SSORealm) realm).setSingleSignOn(request,
189: response, form_cred._userPrincipal,
190: new Password(form_cred._jPassword));
191: }
192: } else if (!realm.reauthenticate(form_cred._userPrincipal))
193: // Else check that it is still authenticated.
194: form_cred._userPrincipal = null;
195:
196: // If this credential is still authenticated
197: if (form_cred._userPrincipal != null) {
198: if (Log.isDebugEnabled())
199: Log.debug("FORM Authenticated for "
200: + form_cred._userPrincipal.getName());
201: request.setAuthType(Constraint.__FORM_AUTH);
202: request.setUserPrincipal(form_cred._userPrincipal);
203: return form_cred._userPrincipal;
204: } else
205: session.setAttribute(__J_AUTHENTICATED, null);
206: } else if (realm instanceof SSORealm) {
207: // Try a single sign on.
208: Credential cred = ((SSORealm) realm).getSingleSignOn(
209: request, response);
210:
211: if (request.getUserPrincipal() != null) {
212: form_cred = new FormCredential();
213: form_cred._userPrincipal = request.getUserPrincipal();
214: form_cred._jUserName = form_cred._userPrincipal
215: .getName();
216: if (cred != null)
217: form_cred._jPassword = cred.toString();
218: if (Log.isDebugEnabled())
219: Log.debug("SSO for " + form_cred._userPrincipal);
220:
221: request.setAuthType(Constraint.__FORM_AUTH);
222: session.setAttribute(__J_AUTHENTICATED, form_cred);
223: return form_cred._userPrincipal;
224: }
225: }
226:
227: // Don't authenticate authform or errorpage
228: if (isLoginOrErrorPage(pathInContext))
229: return SecurityHandler.__NOBODY;
230:
231: // redirect to login page
232: if (response != null) {
233: if (request.getQueryString() != null)
234: uri += "?" + request.getQueryString();
235: session.setAttribute(__J_URI, request.getScheme() + "://"
236: + request.getServerName() + ":"
237: + request.getServerPort()
238: + URIUtil.addPaths(request.getContextPath(), uri));
239: response.setContentLength(0);
240: response.sendRedirect(response
241: .encodeRedirectURL(URIUtil.addPaths(request
242: .getContextPath(), _formLoginPage)));
243: }
244:
245: return null;
246: }
247:
248: public boolean isLoginOrErrorPage(String pathInContext) {
249: return pathInContext != null
250: && (pathInContext.equals(_formErrorPath) || pathInContext
251: .equals(_formLoginPath));
252: }
253:
254: /* ------------------------------------------------------------ */
255: /** FORM Authentication credential holder.
256: */
257: private static class FormCredential implements Serializable,
258: HttpSessionBindingListener {
259: String _jUserName;
260: String _jPassword;
261: transient Principal _userPrincipal;
262: transient UserRealm _realm;
263:
264: void authenticate(UserRealm realm, String user,
265: String password, Request request) {
266: _jUserName = user;
267: _jPassword = password;
268: _userPrincipal = realm
269: .authenticate(user, password, request);
270: if (_userPrincipal != null)
271: _realm = realm;
272: }
273:
274: void authenticate(UserRealm realm, Request request) {
275: _userPrincipal = realm.authenticate(_jUserName, _jPassword,
276: request);
277: if (_userPrincipal != null)
278: _realm = realm;
279: }
280:
281: public void valueBound(HttpSessionBindingEvent event) {
282: }
283:
284: public void valueUnbound(HttpSessionBindingEvent event) {
285: if (Log.isDebugEnabled())
286: Log.debug("Logout " + _jUserName);
287:
288: if (_realm instanceof SSORealm)
289: ((SSORealm) _realm).clearSingleSignOn(_jUserName);
290:
291: if (_realm != null && _userPrincipal != null)
292: _realm.logout(_userPrincipal);
293: }
294:
295: public int hashCode() {
296: return _jUserName.hashCode() + _jPassword.hashCode();
297: }
298:
299: public boolean equals(Object o) {
300: if (!(o instanceof FormCredential))
301: return false;
302: FormCredential fc = (FormCredential) o;
303: return _jUserName.equals(fc._jUserName)
304: && _jPassword.equals(fc._jPassword);
305: }
306:
307: public String toString() {
308: return "Cred[" + _jUserName + "]";
309: }
310:
311: }
312: }
|