From 67ce9cc1688953aaa74f8febfaf90e9742d606a6 Mon Sep 17 00:00:00 2001 From: Arjan Tijms Date: Fri, 8 Mar 2024 15:10:53 +0100 Subject: [PATCH] Initial tests for new TCK Signed-off-by: Arjan Tijms --- tck/.mvn/keepme | 0 tck/app-policy/pom.xml | 52 +++ .../test/PolicyRegistrationListener.java | 38 ++ .../authorization/test/ProtectedServlet.java | 63 +++ .../tck/authorization/test/TestPolicy.java | 65 +++ .../src/main/webapp/WEB-INF/beans.xml | 24 ++ .../src/main/webapp/WEB-INF/web.xml | 28 ++ .../tck/authorization/test/AppPolicyIT.java | 97 +++++ tck/app-policy2/pom.xml | 52 +++ .../test/PolicyRegistrationListener.java | 38 ++ .../authorization/test/ProtectedServlet.java | 65 +++ .../tck/authorization/test/TestPolicy.java | 95 +++++ .../src/main/webapp/WEB-INF/beans.xml | 24 ++ .../src/main/webapp/WEB-INF/web.xml | 28 ++ .../tck/authorization/test/AppPolicy2IT.java | 116 ++++++ tck/common/pom.xml | 60 +++ .../authorization/util/ArquillianBase.java | 192 +++++++++ .../tck/authorization/util/Assert.java | 75 ++++ .../tck/authorization/util/ShrinkWrap.java | 36 ++ .../jakarta/tck/authorization/util/Utils.java | 384 ++++++++++++++++++ tck/pom.xml | 346 ++++++++++++++++ tck/reza.pass | 16 + 22 files changed, 1894 insertions(+) create mode 100644 tck/.mvn/keepme create mode 100644 tck/app-policy/pom.xml create mode 100644 tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java create mode 100644 tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java create mode 100644 tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java create mode 100644 tck/app-policy/src/main/webapp/WEB-INF/beans.xml create mode 100644 tck/app-policy/src/main/webapp/WEB-INF/web.xml create mode 100644 tck/app-policy/src/test/java/ee/jakarta/tck/authorization/test/AppPolicyIT.java create mode 100644 tck/app-policy2/pom.xml create mode 100644 tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java create mode 100644 tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java create mode 100644 tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java create mode 100644 tck/app-policy2/src/main/webapp/WEB-INF/beans.xml create mode 100644 tck/app-policy2/src/main/webapp/WEB-INF/web.xml create mode 100644 tck/app-policy2/src/test/java/ee/jakarta/tck/authorization/test/AppPolicy2IT.java create mode 100644 tck/common/pom.xml create mode 100644 tck/common/src/main/java/ee/jakarta/tck/authorization/util/ArquillianBase.java create mode 100644 tck/common/src/main/java/ee/jakarta/tck/authorization/util/Assert.java create mode 100644 tck/common/src/main/java/ee/jakarta/tck/authorization/util/ShrinkWrap.java create mode 100644 tck/common/src/main/java/ee/jakarta/tck/authorization/util/Utils.java create mode 100644 tck/pom.xml create mode 100644 tck/reza.pass diff --git a/tck/.mvn/keepme b/tck/.mvn/keepme new file mode 100644 index 0000000..e69de29 diff --git a/tck/app-policy/pom.xml b/tck/app-policy/pom.xml new file mode 100644 index 0000000..e40e1d6 --- /dev/null +++ b/tck/app-policy/pom.xml @@ -0,0 +1,52 @@ + + + + + 4.0.0 + + + org.eclipse.ee4j.authorization.tck + jakarta-authorization-tck + 4.0.0-SNAPSHOT + + + app-mem-policy + war + + + This tests executing a custom permission check before authentication takes place. + + + + false + + + + + org.eclipse.ee4j.authorization.tck + common + ${project.version} + + + + + app-mem-policy + + diff --git a/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java b/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java new file mode 100644 index 0000000..7fd35cb --- /dev/null +++ b/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authorization.test; + +import jakarta.security.jacc.PolicyFactory; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; + +/** + * ServletContextListener that is used to install a custom authorization policy. + * + * @author Arjan Tijms + * + */ +@WebListener +public class PolicyRegistrationListener implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + PolicyFactory policyFactory = PolicyFactory.getPolicyFactory(); + policyFactory.setPolicy(new TestPolicy(policyFactory.getPolicy())); + } + +} \ No newline at end of file diff --git a/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java b/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java new file mode 100644 index 0000000..8119646 --- /dev/null +++ b/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Contributors to Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.test; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Protected Servlet that prints out the name of the authenticated caller and whether + * this caller is in any of the roles {foo, bar, kaz} + * + *

