This is an automated email from the ASF dual-hosted git repository. reschke pushed a commit to branch OAK-12284 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 7c71c604b1f934df604622a71be0890ae1528a59 Author: Julian Reschke <[email protected]> AuthorDate: Fri Jun 26 13:00:11 2026 +0100 OAK-12284: oak-http: extract Authorization Field parsing and add test coverage --- .../jackrabbit/oak/http/AuthorizationField.java | 56 +++++++++++++++++ .../org/apache/jackrabbit/oak/http/OakServlet.java | 13 +--- .../oak/http/AuthorizationFieldTest.java | 72 ++++++++++++++++++++++ 3 files changed, 129 insertions(+), 12 deletions(-) diff --git a/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java b/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java new file mode 100755 index 0000000000..c572e4a3ed --- /dev/null +++ b/oak-http/src/main/java/org/apache/jackrabbit/oak/http/AuthorizationField.java @@ -0,0 +1,56 @@ +/* + * 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.jackrabbit.oak.http; + +import org.apache.jackrabbit.util.Base64; + +import javax.jcr.SimpleCredentials; +import javax.security.auth.login.LoginException; +import java.util.Enumeration; +import java.util.NoSuchElementException; + +public class AuthorizationField { + + private AuthorizationField() { + // no constructor + } + + public static SimpleCredentials valueOf(Enumeration<String> values) throws LoginException { + String field ; + try { + field = values.nextElement(); + } catch (NoSuchElementException ex) { + throw new LoginException(ex.getMessage()); + } + + if (values.hasMoreElements()) { + throw new LoginException("Authorization field has multiple field line values"); + } + + return parseCredentials(field); + } + + private static SimpleCredentials parseCredentials(String fieldValue) throws LoginException { + if (fieldValue.startsWith("Basic ")) { + String[] basic = + Base64.decode(fieldValue.substring("Basic ".length())).split(":"); + return new SimpleCredentials(basic[0], basic[1].toCharArray()); + } else { + throw new LoginException("Only Basic Authentication supported"); + } + } +} diff --git a/oak-http/src/main/java/org/apache/jackrabbit/oak/http/OakServlet.java b/oak-http/src/main/java/org/apache/jackrabbit/oak/http/OakServlet.java index c6bcd3bc7c..1e000c21b5 100644 --- a/oak-http/src/main/java/org/apache/jackrabbit/oak/http/OakServlet.java +++ b/oak-http/src/main/java/org/apache/jackrabbit/oak/http/OakServlet.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.Iterator; import java.util.Map.Entry; -import javax.jcr.Credentials; import javax.jcr.NoSuchWorkspaceException; import javax.jcr.SimpleCredentials; import javax.security.auth.login.LoginException; @@ -39,7 +38,6 @@ import org.apache.jackrabbit.oak.api.ContentSession; import org.apache.jackrabbit.oak.api.Root; import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.commons.PathUtils; -import org.apache.jackrabbit.util.Base64; import org.apache.tika.mime.MediaType; public class OakServlet extends HttpServlet { @@ -68,16 +66,7 @@ public class OakServlet extends HttpServlet { HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { - Credentials credentials = null; - - String authorization = request.getHeader("Authorization"); - if (authorization != null && authorization.startsWith("Basic ")) { - String[] basic = - Base64.decode(authorization.substring("Basic ".length())).split(":"); - credentials = new SimpleCredentials(basic[0], basic[1].toCharArray()); - } else { - throw new LoginException(); - } + SimpleCredentials credentials = AuthorizationField.valueOf(request.getHeaders("Authorization")); ContentSession session = repository.login(credentials, null); try { diff --git a/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java b/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java new file mode 100755 index 0000000000..e2a3ab214d --- /dev/null +++ b/oak-http/src/test/java/org/apache/jackrabbit/oak/http/AuthorizationFieldTest.java @@ -0,0 +1,72 @@ +/* + * 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.jackrabbit.oak.http; + +import org.junit.Test; + +import javax.jcr.SimpleCredentials; +import javax.security.auth.login.LoginException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class AuthorizationFieldTest { + + @Test(expected = LoginException.class) + public void testNone() throws LoginException { + AuthorizationField.valueOf(Collections.enumeration(List.of())); + } + + @Test(expected = LoginException.class) + public void testTwo() throws LoginException { + AuthorizationField.valueOf(Collections.enumeration(List.of("a", "b"))); + } + + @Test + public void testValid() throws LoginException { + String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8)); + SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("Basic " + b64))); + assertEquals("foo", credentials.getUserID()); + assertEquals("bar", new String(credentials.getPassword())); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) // BUG: OAK-12259 + public void testInvalidBase64() throws LoginException { + String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8)); + SimpleCredentials credentials = AuthorizationField.valueOf(Collections.enumeration(List.of("Basic dksjdkj" + b64))); + assertEquals("foo", credentials.getUserID()); + assertEquals("bar", new String(credentials.getPassword())); + } + + @Test(expected = LoginException.class) + public void testNoScheme() throws LoginException { + String b64 = Base64.getEncoder().encodeToString("foo:bar".getBytes(StandardCharsets.UTF_8)); + AuthorizationField.valueOf(Collections.enumeration(List.of(b64))); + } + + @Test + public void testColonInPassword() throws LoginException { + String b64 = "Basic " + Base64.getEncoder().encodeToString("foo:bar:qux".getBytes(StandardCharsets.UTF_8)); + SimpleCredentials credentials =AuthorizationField.valueOf(Collections.enumeration(List.of(b64))); + assertEquals("foo", credentials.getUserID()); + // BUG: OAK-12259 + assertEquals("bar", new String(credentials.getPassword())); + } +} \ No newline at end of file
