You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/06/23 11:57:16 UTC
svn commit: r1687015 - in
/tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider:
TomcatAuthConfig.java modules/BasicAuthModule.java
Author: markt
Date: Tue Jun 23 09:57:16 2015
New Revision: 1687015
URL: http://svn.apache.org/r1687015
Log:
Implemented JASPIC module for BASIC authentication
Patch by fjodorver
Added:
tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/modules/BasicAuthModule.java (with props)
Modified:
tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/TomcatAuthConfig.java
Modified: tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/TomcatAuthConfig.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/TomcatAuthConfig.java?rev=1687015&r1=1687014&r2=1687015&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/TomcatAuthConfig.java (original)
+++ tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/TomcatAuthConfig.java Tue Jun 23 09:57:16 2015
@@ -29,6 +29,7 @@ import javax.security.auth.message.confi
import javax.security.auth.message.config.ServerAuthContext;
import org.apache.catalina.Realm;
+import org.apache.catalina.authenticator.jaspic.provider.modules.BasicAuthModule;
import org.apache.catalina.authenticator.jaspic.provider.modules.TomcatAuthModule;
public class TomcatAuthConfig implements ServerAuthConfig {
@@ -92,6 +93,7 @@ public class TomcatAuthConfig implements
private Collection<TomcatAuthModule> getModules() {
List<TomcatAuthModule> modules = new ArrayList<>();
+ modules.add(new BasicAuthModule());
return modules;
}
}
Added: tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/modules/BasicAuthModule.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/modules/BasicAuthModule.java?rev=1687015&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/modules/BasicAuthModule.java (added)
+++ tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/modules/BasicAuthModule.java Tue Jun 23 09:57:16 2015
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.authenticator.jaspic.provider.modules;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.message.AuthException;
+import javax.security.auth.message.AuthStatus;
+import javax.security.auth.message.MessageInfo;
+import javax.security.auth.message.MessagePolicy;
+import javax.security.auth.message.callback.CallerPrincipalCallback;
+import javax.security.auth.message.callback.GroupPrincipalCallback;
+import javax.security.auth.message.callback.PasswordValidationCallback;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.codec.binary.Base64;
+
+public class BasicAuthModule extends TomcatAuthModule {
+
+ private Class<?>[] supportedMessageTypes = new Class[] { HttpServletRequest.class,
+ HttpServletResponse.class };
+
+ private CallbackHandler handler;
+
+
+ @Override
+ public String getAuthenticationType() {
+ return "BASIC";
+ }
+
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
+ CallbackHandler handler, Map options) throws AuthException {
+ this.handler = handler;
+ }
+
+
+ @Override
+ public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
+ Subject serviceSubject) throws AuthException {
+ if (!isMandatory(messageInfo)) {
+ return AuthStatus.SUCCESS;
+ }
+
+ HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
+ HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
+ String authorization = request.getHeader(AUTHORIZATION_HEADER);
+
+ String realmName = getRealmName(messageInfo);
+
+ if (authorization == null) {
+ return sendUnauthorizedError(response, realmName);
+ }
+
+ BasicCredentials credentials = parseAuthorizationString(authorization);
+ String username = credentials.getUsername();
+ char[] password = credentials.getPassword().toCharArray();
+
+ try {
+ PasswordValidationCallback passwordCallback = new PasswordValidationCallback(
+ clientSubject, username, password);
+ handler.handle(new Callback[] { passwordCallback });
+
+ if (!passwordCallback.getResult()) {
+ sendUnauthorizedError(response, realmName);
+ }
+
+ GenericPrincipal principal = getPrincipal(passwordCallback);
+
+ CallerPrincipalCallback principalCallback = new CallerPrincipalCallback(clientSubject,
+ principal);
+ GroupPrincipalCallback groupCallback = new GroupPrincipalCallback(clientSubject,
+ principal.getRoles());
+ handler.handle(new Callback[] { principalCallback, groupCallback });
+ return AuthStatus.SUCCESS;
+
+ } catch (Exception e) {
+ throw new AuthException(e.getMessage());
+ }
+ }
+
+
+ private AuthStatus sendUnauthorizedError(HttpServletResponse response, String realmName)
+ throws AuthException {
+ String authHeader = MessageFormat.format("Basic realm=\"{0}\"", realmName);
+ response.setHeader(AUTH_HEADER_NAME, authHeader);
+ try {
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ } catch (IOException e) {
+ throw new AuthException(e.getMessage());
+ }
+ return AuthStatus.SEND_CONTINUE;
+ }
+
+
+ private GenericPrincipal getPrincipal(PasswordValidationCallback passwordCallback) {
+ Iterator<Object> credentials =
+ passwordCallback.getSubject().getPrivateCredentials().iterator();
+ return (GenericPrincipal) credentials.next();
+ }
+
+
+ private BasicCredentials parseAuthorizationString(String authorization) {
+ MessageBytes authorizationBytes = MessageBytes.newInstance();
+ authorizationBytes.setString(authorization);
+ authorizationBytes.toBytes();
+ ByteChunk authorizationBC = authorizationBytes.getByteChunk();
+ return new BasicCredentials(authorizationBC);
+ }
+
+
+ @Override
+ public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
+ throws AuthException {
+ return null;
+ }
+
+
+ @Override
+ public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
+
+ }
+
+
+ @Override
+ public Class<?>[] getSupportedMessageTypes() {
+ return supportedMessageTypes;
+ }
+
+
+ /**
+ * Parser for an HTTP Authorization header for BASIC authentication as per
+ * RFC 2617 section 2, and the Base64 encoded credentials as per RFC 2045
+ * section 6.8.
+ */
+ protected static class BasicCredentials {
+
+ // the only authentication method supported by this parser
+ // note: we include single white space as its delimiter
+ private static final String METHOD = "basic ";
+
+ private ByteChunk authorization;
+ private int initialOffset;
+ private int base64blobOffset;
+ private int base64blobLength;
+
+ private String username = null;
+ private String password = null;
+
+ /**
+ * Parse the HTTP Authorization header for BASIC authentication as per
+ * RFC 2617 section 2, and the Base64 encoded credentials as per RFC
+ * 2045 section 6.8.
+ *
+ * @param input The header value to parse in-place
+ * @throws IllegalArgumentException If the header does not conform to RFC
+ * 2617
+ */
+ public BasicCredentials(ByteChunk input) throws IllegalArgumentException {
+ authorization = input;
+ initialOffset = input.getOffset();
+ parseMethod();
+ byte[] decoded = parseBase64();
+ parseCredentials(decoded);
+ }
+
+ /**
+ * Trivial accessor.
+ *
+ * @return the decoded username token as a String, which is never be
+ * <code>null</code>, but can be empty.
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Trivial accessor.
+ *
+ * @return the decoded password token as a String, or <code>null</code>
+ * if no password was found in the credentials.
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /*
+ * The authorization method string is case-insensitive and must have at
+ * least one space character as a delimiter.
+ */
+ private void parseMethod() throws IllegalArgumentException {
+ if (authorization.startsWithIgnoreCase(METHOD, 0)) {
+ // step past the auth method name
+ base64blobOffset = initialOffset + METHOD.length();
+ base64blobLength = authorization.getLength() - METHOD.length();
+ } else {
+ // is this possible, or permitted?
+ throw new IllegalArgumentException("Authorization header method is not \"Basic\"");
+ }
+ }
+
+ /*
+ * Decode the base64-user-pass token, which RFC 2617 states can be
+ * longer than the 76 characters per line limit defined in RFC 2045. The
+ * base64 decoder will ignore embedded line break characters as well as
+ * surplus surrounding white space.
+ */
+ private byte[] parseBase64() throws IllegalArgumentException {
+ byte[] decoded = Base64.decodeBase64(authorization.getBuffer(), base64blobOffset,
+ base64blobLength);
+ // restore original offset
+ authorization.setOffset(initialOffset);
+ if (decoded == null) {
+ throw new IllegalArgumentException("Basic Authorization credentials are not Base64");
+ }
+ return decoded;
+ }
+
+ /*
+ * Extract the mandatory username token and separate it from the
+ * optional password token. Tolerate surplus surrounding white space.
+ */
+ private void parseCredentials(byte[] decoded) throws IllegalArgumentException {
+
+ int colon = -1;
+ for (int i = 0; i < decoded.length; i++) {
+ if (decoded[i] == ':') {
+ colon = i;
+ break;
+ }
+ }
+
+ if (colon < 0) {
+ username = new String(decoded, StandardCharsets.ISO_8859_1);
+ // password will remain null!
+ } else {
+ username = new String(decoded, 0, colon, StandardCharsets.ISO_8859_1);
+ password = new String(decoded, colon + 1, decoded.length - colon - 1,
+ StandardCharsets.ISO_8859_1);
+ // tolerate surplus white space around credentials
+ if (password.length() > 1) {
+ password = password.trim();
+ }
+ }
+ // tolerate surplus white space around credentials
+ if (username.length() > 1) {
+ username = username.trim();
+ }
+ }
+ }
+}
Propchange: tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/provider/modules/BasicAuthModule.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org