+ * The role "foo" is required to access this Servlet. "bar" is a role assigned by the + * TestIdentityStore, "kaz" doesn't exist (but we should still be able to test for it). + * + */ +@WebServlet("/protectedServlet/*") +@DeclareRoles("bar") +@ServletSecurity(@HttpConstraint(rolesAllowed = "foo")) +public class ProtectedServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + response.getWriter().write("This is a servlet \n"); + + String webName = null; + if (request.getUserPrincipal() != null) { + webName = request.getUserPrincipal().getName(); + } + + response.getWriter().write("web username: " + webName + "\n"); + + response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n"); + response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n"); + response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n"); + } + +} diff --git a/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java b/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java new file mode 100644 index 0000000..23739d4 --- /dev/null +++ b/tck/app-policy/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authorization.test; + +import jakarta.security.jacc.Policy; +import jakarta.security.jacc.WebResourcePermission; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.logging.Logger; +import javax.security.auth.Subject; + +/** + * Policy implementation that uses a custom permission check + * to grant access to {@code /protectedServlet/[*]/test} to + * the unauthenticated caller. + */ +public class TestPolicy implements Policy { + + private static final Logger LOGGER = Logger.getLogger(TestPolicy.class.getName()); + + private final Policy originalPolicy; + + public TestPolicy(Policy policy) { + this.originalPolicy = policy; + } + + public boolean implies(Permission permissionToBeChecked, Subject subject) { + LOGGER.info(permissionToBeChecked.toString()); + LOGGER.info(subject.toString()); + + // First try our custom permission checking + if (impliesCustom(permissionToBeChecked)) { + return true; + } + + // If custom doesn't grant access, try the original policy so we + // keep all normal checks in place. + return originalPolicy.implies(permissionToBeChecked, subject); + } + + public PermissionCollection getPermissionCollection(Subject subject) { + return originalPolicy.getPermissionCollection(subject); + } + + private boolean impliesCustom(Permission permissionToBeChecked) { + return + permissionToBeChecked instanceof WebResourcePermission && + permissionToBeChecked.getName().startsWith("/protectedServlet/") && + permissionToBeChecked.getName().endsWith("/test"); + } + +} diff --git a/tck/app-policy/src/main/webapp/WEB-INF/beans.xml b/tck/app-policy/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000..7b7b7ad --- /dev/null +++ b/tck/app-policy/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/tck/app-policy/src/main/webapp/WEB-INF/web.xml b/tck/app-policy/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..f6223ae --- /dev/null +++ b/tck/app-policy/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + + BASIC + file + + diff --git a/tck/app-policy/src/test/java/ee/jakarta/tck/authorization/test/AppPolicyIT.java b/tck/app-policy/src/test/java/ee/jakarta/tck/authorization/test/AppPolicyIT.java new file mode 100644 index 0000000..fe17631 --- /dev/null +++ b/tck/app-policy/src/test/java/ee/jakarta/tck/authorization/test/AppPolicyIT.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Contributors to Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.test; + +import static ee.jakarta.tck.authorization.util.Assert.assertDefaultAccess; +import static ee.jakarta.tck.authorization.util.Assert.assertDefaultAuthenticated; +import static ee.jakarta.tck.authorization.util.Assert.assertDefaultNoAccess; +import static ee.jakarta.tck.authorization.util.Assert.assertDefaultNotAuthenticated; +import static ee.jakarta.tck.authorization.util.ShrinkWrap.mavenWar; + +import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider; +import ee.jakarta.tck.authorization.util.ArquillianBase; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.junit.Test; +import org.junit.runner.RunWith; + + +@RunWith(Arquillian.class) +public class AppPolicyIT extends ArquillianBase { + + @Deployment(testable = false) + public static Archive createDeployment() { + return mavenWar(); + } + + // Test several general conditions to make sure security + // works in the normal way + + /** + * Normally authenticated for a request to the default path. + * Should have access via the role foo + */ + @Test + public void testAuthenticated() { + DefaultCredentialsProvider credentialsProvider = new DefaultCredentialsProvider(); + credentialsProvider.addCredentials("reza", "secret1"); + + getWebClient().setCredentialsProvider(credentialsProvider); + + assertDefaultAuthenticated( + readFromServer("/protectedServlet")); + } + + /** + * Not authenticated on the default path. + * Should not have access, since not in the required role foo + */ + @Test + public void testNotAuthenticated() { + assertDefaultNoAccess( + readFromServer("/protectedServlet")); + } + + /** + * Wrongly authenticated on the default path. + * Should not have access, since not in the required role foo + */ + @Test + public void testNotAuthenticatedWrongName() { + assertDefaultNoAccess( + readFromServer("/protectedServlet?name=romo&password=secret1")); + } + + // Test on the special test path which a custom policy is observing + + /** + * Should have access, despite not being in the required role foo. + * The custom policy made an exception here. + * + * But, the caller should not be in any roles (specially, should not be in role foo) + */ + @Test + public void testNotAuthenticatedSpecial() { + String response = readFromServer("/protectedServlet/foo/test"); + + assertDefaultAccess(response); + assertDefaultNotAuthenticated(response); + } + +} diff --git a/tck/app-policy2/pom.xml b/tck/app-policy2/pom.xml new file mode 100644 index 0000000..29dea4d --- /dev/null +++ b/tck/app-policy2/pom.xml @@ -0,0 +1,52 @@ + + + + + 4.0.0 + + + org.eclipse.ee4j.authorization.tck + jakarta-authorization-tck + 4.0.0-SNAPSHOT + + + app-mem-policy2 + war + + + This tests executing a custom permission check before authentication takes place. + + + + false + + + + + org.eclipse.ee4j.authorization.tck + common + ${project.version} + + + + + app-mem-policy2 + + diff --git a/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java b/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java new file mode 100644 index 0000000..7fd35cb --- /dev/null +++ b/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/PolicyRegistrationListener.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authorization.test; + +import jakarta.security.jacc.PolicyFactory; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; + +/** + * ServletContextListener that is used to install a custom authorization policy. + * + * @author Arjan Tijms + * + */ +@WebListener +public class PolicyRegistrationListener implements ServletContextListener { + + @Override + public void contextInitialized(ServletContextEvent sce) { + PolicyFactory policyFactory = PolicyFactory.getPolicyFactory(); + policyFactory.setPolicy(new TestPolicy(policyFactory.getPolicy())); + } + +} \ No newline at end of file diff --git a/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java b/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java new file mode 100644 index 0000000..0e712cf --- /dev/null +++ b/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/ProtectedServlet.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.test; + +import jakarta.annotation.security.DeclareRoles; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.HttpConstraint; +import jakarta.servlet.annotation.ServletSecurity; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Protected Servlet that prints out the name of the authenticated caller and whether + * this caller is in any of the roles {foo, bar, kaz} + * + *

