From 80f5b25c799217282aea79671745839f8774cb90 Mon Sep 17 00:00:00 2001 From: Jan Martiska Date: Fri, 3 Mar 2017 14:33:00 +0100 Subject: [PATCH] [WFLY-8278] Tests for Elytron+Batch subsystem integration --- .../batch/BatchSubsystemSecurityTestCase.java | 392 ++++++++++++++++++ .../elytron/batch/FailingBatchlet.java | 51 +++ .../elytron/batch/IdentityBatchlet.java | 51 +++ .../elytron/batch/LongRunningBatchlet.java | 55 +++ .../batch/SecurityDomainSettingEJB.java | 39 ++ .../elytron/batch/assert-identity.xml | 8 + .../elytron/batch/failing-batchlet.xml | 12 + .../elytron/batch/long-running-batchlet.xml | 8 + .../common/AbstractElytronSetupTask.java | 12 +- .../AbstractUserRolesSecurityDomain.java | 9 + .../EJBApplicationSecurityDomainMapping.java | 55 +++ .../common/elytron/PermissionMapper.java | 49 +++ .../elytron/PropertyFileBasedDomain.java | 20 +- .../elytron/SimplePermissionMapper.java | 291 +++++++++++++ 14 files changed, 1034 insertions(+), 18 deletions(-) create mode 100644 testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/BatchSubsystemSecurityTestCase.java create mode 100644 testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/FailingBatchlet.java create mode 100644 testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/IdentityBatchlet.java create mode 100644 testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/LongRunningBatchlet.java create mode 100644 testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/SecurityDomainSettingEJB.java create mode 100644 testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/assert-identity.xml create mode 100644 testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/failing-batchlet.xml create mode 100644 testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/long-running-batchlet.xml create mode 100644 testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/EJBApplicationSecurityDomainMapping.java create mode 100644 testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PermissionMapper.java create mode 100644 testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/SimplePermissionMapper.java diff --git a/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/BatchSubsystemSecurityTestCase.java b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/BatchSubsystemSecurityTestCase.java new file mode 100644 index 000000000000..36ab7b9884cb --- /dev/null +++ b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/BatchSubsystemSecurityTestCase.java @@ -0,0 +1,392 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.integration.elytron.batch; + +import java.security.AllPermission; +import java.util.Properties; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javax.batch.operations.JobOperator; +import javax.batch.operations.JobSecurityException; +import javax.batch.runtime.BatchRuntime; +import javax.batch.runtime.BatchStatus; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.as.arquillian.api.ServerSetup; +import org.jboss.as.arquillian.api.ServerSetupTask; +import org.jboss.as.arquillian.container.ManagementClient; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.test.shared.ServerReload; +import org.jboss.as.test.shared.TimeoutUtil; +import org.jboss.as.test.shared.integration.ejb.security.PermissionUtils; +import org.jboss.dmr.ModelNode; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.wildfly.extension.batch.jberet.deployment.BatchPermission; +import org.wildfly.security.auth.permission.LoginPermission; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityDomain; +import org.wildfly.security.auth.server.SecurityIdentity; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.test.security.common.AbstractElytronSetupTask; +import org.wildfly.test.security.common.elytron.ConfigurableElement; +import org.wildfly.test.security.common.elytron.EJBApplicationSecurityDomainMapping; +import org.wildfly.test.security.common.elytron.PropertyFileBasedDomain; +import org.wildfly.test.security.common.elytron.SimplePermissionMapper; + +import static org.jboss.as.controller.client.helpers.ClientConstants.ADDRESS; +import static org.jboss.as.controller.client.helpers.ClientConstants.OP; +import static org.jboss.as.controller.client.helpers.ClientConstants.UNDEFINE_ATTRIBUTE_OPERATION; +import static org.jboss.as.controller.client.helpers.ClientConstants.WRITE_ATTRIBUTE_OPERATION; + +/** + * This is for testing the BatchPermission from batch-jberet subsystem. + * It also checks that when running a Batch job as a particular user, the security identity can be retrieved + * within the job's code. + * + * The security setup is like this: + * user1/password1 -> can do everything + * user2/password2 -> can stop jobs only + * user3/password3 -> can read jobs only + * + * @author Jan Martiska + */ +@ServerSetup({BatchSubsystemSecurityTestCase.CreateBatchSecurityDomainSetupTask.class, + BatchSubsystemSecurityTestCase.ActivateBatchSecurityDomainSetupTask.class}) +@RunWith(Arquillian.class) +public class BatchSubsystemSecurityTestCase { + + static final String BATCH_SECURITY_DOMAIN_NAME = "BatchDomain"; + + @Deployment + public static Archive createDeployment() { + final JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "batch-test.jar"); + jar.addClasses(AbstractElytronSetupTask.class, + SecurityDomainSettingEJB.class, + TimeoutUtil.class, + IdentityBatchlet.class, + FailingBatchlet.class, + LongRunningBatchlet.class); + jar.addAsManifestResource(BatchSubsystemSecurityTestCase.class.getPackage(), + "assert-identity.xml", + "batch-jobs/assert-identity.xml"); + jar.addAsManifestResource(BatchSubsystemSecurityTestCase.class.getPackage(), + "failing-batchlet.xml", + "batch-jobs/failing-batchlet.xml"); + jar.addAsManifestResource(BatchSubsystemSecurityTestCase.class.getPackage(), + "long-running-batchlet.xml", + "batch-jobs/long-running-batchlet.xml"); + jar.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); + jar.addAsManifestResource(PermissionUtils.createPermissionsXmlAsset(new AllPermission()), "permissions.xml"); + return jar; + } + + // this is the identityWithinJob using which the batch job was invoked + // the job itself completes this future when it runs + static volatile CompletableFuture identityWithinJob; + + private JobOperator operator; + + @Before + public void before() { + operator = BatchRuntime.getJobOperator(); + } + + + /** + * Try running a job as a user who has the permission to run jobs. It should succeed. + * The job should also be able to retrieve the name of the user who ran it. + */ + @Test + public void testStart_Allowed() throws Exception { + identityWithinJob = new CompletableFuture<>(); + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + user1.runAs((Callable)() -> operator.start("assert-identity", new Properties())); + final String actualUsername = identityWithinJob.get(TimeoutUtil.adjust(20), TimeUnit.SECONDS); + Assert.assertEquals("user1", actualUsername); + } + + /** + * Try running a job as a user who doesn't have the permission to run jobs. It should not succeed. + */ + @Test + public void testStart_NotAllowed() throws Exception { + final SecurityIdentity user2 = getSecurityIdentity("user2", "password2"); + try { + user2.runAs((Callable)() -> operator.start("assert-identity", new Properties())); + Assert.fail("user2 shouldn't be allowed to start batch jobs"); + } catch(JobSecurityException e) { + // OK + } + } + + /** + * Test reading execution metadata by a user who has the permission to do it. + * User1 runs a job and then user2 tries to read its metadata. + */ + @Test + public void testRead_Allowed() throws Exception { + final Properties jobParams = new Properties(); + jobParams.put("prop1", "val1"); + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final SecurityIdentity user3 = getSecurityIdentity("user3", "password3"); + final Long executionId = user1 + .runAs((Callable)() -> operator.start("assert-identity", jobParams)); + final Properties retrievedParams = user3 + .runAs((Callable)() -> operator.getJobExecution(executionId).getJobParameters()); + Assert.assertEquals(jobParams, retrievedParams); + } + + /** + * Test reading execution metadata by a user who doesn't have the permission to do it. + * User1 runs a job and then user2 tries to read its metadata. + */ + @Test + public void testRead_NotAllowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final SecurityIdentity user2 = getSecurityIdentity("user2", "password2"); + final Long executionId = user1 + .runAs((Callable)() -> operator.start("assert-identity", new Properties())); + try { + user2.runAs((Callable)() -> operator.getJobExecution(executionId).getJobParameters()); + Assert.fail("user2 shouldn't be allowed to read batch job metadata"); + } catch(JobSecurityException e) { + // OK + } + } + + /** + * Test restarting failed jobs by a user who has the permission to do it. + */ + @Test + public void testRestart_Allowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + Properties params = new Properties(); + params.put("should.fail", "true"); + final Long executionId = user1 + .runAs((Callable)() -> operator.start("failing-batchlet", params)); + waitForJobEnd(executionId, 10); + Assert.assertEquals(BatchStatus.FAILED, operator.getJobExecution(executionId).getBatchStatus()); + params.put("should.fail", "false"); + final Long executionIdAfterRestart = user1 + .runAs((Callable)() -> operator.restart(executionId, params)); + waitForJobEnd(executionIdAfterRestart, 10); + Assert.assertEquals(BatchStatus.COMPLETED, operator.getJobExecution(executionIdAfterRestart).getBatchStatus()); + } + + /** + * Test restarting failed jobs by a user who doesn't have the permission to do it. + */ + @Test + public void testRestart_NotAllowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final SecurityIdentity user2 = getSecurityIdentity("user2", "password2"); + Properties params = new Properties(); + params.put("should.fail", "true"); + final Long executionId = user1 + .runAs((Callable)() -> operator.start("failing-batchlet", params)); + waitForJobEnd(executionId, 10); + Assert.assertEquals(BatchStatus.FAILED, operator.getJobExecution(executionId).getBatchStatus()); + try { + user2.runAs((Callable)() -> operator.restart(executionId, params)); + Assert.fail("user2 shouldn't be allowed to restart batch jobs"); + } catch(JobSecurityException e) { + // OK + } + } + + + /** + * Abandoning an execution by a user who has the permission to do it. + */ + @Test + public void testAbandon_Allowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final Long id = user1.runAs((Callable)() -> operator.start("assert-identity", new Properties())); + waitForJobEnd(id, 10); + user1.runAs(() -> operator.abandon(id)); + Assert.assertEquals(operator.getJobExecution(id).getBatchStatus(), BatchStatus.ABANDONED); + } + + /** + * Abandoning an execution by a user who doesn't have the permission to do it. + */ + @Test + public void testAbandon_NotAllowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final SecurityIdentity user2 = getSecurityIdentity("user2", "password2"); + final Long id = user1.runAs((Callable)() -> operator.start("assert-identity", new Properties())); + waitForJobEnd(id, 10); + try { + user2.runAs(() -> operator.abandon(id)); + Assert.fail("user2 should not be allowed to abandon job executions"); + } catch(JobSecurityException e) { + // OK + } + Assert.assertEquals(operator.getJobExecution(id).getBatchStatus(), BatchStatus.COMPLETED); + } + + /** + * Stopping an execution by a user who doesn't have the permission to do it. + */ + @Test + public void testStop_NotAllowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final SecurityIdentity user3 = getSecurityIdentity("user3", "password3"); + final Long id = user1.runAs((Callable)() -> operator.start("long-running-batchlet", null)); + TimeUnit.SECONDS.sleep(1); + try { + user3.runAs(() -> operator.stop(id)); + Assert.fail("user2 should not be allowed to stop job executions"); + } catch(JobSecurityException e) { + // OK + } + Assert.assertNotEquals(BatchStatus.STOPPED, operator.getJobExecution(id).getBatchStatus()); + } + + /** + * Stopping an execution by a user who has the permission to do it. + */ + @Test + public void testStop_Allowed() throws Exception { + final SecurityIdentity user1 = getSecurityIdentity("user1", "password1"); + final Long id = user1.runAs((Callable)() -> operator.start("long-running-batchlet", null)); + TimeUnit.SECONDS.sleep(1); + user1.runAs(() -> operator.stop(id)); + waitForJobEnd(id, 10); + Assert.assertEquals(BatchStatus.STOPPED, operator.getJobExecution(id).getBatchStatus()); + } + + + private void waitForJobEnd(Long id, int timeoutSeconds) throws TimeoutException { + Long start = System.currentTimeMillis(); + final JobOperator operator = BatchRuntime.getJobOperator(); + while(System.currentTimeMillis() - start < (TimeoutUtil.adjust(timeoutSeconds) * 1000)) { + if(operator.getJobExecution(id).getEndTime() != null) + return; + } + throw new TimeoutException(); + } + + private static SecurityIdentity getSecurityIdentity(String username, String password) + throws RealmUnavailableException { + return SecurityDomain.getCurrent().authenticate(username, new PasswordGuessEvidence(password.toCharArray())); + } + + static class CreateBatchSecurityDomainSetupTask extends AbstractElytronSetupTask { + @Override + protected ConfigurableElement[] getConfigurableElements() { + return new ConfigurableElement[] { + SimplePermissionMapper.builder().withName("batch-permission-mapper") + .permissionMappings( + SimplePermissionMapper.PermissionMapping.builder() + .withPrincipals("user1", "anonymous") + .withPermissions( + SimplePermissionMapper.Permission.builder() + .targetName("*") + .className(BatchPermission.class.getName()) + .module("org.wildfly.extension.batch.jberet") + .build(), + SimplePermissionMapper.Permission.builder() + .className(LoginPermission.class.getName()) + .build()) + .build(), + SimplePermissionMapper.PermissionMapping.builder() + .withPrincipals("user2") + .withPermissions( + SimplePermissionMapper.Permission.builder() + .targetName("stop") + .className(BatchPermission.class.getName()) + .module("org.wildfly.extension.batch.jberet") + .build(), + SimplePermissionMapper.Permission.builder() + .className(LoginPermission.class.getName()) + .build()) + .build(), + SimplePermissionMapper.PermissionMapping.builder() + .withPrincipals("user3") + .withPermissions( + SimplePermissionMapper.Permission.builder() + .targetName("read") + .className(BatchPermission.class.getName()) + .module("org.wildfly.extension.batch.jberet") + .build(), + SimplePermissionMapper.Permission.builder() + .className(LoginPermission.class.getName()) + .build()) + .build() + ).build(), + PropertyFileBasedDomain.builder().withName(BATCH_SECURITY_DOMAIN_NAME) + .permissionMapper("batch-permission-mapper") + .withUser("user1", "password1") + .withUser("user2", "password2") + .withUser("user3", "password3") + .build(), + new EJBApplicationSecurityDomainMapping(BATCH_SECURITY_DOMAIN_NAME, BATCH_SECURITY_DOMAIN_NAME) + }; + } + } + + static class ActivateBatchSecurityDomainSetupTask implements ServerSetupTask { + + final ModelNode BATCH_SUBSYSTEM_ADDRESS = PathAddress.pathAddress("subsystem", "batch-jberet") + .toModelNode(); + + @Override + public void setup(ManagementClient managementClient, String s) throws Exception { + final ModelNode setOp = new ModelNode(); + setOp.get(OP).set(WRITE_ATTRIBUTE_OPERATION); + setOp.get(ADDRESS).set(BATCH_SUBSYSTEM_ADDRESS); + setOp.get("name").set("security-domain"); + setOp.get("value").set(BATCH_SECURITY_DOMAIN_NAME); + + final ModelNode result = managementClient.getControllerClient().execute(setOp); + Assert.assertTrue(result.get("outcome").asString().equals("success")); + + ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient()); + } + + @Override + public void tearDown(ManagementClient managementClient, String s) throws Exception { + final ModelNode undefineOp = new ModelNode(); + undefineOp.get(OP).set(UNDEFINE_ATTRIBUTE_OPERATION); + undefineOp.get(ADDRESS).set(BATCH_SUBSYSTEM_ADDRESS); + undefineOp.get("name").set("security-domain"); + + final ModelNode result = managementClient.getControllerClient().execute(undefineOp); + Assert.assertTrue(result.get("outcome").asString().equals("success")); + + ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient()); + } + + } +} diff --git a/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/FailingBatchlet.java b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/FailingBatchlet.java new file mode 100644 index 000000000000..7095f56f33d3 --- /dev/null +++ b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/FailingBatchlet.java @@ -0,0 +1,51 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.integration.elytron.batch; + +import javax.batch.api.BatchProperty; +import javax.batch.api.Batchlet; +import javax.inject.Inject; +import javax.inject.Named; + +/** + * @author Jan Martiska + */ +@Named +public class FailingBatchlet implements Batchlet { + + @Inject + @BatchProperty(name = "should.fail") + private Boolean shouldFail; + + @Override + public String process() throws Exception { + if(shouldFail) + throw new Exception("failing the job on purpose"); + return "OK"; + } + + @Override + public void stop() throws Exception { + + } +} diff --git a/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/IdentityBatchlet.java b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/IdentityBatchlet.java new file mode 100644 index 000000000000..695a7ce49172 --- /dev/null +++ b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/IdentityBatchlet.java @@ -0,0 +1,51 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.integration.elytron.batch; + +import javax.batch.api.Batchlet; +import javax.inject.Named; + +import org.jboss.logging.Logger; +import org.wildfly.security.auth.server.SecurityDomain; + +/** + * @author Jan Martiska + */ +@Named +public class IdentityBatchlet implements Batchlet { + + private Logger logger = Logger.getLogger(IdentityBatchlet.class); + + @Override + public String process() throws Exception { + final String name = SecurityDomain.getCurrent().getCurrentSecurityIdentity().getPrincipal().getName(); + logger.info("Batchlet running as username: " + name); + BatchSubsystemSecurityTestCase.identityWithinJob.complete(name); + return "OK"; + } + + @Override + public void stop() throws Exception { + + } +} diff --git a/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/LongRunningBatchlet.java b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/LongRunningBatchlet.java new file mode 100644 index 000000000000..eb53f0919e17 --- /dev/null +++ b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/LongRunningBatchlet.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.integration.elytron.batch; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import javax.batch.api.Batchlet; +import javax.batch.runtime.context.JobContext; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jboss.as.test.shared.TimeoutUtil; + +/** + * @author Jan Martiska + */ +@Named +public class LongRunningBatchlet implements Batchlet { + + private final CompletableFuture SHOULD_STOP = new CompletableFuture<>(); + + @Inject + JobContext ctx; + + @Override + public String process() throws Exception { + SHOULD_STOP.get(TimeoutUtil.adjust(10), TimeUnit.SECONDS); + return null; + } + + @Override + public void stop() throws Exception { + SHOULD_STOP.complete(null); + } +} diff --git a/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/SecurityDomainSettingEJB.java b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/SecurityDomainSettingEJB.java new file mode 100644 index 000000000000..425ae887555b --- /dev/null +++ b/testsuite/elytron/src/test/java/org/wildfly/test/integration/elytron/batch/SecurityDomainSettingEJB.java @@ -0,0 +1,39 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.integration.elytron.batch; + +import javax.ejb.LocalBean; +import javax.ejb.Stateless; + +import org.jboss.ejb3.annotation.SecurityDomain; + +/** + * This is here as just a hack to be able to set the security domain of a deployment. + * See https://issues.jboss.org/browse/JBEAP-8702 discussion for more context. + * @author Jan Martiska + */ +@Stateless +@SecurityDomain("BatchDomain") +@LocalBean +public class SecurityDomainSettingEJB { +} diff --git a/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/assert-identity.xml b/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/assert-identity.xml new file mode 100644 index 000000000000..ea0f521990d0 --- /dev/null +++ b/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/assert-identity.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/failing-batchlet.xml b/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/failing-batchlet.xml new file mode 100644 index 000000000000..3d30f265878a --- /dev/null +++ b/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/failing-batchlet.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/long-running-batchlet.xml b/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/long-running-batchlet.xml new file mode 100644 index 000000000000..1bf48e78d57f --- /dev/null +++ b/testsuite/elytron/src/test/resources/org/wildfly/test/integration/elytron/batch/long-running-batchlet.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/testsuite/shared/src/main/java/org/wildfly/test/security/common/AbstractElytronSetupTask.java b/testsuite/shared/src/main/java/org/wildfly/test/security/common/AbstractElytronSetupTask.java index dca7107890d0..172c6afd7431 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/security/common/AbstractElytronSetupTask.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/security/common/AbstractElytronSetupTask.java @@ -38,7 +38,7 @@ * @author Josef Cacek */ public abstract class AbstractElytronSetupTask - implements org.wildfly.core.testrunner.ServerSetupTask, org.jboss.as.arquillian.api.ServerSetupTask { + implements org.jboss.as.arquillian.api.ServerSetupTask { private static final Logger LOGGER = Logger.getLogger(AbstractElytronSetupTask.class); @@ -56,16 +56,6 @@ public void tearDown(final org.jboss.as.arquillian.container.ManagementClient ma tearDown(managementClient.getControllerClient()); } - @Override - public void setup(org.wildfly.core.testrunner.ManagementClient managementClient) throws Exception { - setup(managementClient.getControllerClient()); - } - - @Override - public void tearDown(org.wildfly.core.testrunner.ManagementClient managementClient) throws Exception { - tearDown(managementClient.getControllerClient()); - } - /** * Creates configuration elements (provided by implementation of {@link #getConfigurableElements()} method) and calls * {@link ConfigurableElement#create(CLIWrapper)} for them. diff --git a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/AbstractUserRolesSecurityDomain.java b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/AbstractUserRolesSecurityDomain.java index 5a268a4b58df..2651fe564b61 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/AbstractUserRolesSecurityDomain.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/AbstractUserRolesSecurityDomain.java @@ -37,9 +37,12 @@ public abstract class AbstractUserRolesSecurityDomain extends AbstractConfigurab private final List usersWithRoles; + protected final String permissionMapper; + protected AbstractUserRolesSecurityDomain(Builder builder) { super(builder); this.usersWithRoles = Collections.unmodifiableList(new ArrayList<>(builder.usersWithRoles)); + this.permissionMapper = builder.permMapper; } @Override @@ -52,6 +55,7 @@ public List getUsersWithRoles() { */ public abstract static class Builder> extends AbstractConfigurableElement.Builder { private List usersWithRoles = new ArrayList<>(); + private String permMapper; protected Builder() { } @@ -78,6 +82,11 @@ public final T withUser(String username, String password, String... roles) { return self(); } + public final T permissionMapper(String name) { + permMapper = name; + return self(); + } + } } diff --git a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/EJBApplicationSecurityDomainMapping.java b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/EJBApplicationSecurityDomainMapping.java new file mode 100644 index 000000000000..b73a9c9dda3d --- /dev/null +++ b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/EJBApplicationSecurityDomainMapping.java @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.security.common.elytron; + +import org.jboss.as.test.integration.management.util.CLIWrapper; + +/** + * @author Jan Martiska + */ +public class EJBApplicationSecurityDomainMapping implements ConfigurableElement { + + private final String appDomain; + private final String elytronDomain; + + + public EJBApplicationSecurityDomainMapping(String appDomain, String elytronDomain) { + this.appDomain = appDomain; + this.elytronDomain = elytronDomain; + } + + @Override + public String getName() { + return appDomain; + } + + @Override + public void create(CLIWrapper cli) throws Exception { + cli.sendLine("/subsystem=ejb3/application-security-domain="+ appDomain +":add(security-domain="+elytronDomain+")"); + } + + @Override + public void remove(CLIWrapper cli) throws Exception { + cli.sendLine("/subsystem=ejb3/application-security-domain="+ appDomain +":remove"); + } +} diff --git a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PermissionMapper.java b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PermissionMapper.java new file mode 100644 index 000000000000..ce4c09a966c9 --- /dev/null +++ b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PermissionMapper.java @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.security.common.elytron; + +/** + * @author Jan Martiska + */ +public interface PermissionMapper extends ConfigurableElement { + + enum MappingMode { + AND("and"), + FIRST("first"), + OR("or"), + UNLESS("unless"), + XOR("xor"); + + MappingMode(String string) { + this.string = string; + } + + private String string; + + @Override + public String toString() { + return string; + } + } + +} diff --git a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PropertyFileBasedDomain.java b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PropertyFileBasedDomain.java index e3f0184a3912..5ece2703179a 100644 --- a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PropertyFileBasedDomain.java +++ b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/PropertyFileBasedDomain.java @@ -79,21 +79,27 @@ public void create(CLIWrapper cli) throws Exception { // /subsystem=elytron/simple-role-decoder=test:add(attribute=groups) cli.sendLine(String.format("/subsystem=elytron/simple-role-decoder=%s:add(attribute=groups)", name)); - // /subsystem=elytron/constant-permission-mapper=test:add(permissions=[{class-name="org.wildfly.security.auth.permission.LoginPermission"}]) - cli.sendLine(String.format("/subsystem=elytron/constant-permission-mapper=%s:add(permissions=[{class-name=\"%s\"}])", - name, LoginPermission.class.getName())); + if(permissionMapper == null) { // create a default permission mapper if a custom one wasn't specified + // /subsystem=elytron/constant-permission-mapper=test:add(permissions=[{class-name="org.wildfly.security.auth.permission.LoginPermission"}]) + cli.sendLine(String.format("/subsystem=elytron/constant-permission-mapper=%s:add(permissions=[{class-name=\"%s\"}])", + name, LoginPermission.class.getName())); + } + - // /subsystem=elytron/security-domain=test:add(default-realm=test, permission-mapper=test, realms=[{role-decoder=test, + final String permissionMapperName = permissionMapper == null ? name : permissionMapper; + // /subsystem=elytron/security-domain=test:add(default-realm=test, permission-mapper=PERMISSION_MAPPER, realms=[{role-decoder=test, // realm=test}] cli.sendLine(String.format( - "/subsystem=elytron/security-domain=%s:add(default-realm=%1$s, permission-mapper=%1$s, realms=[{role-decoder=%1$s, realm=%1$s}]", - name)); + "/subsystem=elytron/security-domain=%s:add(default-realm=%1$s, permission-mapper=%2$s, realms=[{role-decoder=%1$s, realm=%1$s}]", + name, permissionMapperName)); } @Override public void remove(CLIWrapper cli) throws Exception { cli.sendLine(String.format("/subsystem=elytron/security-domain=%s:remove()", name)); - cli.sendLine(String.format("/subsystem=elytron/constant-permission-mapper=%s:remove()", name)); + if(permissionMapper == null) { + cli.sendLine(String.format("/subsystem=elytron/constant-permission-mapper=%s:remove()", name)); + } cli.sendLine(String.format("/subsystem=elytron/simple-role-decoder=%s:remove()", name)); cli.sendLine(String.format("/subsystem=elytron/properties-realm=%s:remove()", name)); diff --git a/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/SimplePermissionMapper.java b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/SimplePermissionMapper.java new file mode 100644 index 000000000000..24631b0c0fd9 --- /dev/null +++ b/testsuite/shared/src/main/java/org/wildfly/test/security/common/elytron/SimplePermissionMapper.java @@ -0,0 +1,291 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.wildfly.test.security.common.elytron; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.jboss.as.test.integration.management.util.CLIWrapper; + +/** + * @author Jan Martiska + */ +public class SimplePermissionMapper extends AbstractConfigurableElement implements PermissionMapper { + + private final List mappings; + private final MappingMode mappingMode; + + private SimplePermissionMapper(Builder builder) { + super(builder); + this.mappings = builder.mappings; + this.mappingMode = builder.mappingMode; + } + + @Override + public void create(CLIWrapper cli) throws Exception { + final StringBuilder cliCommand = new StringBuilder(); + cliCommand.append("/subsystem=elytron/simple-permission-mapper=") + .append(name) + .append(":add("); + if (mappingMode != null) { + cliCommand.append("mapping-mode=").append(mappingMode.toString()).append(","); + } + cliCommand.append("permission-mappings=["); + + cliCommand.append(mappings + .stream() + .map(PermissionMapping::toCLIString) + .collect(Collectors.joining(","))); + + cliCommand.append("]"); + cliCommand.append(")"); + + cli.sendLine(cliCommand.toString()); + } + + @Override + public void remove(CLIWrapper cli) throws Exception { + cli.sendLine("/subsystem=elytron/simple-permission-mapper=" + name + ":remove"); + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder to build {@link UndertowSslContext}. The name attribute refers to ssl-context capability name. + */ + public static final class Builder + extends AbstractConfigurableElement.Builder { + + private MappingMode mappingMode; + private List mappings; + + public Builder() { + mappings = new ArrayList<>(); + } + + @Override + protected Builder self() { + return this; + } + + public SimplePermissionMapper build() { + return new SimplePermissionMapper(this); + } + + public Builder mappingMode(MappingMode mappingMode) { + this.mappingMode = mappingMode; + return this; + } + + public Builder permissionMapping(PermissionMapping mapping) { + this.mappings.add(mapping); + return this; + } + + public Builder permissionMappings(PermissionMapping... mappings) { + this.mappings.addAll(Arrays.asList(mappings)); + return this; + } + + } + + public static final class Permission { + private final String className; + private final String module; + private final String targetName; + private final String action; + + public Permission(Builder builder) { + this.className = builder.className; + this.module = builder.module; + this.targetName = builder.targetName; + this.action = builder.action; + } + + public String getClassName() { + return className; + } + + public String getModule() { + return module; + } + + public String getTargetName() { + return targetName; + } + + public String getAction() { + return action; + } + + public static Builder builder() { + return new Builder(); + } + + + public String toCLIString() { + StringBuilder result = new StringBuilder(); + result.append("{"); + List arguments = new ArrayList<>(); + if (action != null) { + arguments.add("action=" + action); + } + if (module != null) { + arguments.add("module=" + module); + } + if (targetName != null) { + arguments.add("target-name=" + targetName); + } + if (className != null) { + arguments.add("class-name=" + className); + } + result.append(StringUtils.join(arguments, ",")); + result.append("}"); + return result.toString(); + } + + public static class Builder { + private String className; + private String module; + private String targetName; + private String action; + + public Builder() { + } + + public Builder className(String className) { + this.className = className; + return this; + } + + public Builder module(String module) { + this.module = module; + return this; + } + + public Builder targetName(String targetName) { + this.targetName = targetName; + return this; + } + + public Builder action(String action) { + this.action = action; + return this; + } + + public Permission build() { + // TODO add null checks here + return new Permission(this); + } + } + + } + + public static final class PermissionMapping { + + private final List permissions; + private final List principals; + private final List roles; + + private PermissionMapping(Builder builder) { + this.permissions = builder.permissions; + this.principals = builder.principals; + this.roles = builder.roles; + } + + public List getPermissions() { + return permissions; + } + + public List getPrincipals() { + return principals; + } + + public List getRoles() { + return roles; + } + + public static Builder builder() { + return new Builder(); + } + + public String toCLIString() { + StringBuilder result = new StringBuilder(); + result.append("{permissions=["); + result.append(permissions.stream() + .map(Permission::toCLIString) + .collect(Collectors.joining(","))); + result.append("]"); + if (principals.size() > 0) { + result.append(",principals=["); + result.append(StringUtils.join(principals, ",")); + result.append("]"); + } + if (roles.size() > 0) { + result.append(",roles=["); + result.append(StringUtils.join(roles, ",")); + result.append("]"); + } + result.append("}"); + return result.toString(); + } + + public static final class Builder { + + private List permissions; + private List principals; + private List roles; + + public Builder() { + permissions = new ArrayList<>(); + principals = new ArrayList<>(); + roles = new ArrayList<>(); + } + + public Builder withPermissions(Permission... permissions) { + this.permissions.addAll(Arrays.asList(permissions)); + return this; + } + + public Builder withPrincipals(String... principals) { + this.principals.addAll(Arrays.asList(principals)); + return this; + } + + public Builder withRoles(String... roles) { + this.roles.addAll(Arrays.asList(roles)); + return this; + } + + public PermissionMapping build() { + return new PermissionMapping(this); + } + + } + } +}