001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.authenticator;
019:
020: import java.io.IOException;
021: import java.security.Principal;
022:
023: import javax.servlet.http.HttpServletResponse;
024:
025: import org.apache.catalina.connector.Request;
026: import org.apache.catalina.connector.Response;
027: import org.apache.catalina.deploy.LoginConfig;
028: import org.apache.catalina.util.Base64;
029: import org.apache.juli.logging.Log;
030: import org.apache.juli.logging.LogFactory;
031: import org.apache.tomcat.util.buf.ByteChunk;
032: import org.apache.tomcat.util.buf.CharChunk;
033: import org.apache.tomcat.util.buf.MessageBytes;
034:
035: /**
036: * An <b>Authenticator</b> and <b>Valve</b> implementation of HTTP BASIC
037: * Authentication, as outlined in RFC 2617: "HTTP Authentication: Basic
038: * and Digest Access Authentication."
039: *
040: * @author Craig R. McClanahan
041: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
042: */
043:
044: public class BasicAuthenticator extends AuthenticatorBase {
045: private static Log log = LogFactory
046: .getLog(BasicAuthenticator.class);
047:
048: /**
049: * Authenticate bytes.
050: */
051: public static final byte[] AUTHENTICATE_BYTES = { (byte) 'W',
052: (byte) 'W', (byte) 'W', (byte) '-', (byte) 'A', (byte) 'u',
053: (byte) 't', (byte) 'h', (byte) 'e', (byte) 'n', (byte) 't',
054: (byte) 'i', (byte) 'c', (byte) 'a', (byte) 't', (byte) 'e' };
055:
056: // ----------------------------------------------------- Instance Variables
057:
058: /**
059: * Descriptive information about this implementation.
060: */
061: protected static final String info = "org.apache.catalina.authenticator.BasicAuthenticator/1.0";
062:
063: // ------------------------------------------------------------- Properties
064:
065: /**
066: * Return descriptive information about this Valve implementation.
067: */
068: public String getInfo() {
069:
070: return (info);
071:
072: }
073:
074: // --------------------------------------------------------- Public Methods
075:
076: /**
077: * Authenticate the user making this request, based on the specified
078: * login configuration. Return <code>true</code> if any specified
079: * constraint has been satisfied, or <code>false</code> if we have
080: * created a response challenge already.
081: *
082: * @param request Request we are processing
083: * @param response Response we are creating
084: * @param config Login configuration describing how authentication
085: * should be performed
086: *
087: * @exception IOException if an input/output error occurs
088: */
089: public boolean authenticate(Request request, Response response,
090: LoginConfig config) throws IOException {
091:
092: // Have we already authenticated someone?
093: Principal principal = request.getUserPrincipal();
094: String ssoId = (String) request
095: .getNote(Constants.REQ_SSOID_NOTE);
096: if (principal != null) {
097: if (log.isDebugEnabled())
098: log.debug("Already authenticated '"
099: + principal.getName() + "'");
100: // Associate the session with any existing SSO session
101: if (ssoId != null)
102: associate(ssoId, request.getSessionInternal(true));
103: return (true);
104: }
105:
106: // Is there an SSO session against which we can try to reauthenticate?
107: if (ssoId != null) {
108: if (log.isDebugEnabled())
109: log.debug("SSO Id " + ssoId + " set; attempting "
110: + "reauthentication");
111: /* Try to reauthenticate using data cached by SSO. If this fails,
112: either the original SSO logon was of DIGEST or SSL (which
113: we can't reauthenticate ourselves because there is no
114: cached username and password), or the realm denied
115: the user's reauthentication for some reason.
116: In either case we have to prompt the user for a logon */
117: if (reauthenticateFromSSO(ssoId, request))
118: return true;
119: }
120:
121: // Validate any credentials already included with this request
122: String username = null;
123: String password = null;
124:
125: MessageBytes authorization = request.getCoyoteRequest()
126: .getMimeHeaders().getValue("authorization");
127:
128: if (authorization != null) {
129: authorization.toBytes();
130: ByteChunk authorizationBC = authorization.getByteChunk();
131: if (authorizationBC.startsWithIgnoreCase("basic ", 0)) {
132: authorizationBC
133: .setOffset(authorizationBC.getOffset() + 6);
134: // FIXME: Add trimming
135: // authorizationBC.trim();
136:
137: CharChunk authorizationCC = authorization
138: .getCharChunk();
139: Base64.decode(authorizationBC, authorizationCC);
140:
141: // Get username and password
142: int colon = authorizationCC.indexOf(':');
143: if (colon < 0) {
144: username = authorizationCC.toString();
145: } else {
146: char[] buf = authorizationCC.getBuffer();
147: username = new String(buf, 0, colon);
148: password = new String(buf, colon + 1,
149: authorizationCC.getEnd() - colon - 1);
150: }
151:
152: authorizationBC
153: .setOffset(authorizationBC.getOffset() - 6);
154: }
155:
156: principal = context.getRealm().authenticate(username,
157: password);
158: if (principal != null) {
159: register(request, response, principal,
160: Constants.BASIC_METHOD, username, password);
161: return (true);
162: }
163: }
164:
165: // Send an "unauthorized" response and an appropriate challenge
166: MessageBytes authenticate = response.getCoyoteResponse()
167: .getMimeHeaders().addValue(AUTHENTICATE_BYTES, 0,
168: AUTHENTICATE_BYTES.length);
169: CharChunk authenticateCC = authenticate.getCharChunk();
170: authenticateCC.append("Basic realm=\"");
171: if (config.getRealmName() == null) {
172: authenticateCC.append(request.getServerName());
173: authenticateCC.append(':');
174: authenticateCC.append(Integer.toString(request
175: .getServerPort()));
176: } else {
177: authenticateCC.append(config.getRealmName());
178: }
179: authenticateCC.append('\"');
180: authenticate.toChars();
181: response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
182: //response.flushBuffer();
183: return (false);
184:
185: }
186:
187: }
|