+ * The role "kaz" is required to access this Servlet. "bar" is a role assigned by the + * TestIdentityStore. + * + */ +@WebServlet("/protectedServlet/*") +@DeclareRoles({"foo", "bar"}) +@ServletSecurity(@HttpConstraint(rolesAllowed = "kaz")) +public class ProtectedServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + response.getWriter().write("This is a servlet \n"); + + String webName = null; + if (request.getUserPrincipal() != null) { + webName = request.getUserPrincipal().getName(); + } + + response.getWriter().write("web username: " + webName + "\n"); + + response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n"); + response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n"); + response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n"); + + + } + +} diff --git a/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java b/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java new file mode 100644 index 0000000..1d0778d --- /dev/null +++ b/tck/app-policy2/src/main/java/ee/jakarta/tck/authorization/test/TestPolicy.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package ee.jakarta.tck.authorization.test; + +import static jakarta.security.jacc.PolicyContext.PRINCIPAL_MAPPER; + +import jakarta.security.jacc.Policy; +import jakarta.security.jacc.PolicyContext; +import jakarta.security.jacc.PolicyContextException; +import jakarta.security.jacc.PrincipalMapper; +import jakarta.security.jacc.WebResourcePermission; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Principal; +import java.util.logging.Logger; +import javax.security.auth.Subject; + +/** + * Policy implementation that uses a custom permission check + * to grant access to {@code /protectedServlet/[*]/test} to + * the *authenticated* caller. + */ +public class TestPolicy implements Policy { + + private static final Logger LOGGER = Logger.getLogger(TestPolicy.class.getName()); + + private final Policy originalPolicy; + + public TestPolicy(Policy policy) { + this.originalPolicy = policy; + } + + @Override + public boolean implies(Permission permissionToBeChecked, Subject subject) { + LOGGER.info(permissionToBeChecked.toString()); + LOGGER.info(subject.toString()); + + Principal callerPrincipal = getCallerPrincipal(subject); + + LOGGER.info(callerPrincipal == null? "null" : callerPrincipal.toString() + " " + callerPrincipal.getName()); + + // First try our custom permission checking, but only for authenticated callers + if (isAuthenticated(callerPrincipal) && impliesCustom(permissionToBeChecked)) { + return true; + } + + // If custom doesn't grant access, try the original policy so we + // keep all normal checks in place. + return originalPolicy.implies(permissionToBeChecked, subject); + } + + @Override + public PermissionCollection getPermissionCollection(Subject subject) { + return originalPolicy.getPermissionCollection(subject); + } + + private Principal getCallerPrincipal(Subject subject) { + try { + // Use the PrincipalMapper to retrieve the caller principal + // that should be somewhere in the Subject + + PrincipalMapper principalMapper = PolicyContext.getContext(PRINCIPAL_MAPPER); + + return principalMapper.getCallerPrincipal(subject); + + } catch (PolicyContextException e) { + throw new IllegalStateException(e); + } + } + + private boolean isAuthenticated(Principal callerPrincipal) { + return callerPrincipal != null && callerPrincipal.getName() != null; + } + + private boolean impliesCustom(Permission permissionToBeChecked) { + return + permissionToBeChecked instanceof WebResourcePermission && + permissionToBeChecked.getName().startsWith("/protectedServlet/") && + permissionToBeChecked.getName().endsWith("/test"); + } + +} diff --git a/tck/app-policy2/src/main/webapp/WEB-INF/beans.xml b/tck/app-policy2/src/main/webapp/WEB-INF/beans.xml new file mode 100644 index 0000000..7a54976 --- /dev/null +++ b/tck/app-policy2/src/main/webapp/WEB-INF/beans.xml @@ -0,0 +1,24 @@ + + + + \ No newline at end of file diff --git a/tck/app-policy2/src/main/webapp/WEB-INF/web.xml b/tck/app-policy2/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..f6223ae --- /dev/null +++ b/tck/app-policy2/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,28 @@ + + + + + + BASIC + file + + diff --git a/tck/app-policy2/src/test/java/ee/jakarta/tck/authorization/test/AppPolicy2IT.java b/tck/app-policy2/src/test/java/ee/jakarta/tck/authorization/test/AppPolicy2IT.java new file mode 100644 index 0000000..b793bb4 --- /dev/null +++ b/tck/app-policy2/src/test/java/ee/jakarta/tck/authorization/test/AppPolicy2IT.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.test; + +import static ee.jakarta.tck.authorization.util.Assert.assertDefaultAccess; +import static ee.jakarta.tck.authorization.util.Assert.assertDefaultNoAccess; +import static ee.jakarta.tck.authorization.util.ShrinkWrap.mavenWar; + +import com.gargoylesoftware.htmlunit.DefaultCredentialsProvider; +import ee.jakarta.tck.authorization.util.ArquillianBase; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.junit.Test; +import org.junit.runner.RunWith; + + +@RunWith(Arquillian.class) +public class AppPolicy2IT extends ArquillianBase { + + @Deployment(testable = false) + public static Archive createDeployment() { + return mavenWar(); + } + + // Test several general conditions to make sure security + // works in the normal way + + /** + * Normally authenticated for a request to the default path. + * Should not have access, since not in role "kaz" + */ + @Test + public void testAuthenticated() { + DefaultCredentialsProvider credentialsProvider = new DefaultCredentialsProvider(); + credentialsProvider.addCredentials("reza", "secret1"); + + getWebClient().setCredentialsProvider(credentialsProvider); + + assertDefaultNoAccess( + readFromServer("/protectedServlet")); + } + + /** + * Not authenticated on the default path. + * Should not have access, since not in the required role "kaz" + */ + @Test + public void testNotAuthenticated() { + assertDefaultNoAccess( + readFromServer("/protectedServlet")); + } + + /** + * Wrongly authenticated on the default path. + * Should not have access, since not in the required role "kaz" + */ + @Test + public void testNotAuthenticatedWrongName() { + DefaultCredentialsProvider credentialsProvider = new DefaultCredentialsProvider(); + credentialsProvider.addCredentials("romo", "secret1"); + + getWebClient().setCredentialsProvider(credentialsProvider); + + assertDefaultNoAccess( + readFromServer("/protectedServlet")); + } + + + // Test on the special test path which a custom policy is observing + + /** + * Should not have access, since the custom policy checks for any authenticated caller, + * and we don't authenticate here. + * + */ + @Test + public void testNotAuthenticatedSpecial() { + String response = readFromServer("/protectedServlet/foo/test"); + + assertDefaultNoAccess(response); + } + + /** + * Should have access, since the custom policy checks for any authenticated caller, + * and we authenticate here. + * + */ + @Test + public void testAuthenticatedSpecial() { + DefaultCredentialsProvider credentialsProvider = new DefaultCredentialsProvider(); + credentialsProvider.addCredentials("reza", "secret1"); + + getWebClient().setCredentialsProvider(credentialsProvider); + + String response = readFromServer("/protectedServlet/foo/test"); + + assertDefaultAccess(response); + } + +} diff --git a/tck/common/pom.xml b/tck/common/pom.xml new file mode 100644 index 0000000..744c7ff --- /dev/null +++ b/tck/common/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + + org.eclipse.ee4j.authorization.tck + jakarta-authorization-tck + 4.0.0-SNAPSHOT + + + common + jar + + + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.0 + provided + + + + junit + junit + provided + + + + org.jboss.arquillian.junit + arquillian-junit-container + provided + + + + net.sourceforge.htmlunit + htmlunit + 2.70.0 + provided + + + diff --git a/tck/common/src/main/java/ee/jakarta/tck/authorization/util/ArquillianBase.java b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/ArquillianBase.java new file mode 100644 index 0000000..90d4d31 --- /dev/null +++ b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/ArquillianBase.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.util; + +import static java.util.logging.Level.SEVERE; +import static org.apache.http.HttpStatus.SC_MULTIPLE_CHOICES; +import static org.apache.http.HttpStatus.SC_OK; +import static org.jsoup.Jsoup.parse; +import static org.jsoup.parser.Parser.xmlParser; + +import java.io.IOException; +import java.net.URL; +import java.util.logging.Logger; + +import org.jboss.arquillian.test.api.ArquillianResource; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import com.gargoylesoftware.htmlunit.DefaultCssErrorHandler; +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebResponse; + +public class ArquillianBase { + + private static final Logger logger = Logger.getLogger(ArquillianBase.class.getName()); + + private WebClient webClient; + private String response; + private String responsePath; + + @ArquillianResource + private URL base; + + @Rule + public TestWatcher ruleExample = new TestWatcher() { + @Override + protected void failed(Throwable e, Description description) { + super.failed(e, description); + + logger.log(SEVERE, + "\n\nTest failed: " + + description.getClassName() + "." + description.getMethodName() + + + "\nMessage: " + e.getMessage() + + + "\nLast response: " + + + "\n\n" + formatHTML(response) + "\n\n"); + + } + }; + + @Before + public void setUp() { + Logger logger = Logger.getLogger(DefaultCssErrorHandler.class.getName()); + logger.setLevel(SEVERE); + + response = null; + webClient = new WebClient() { + + private static final long serialVersionUID = 1L; + + @Override + public void printContentIfNecessary(WebResponse webResponse) { + int statusCode = webResponse.getStatusCode(); + if (getOptions().isPrintContentOnFailingStatusCode() && !(statusCode >= SC_OK && statusCode < SC_MULTIPLE_CHOICES)) { + logger.log(SEVERE, webResponse.getWebRequest().getUrl().toExternalForm()); + } + super.printContentIfNecessary(webResponse); + } + }; + webClient.getOptions().setThrowExceptionOnFailingStatusCode(false); + if (System.getProperty("glassfish.suspend") != null) { + webClient.getOptions().setTimeout(0); + } + } + + @After + public void tearDown() { + webClient.getCookieManager().clearCookies(); + webClient.close(); + } + + protected String readFromServer(String path) { + response = ""; + WebResponse localResponse = responseFromServer(path); + if (localResponse != null) { + response = localResponse.getContentAsString(); + } + + return response; + } + + protected WebResponse responseFromServer(String path) { + + WebResponse webResponse = null; + + Page page = pageFromServer(path); + if (page != null) { + webResponse = page.getWebResponse(); + if (webResponse != null) { + response = webResponse.getContentAsString(); + } + } + + return webResponse; + } + + protected

P pageFromServer(String path) { + if (base.toString().endsWith("/") && path.startsWith("/")) { + path = path.substring(1); + } + + try { + response = ""; + + P page = webClient.getPage(base + path); + + if (page != null) { + WebResponse localResponse = page.getWebResponse(); + responsePath = page.getUrl().toString(); + if (localResponse != null) { + response = localResponse.getContentAsString(); + + if (System.getProperty("tck.log.response") != null) { + printLastResponse(); + } + } + } + + return page; + + } catch (FailingHttpStatusCodeException | IOException e) { + throw new IllegalStateException(e); + } + } + + protected void printLastResponse() { + logger.info( + "\n\n" + + "Requested path:\n" + responsePath + + "\n\n" + + + "Response :\n" + formatHTML(response) + + "\n\n\n"); + } + + protected void printPage(Page page) { + if (page != null) { + WebResponse localResponse = page.getWebResponse(); + responsePath = page.getUrl().toString(); + if (localResponse != null) { + response = localResponse.getContentAsString(); + } + + printLastResponse(); + } + } + + protected WebClient getWebClient() { + return webClient; + } + + public static String formatHTML(String html) { + try { + return parse(html, "", xmlParser()).toString(); + } catch (Exception e) { + return html; + } + } + +} diff --git a/tck/common/src/main/java/ee/jakarta/tck/authorization/util/Assert.java b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/Assert.java new file mode 100644 index 0000000..4ef03f8 --- /dev/null +++ b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/Assert.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Contributors to the Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public final class Assert { + + public static void assertDefaultNoAccess(String response) { + assertFalse( + "Should not have access to servlet, but had access.\n" + + response, + response.contains("This is a servlet")); + } + + public static void assertDefaultAccess(String response) { + assertTrue( + "Should have access to servlet, but had no access.\n" + + response, + response.contains("This is a servlet")); + } + + public static void assertDefaultAuthenticated(String response) { + assertAuthenticated("web", "reza", response, "foo", "bar"); + } + + public static void assertDefaultNotAuthenticated(String response) { + assertNotAuthenticated("web", "reza", response, "foo", "bar"); + } + + public static void assertAuthenticated(String userType, String name, String response, String... roles) { + assertTrue( + "Should be authenticated as user " + name + " but was not \n Response: \n" + + response + "\n search: " + userType + " username: " + name, + response.contains(userType + " username: " + name)); + + for (String role : roles) { + assertTrue( + "Authenticated user should have role \"" + role + "\", but did not \n Response: \n" + + response, + response.contains(userType + " user has role \"" + role + "\": true")); + } + } + + public static void assertNotAuthenticated(String userType, String name, String response, String... roles) { + assertFalse( + "Should not be authenticated as user " + name + " but was \n Response: \n" + + response + "\n search: " + userType + " username: " + name, + response.contains(userType + " username: " + name)); + + for (String role : roles) { + assertFalse( + "Authenticated user should not have role \"" + role + "\", but did \n Response: \n" + + response, + response.contains(userType + " user has role \"" + role + "\": true")); + } + } + +} diff --git a/tck/common/src/main/java/ee/jakarta/tck/authorization/util/ShrinkWrap.java b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/ShrinkWrap.java new file mode 100644 index 0000000..f3f541d --- /dev/null +++ b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/ShrinkWrap.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.util; + +import static java.lang.System.getProperty; +import static org.jboss.shrinkwrap.api.ShrinkWrap.create; + +import java.io.File; + +import org.jboss.shrinkwrap.api.importer.ZipImporter; +import org.jboss.shrinkwrap.api.spec.WebArchive; + +public class ShrinkWrap { + + public static WebArchive mavenWar() { + return + create(ZipImporter.class, getProperty("finalName") + ".war") + .importFrom(new File("target/" + getProperty("finalName") + ".war")) + .as(WebArchive.class); + } + +} diff --git a/tck/common/src/main/java/ee/jakarta/tck/authorization/util/Utils.java b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/Utils.java new file mode 100644 index 0000000..ddd5cdd --- /dev/null +++ b/tck/common/src/main/java/ee/jakarta/tck/authorization/util/Utils.java @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2024, 2024 Contributors to the Eclipse Foundation. + * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package ee.jakarta.tck.authorization.util; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.regex.Pattern.quote; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.xml.bind.DatatypeConverter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * An assortment of various utility methods. + * + * @author Arjan Tijms + * + */ +public final class Utils { + + private static final String ERROR_UNSUPPORTED_ENCODING = "UTF-8 is apparently not supported on this platform."; + + private Utils() {} + + public static boolean notNull(Object... objects) { + for (Object object : objects) { + if (object == null) { + return false; + } + } + + return true; + } + + /** + * Returns true if the given string is null or is empty. + * + * @param string The string to be checked on emptiness. + * @return True if the given string is null or is empty. + */ + public static boolean isEmpty(String string) { + return string == null || string.isEmpty(); + } + + /** + * Returns true if the given array is null or is empty. + * + * @param array The array to be checked on emptiness. + * @return true if the given array is null or is empty. + */ + public static boolean isEmpty(Object[] array) { + return array == null || array.length == 0; + } + + /** + * Returns true if the given collection is null or is empty. + * + * @param collection The collection to be checked on emptiness. + * @return true if the given collection is null or is empty. + */ + public static boolean isEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } + + /** + * Returns true if the given object equals one of the given objects. + * @param The generic object type. + * @param object The object to be checked if it equals one of the given objects. + * @param objects The argument list of objects to be tested for equality. + * @return true if the given object equals one of the given objects. + */ + @SafeVarargs + public static boolean isOneOf(T object, T... objects) { + for (Object other : objects) { + if (object == null ? other == null : object.equals(other)) { + return true; + } + } + + return false; + } + + public static String getBaseURL(HttpServletRequest request) { + String url = request.getRequestURL().toString(); + return url.substring(0, url.length() - request.getRequestURI().length()) + request.getContextPath(); + } + + public static void redirect(HttpServletResponse response, String location) { + try { + response.sendRedirect(location); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public static void redirect(HttpServletRequest request, HttpServletResponse response, String location) { + try { + if (isFacesAjaxRequest(request)) { + response.setHeader("Cache-Control", "no-cache,no-store,must-revalidate"); + response.setDateHeader("Expires", 0); + response.setHeader("Pragma", "no-cache"); // Backwards compatibility for HTTP 1.0. + response.setContentType("text/xml"); + response.setCharacterEncoding(UTF_8.name()); + response.getWriter().printf(FACES_AJAX_REDIRECT_XML, location); + } + else { + response.sendRedirect(location); + } + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static final Set FACES_AJAX_HEADERS = unmodifiableSet("partial/ajax", "partial/process"); + private static final String FACES_AJAX_REDIRECT_XML = "" + + ""; + + public static boolean isFacesAjaxRequest(HttpServletRequest request) { + return FACES_AJAX_HEADERS.contains(request.getHeader("Faces-Request")); + } + + @SuppressWarnings("unchecked") + public static Set unmodifiableSet(Object... values) { + Set set = new HashSet<>(); + + for (Object value : values) { + if (value instanceof Object[]) { + for (Object item : (Object[]) value) { + set.add((E) item); + } + } + else if (value instanceof Collection) { + for (Object item : (Collection) value) { + set.add((E) item); + } + } + else { + set.add((E) value); + } + } + + return Collections.unmodifiableSet(set); + } + + public static String encodeURL(String string) { + if (string == null) { + return null; + } + + try { + return URLEncoder.encode(string, UTF_8.name()); + } + catch (UnsupportedEncodingException e) { + throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ENCODING, e); + } + } + + public static String decodeURL(String string) { + if (string == null) { + return null; + } + + try { + return URLDecoder.decode(string, UTF_8.name()); + } + catch (UnsupportedEncodingException e) { + throw new UnsupportedOperationException(ERROR_UNSUPPORTED_ENCODING, e); + } + } + + public static String getSingleParameterFromState(String state, String paramName) { + Map> requestStateParameters = getParameterMapFromState(state); + + List parameterValues = requestStateParameters.get(paramName); + if (!isEmpty(parameterValues)) { + return parameterValues.get(0); + } + + return null; + } + + public static Map> getParameterMapFromState(String state) { + return toParameterMap(unserializeURLSafe(state)); + } + + /** + * Converts the given request query string to request parameter values map. + * @param queryString The request query string. + * @return The request query string as request parameter values map. + */ + public static Map> toParameterMap(String queryString) { + String[] parameters = queryString.split(quote("&")); + Map> parameterMap = new LinkedHashMap<>(parameters.length); + + for (String parameter : parameters) { + if (parameter.contains("=")) { + String[] pair = parameter.split(quote("=")); + String key = decodeURL(pair[0]); + String value = (pair.length > 1 && !isEmpty(pair[1])) ? decodeURL(pair[1]) : ""; + List values = parameterMap.computeIfAbsent(key, k -> new ArrayList<>(1)); + + values.add(value); + } + } + + return parameterMap; + } + + /** + * Converts the given request parameter values map to request query string. + * @param parameterMap The request parameter values map. + * @return The request parameter values map as request query string. + */ + public static String toQueryString(Map> parameterMap) { + StringBuilder queryString = new StringBuilder(); + + for (Entry> entry : parameterMap.entrySet()) { + String name = encodeURL(entry.getKey()); + + for (String value : entry.getValue()) { + if (queryString.length() > 0) { + queryString.append("&"); + } + + queryString.append(name).append("=").append(encodeURL(value)); + } + } + + return queryString.toString(); + } + + public static String getSingleParameterFromQueryString(String queryString, String paramName) { + if (!isEmpty(queryString)) { + Map> requestParameters = toParameterMap(queryString); + + if (!isEmpty(requestParameters.get(paramName))) { + return requestParameters.get(paramName).get(0); + } + } + + return null; + } + + /** + * Serialize the given string to the short possible unique URL-safe representation. The current implementation will + * decode the given string with UTF-8 and then compress it with ZLIB using "best compression" algorithm and then + * Base64-encode the resulting bytes without the = padding, whereafter the Base64 characters + * + and / are been replaced by respectively - and _ to make it + * URL-safe (so that no platform-sensitive URL-encoding needs to be done when used in URLs). + * @param string The string to be serialized. + * @return The serialized URL-safe string, or null when the given string is itself null. + */ + public static String serializeURLSafe(String string) { + if (string == null) { + return null; + } + + try { + InputStream raw = new ByteArrayInputStream(string.getBytes(UTF_8)); + ByteArrayOutputStream deflated = new ByteArrayOutputStream(); + stream(raw, new DeflaterOutputStream(deflated, new Deflater(Deflater.BEST_COMPRESSION))); + String base64 = DatatypeConverter.printBase64Binary(deflated.toByteArray()); + return base64.replace('+', '-').replace('/', '_').replace("=", ""); + } + catch (IOException e) { + // This will occur when ZLIB and/or UTF-8 are not supported, but this is not to be expected these days. + throw new RuntimeException(e); + } + } + + /** + * Unserialize the given serialized URL-safe string. This does the inverse of {@link #serializeURLSafe(String)}. + * @param string The serialized URL-safe string to be unserialized. + * @return The unserialized string, or null when the given string is by itself null. + * @throws IllegalArgumentException When the given serialized URL-safe string is not in valid format as returned by + * {@link #serializeURLSafe(String)}. + */ + public static String unserializeURLSafe(String string) { + if (string == null) { + return null; + } + + try { + String base64 = string.replace('-', '+').replace('_', '/') + "===".substring(0, string.length() % 4); + InputStream deflated = new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(base64)); + return new String(toByteArray(new InflaterInputStream(deflated)), UTF_8); + } + catch (UnsupportedEncodingException e) { + // This will occur when UTF-8 is not supported, but this is not to be expected these days. + throw new RuntimeException(e); + } + catch (Exception e) { + // This will occur when the string is not in valid Base64 or ZLIB format. + throw new IllegalArgumentException(e); + } + } + + public static long stream(InputStream input, OutputStream output) throws IOException { + try (ReadableByteChannel inputChannel = Channels.newChannel(input); + WritableByteChannel outputChannel = Channels.newChannel(output)) + { + ByteBuffer buffer = ByteBuffer.allocateDirect(10240); + long size = 0; + + while (inputChannel.read(buffer) != -1) { + buffer.flip(); + size += outputChannel.write(buffer); + buffer.clear(); + } + + return size; + } + } + + /** + * Read the given input stream into a byte array. The given input stream will implicitly be closed after streaming, + * regardless of whether an exception is been thrown or not. + * @param input The input stream. + * @return The input stream as a byte array. + * @throws IOException When an I/O error occurs. + */ + public static byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + stream(input, output); + return output.toByteArray(); + } + + public static boolean isImplementationOf(Method implementationMethod, Method interfaceMethod) { + return + interfaceMethod.getDeclaringClass().isAssignableFrom(implementationMethod.getDeclaringClass()) && + interfaceMethod.getName().equals(implementationMethod.getName()) && + Arrays.equals(interfaceMethod.getParameterTypes(), implementationMethod.getParameterTypes()); + } + + public static Method getMethod(Class base, String name, Class... parameterTypes) { + try { + // Method literals in Java would be nice + return base.getMethod(name, parameterTypes); + } catch (NoSuchMethodException | SecurityException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/tck/pom.xml b/tck/pom.xml new file mode 100644 index 0000000..66404ca --- /dev/null +++ b/tck/pom.xml @@ -0,0 +1,346 @@ + + + + + 4.0.0 + + + org.eclipse.ee4j + project + 1.0.9 + + + + org.eclipse.ee4j.authorization.tck + jakarta-authorization-tck + 4.0.0-SNAPSHOT + + pom + + Jakarta Authorization TCK - main + + + + common + + + app-policy + app-policy2 + + + + 17 + + ${project.artifactId}-${project.version} + ${project.basedir}/LICENSE_EFTL.md + + 4.0.0-SNAPSHOT + + ${project.basedir}/target + + + + + + org.jboss.arquillian + arquillian-bom + 1.8.0.Final + pom + import + + + org.jboss.arquillian.container + arquillian-container-test-api + 1.8.0.Final + + + org.omnifaces.arquillian + glassfish-client-ee10 + 1.4 + test + + + + + + jakarta.authorization + jakarta.authorization-api + 3.0.0-SNAPSHOT + provided + + + + jakarta.servlet + jakarta.servlet-api + 6.1.0-M2 + provided + + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + + + + + + + junit + junit + test + + + + org.jboss.arquillian.junit + arquillian-junit-container + test + + + + org.jboss.arquillian.protocol + arquillian-protocol-servlet + test + + + + net.sourceforge.htmlunit + htmlunit + 2.70.0 + test + + + + org.jsoup + jsoup + 1.17.2 + + + + + ${buildDirectory} + + + + + org.apache.maven.plugins + maven-war-plugin + 3.4.0 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.6.1 + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.5 + + + org.apache.maven.plugins + maven-site-plugin + 4.0.0-M13 + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-maven + + enforce + + + + + 3.9.0 + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + ${project.build.finalName} + + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 3.2.5 + + + post-integration-test + + failsafe-report-only + + + + + true + + + + + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 3.2.5 + + ${skipSurefireReport} + true + + + + + + + + + + + glassfish-ci-managed + + + true + + + + 8.0.0-SNAPSHOT + 8080 + false + ${maven.multiModuleProjectDirectory}/target + + + + + + org.omnifaces.arquillian + arquillian-glassfish-server-managed + 1.4 + test + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + process-test-classes + + unpack + + + ${skipITs} + ${glassfish.root} + ${glassfish.root}/dependency-maven-plugin-markers + + + org.glassfish.main.distributions + glassfish + ${glassfish.version} + zip + true + ${glassfish.root} + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.0.0 + + + set-port + pre-integration-test + + run + + + ${skipITs} + + Replacing in ${glassfish.root} + + + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + ${glassfish.root}/glassfish8 + + create-file-user --groups foo:bar --passwordfile ${maven.multiModuleProjectDirectory}/reza.pass reza + + + + + + + + + + diff --git a/tck/reza.pass b/tck/reza.pass new file mode 100644 index 0000000..70b5394 --- /dev/null +++ b/tck/reza.pass @@ -0,0 +1,16 @@ +# +# Copyright (c) 2024, 2024 Contributors to the Eclipse Foundation. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# +AS_ADMIN_USERPASSWORD=secret1 \ No newline at end of file