/* * JBoss, Home of Professional Open Source. * Copyright 2013, 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.jboss.as.test.integration.mgmt.access; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.BYTES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIBE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PASSWORD; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CHILDREN_NAMES_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CONFIG_AS_XML_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SHUTDOWN; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TYPE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UPLOAD_DEPLOYMENT_BYTES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERNAME; import static org.jboss.as.test.integration.management.rbac.RbacUtil.ADMINISTRATOR_USER; import static org.jboss.as.test.integration.management.rbac.RbacUtil.AUDITOR_USER; import static org.jboss.as.test.integration.management.rbac.RbacUtil.DEPLOYER_USER; import static org.jboss.as.test.integration.management.rbac.RbacUtil.MAINTAINER_USER; import static org.jboss.as.test.integration.management.rbac.RbacUtil.MONITOR_USER; import static org.jboss.as.test.integration.management.rbac.RbacUtil.OPERATOR_USER; import static org.jboss.as.test.integration.management.rbac.RbacUtil.SUPERUSER_USER; import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.nio.charset.Charset; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.operations.common.Util; import org.jboss.as.test.integration.management.interfaces.ManagementInterface; import org.jboss.as.test.integration.management.rbac.Outcome; import org.jboss.as.test.integration.management.rbac.RbacUtil; import org.jboss.as.test.integration.management.util.MgmtOperationException; import org.jboss.dmr.ModelNode; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.wildfly.core.testrunner.ManagementClient; /** * Basic tests of the standard RBAC roles. * * @author Brian Stansberry (c) 2013 Red Hat Inc. */ public abstract class StandardRolesBasicTestCase extends AbstractManagementInterfaceRbacTestCase { private static final String DEPLOYMENT_1 = "deployment=war-example.war"; private static final String DEPLOYMENT_2 = "deployment=rbac.txt"; private static final byte[] DEPLOYMENT_2_CONTENT = "CONTENT".getBytes(Charset.defaultCharset()); private static final String AUTHORIZATION = "core-service=management/access=authorization"; private static final String ROLE_MAPPING_BASE = "core-service=management/access=authorization/role-mapping="; private static final String MANAGEMENT_REALM = "core-service=management/security-realm=ManagementRealm"; private static final String HTTP_BINDING = "socket-binding-group=standard-sockets/socket-binding=management-http"; private static final String MEMORY_MBEAN = "core-service=platform-mbean/type=memory"; protected static final String EXAMPLE_DS = "subsystem=rbac/rbac-constrained=default"; private static final String TEST_PATH = "path=rbac.test"; private static final ModelNode WFLY_1916_OP; static { WFLY_1916_OP = Util.createEmptyOperation(UPLOAD_DEPLOYMENT_BYTES, PathAddress.EMPTY_ADDRESS); WFLY_1916_OP.get(BYTES).set(new byte[64]); WFLY_1916_OP.protect(); } protected static void deployDeployment1(ManagementClient client) throws IOException { ModelNode op = createOpNode(DEPLOYMENT_1, ADD); op.get(ENABLED).set(false); ModelNode content = op.get(CONTENT).add(); content.get(BYTES).set(DEPLOYMENT_2_CONTENT); RbacUtil.executeOperation(client.getControllerClient(), op, Outcome.SUCCESS); } protected static void removeDeployment1(ManagementClient client) throws IOException { ModelNode op = createOpNode(DEPLOYMENT_1, REMOVE); RbacUtil.executeOperation(client.getControllerClient(), op, Outcome.SUCCESS); } @Before public void deploy() throws IOException, MgmtOperationException { deployDeployment1(getManagementClient()); } @After public void tearDown() throws IOException, MgmtOperationException { removeDeployment1(getManagementClient()); AssertionError assertionError = null; try { removeResource(DEPLOYMENT_2); } catch (AssertionError e) { assertionError = e; } finally { removeResource(TEST_PATH); } if (assertionError != null) { throw assertionError; } } @Test public void testMonitor() throws Exception { ManagementInterface client = getClientForUser(MONITOR_USER); whoami(client, MONITOR_USER); readWholeConfig(client, Outcome.UNAUTHORIZED); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.HIDDEN); readResource(client, MANAGEMENT_REALM, Outcome.HIDDEN); checkSensitiveAttribute(client, false); runGC(client, Outcome.UNAUTHORIZED); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } addDeployment2(client, Outcome.UNAUTHORIZED); addPath(client, Outcome.UNAUTHORIZED); testWFLY1916(client, Outcome.UNAUTHORIZED); // Monitor can't shutdown testWCORE1067(client); } @Test public void testOperator() throws Exception { ManagementInterface client = getClientForUser(OPERATOR_USER); whoami(client, OPERATOR_USER); readWholeConfig(client, Outcome.UNAUTHORIZED); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.HIDDEN); readResource(client, MANAGEMENT_REALM, Outcome.HIDDEN); checkSensitiveAttribute(client, false); runGC(client, Outcome.SUCCESS); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } addDeployment2(client, Outcome.UNAUTHORIZED); addPath(client, Outcome.UNAUTHORIZED); testWFLY1916(client, Outcome.SUCCESS); } @Test public void testMaintainer() throws Exception { ManagementInterface client = getClientForUser(MAINTAINER_USER); whoami(client, MAINTAINER_USER); readWholeConfig(client, Outcome.UNAUTHORIZED); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.HIDDEN); readResource(client, MANAGEMENT_REALM, Outcome.HIDDEN); checkSensitiveAttribute(client, false); runGC(client, Outcome.SUCCESS); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } addDeployment2(client, Outcome.SUCCESS); addPath(client, Outcome.SUCCESS); testWFLY1916(client, Outcome.SUCCESS); } @Test public void testDeployer() throws Exception { ManagementInterface client = getClientForUser(DEPLOYER_USER); whoami(client, DEPLOYER_USER); readWholeConfig(client, Outcome.UNAUTHORIZED); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.HIDDEN); readResource(client, MANAGEMENT_REALM, Outcome.HIDDEN); checkSensitiveAttribute(client, false); runGC(client, Outcome.UNAUTHORIZED); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } addDeployment2(client, Outcome.SUCCESS); addPath(client, Outcome.UNAUTHORIZED); testWFLY1916(client, Outcome.SUCCESS); // Deployer can't shutdown testWCORE1067(client); } @Test public void testAdministrator() throws Exception { ManagementInterface client = getClientForUser(ADMINISTRATOR_USER); whoami(client, ADMINISTRATOR_USER); readWholeConfig(client, Outcome.SUCCESS); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.SUCCESS); readResource(client, MANAGEMENT_REALM, Outcome.SUCCESS); checkSensitiveAttribute(client, true); runGC(client, Outcome.SUCCESS); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } modifyAccessibleRoles(client, RbacUtil.MONITOR_ROLE, RbacUtil.OPERATOR_ROLE, RbacUtil.MAINTAINER_ROLE, RbacUtil.ADMINISTRATOR_ROLE, RbacUtil.DEPLOYER_ROLE); modifyInaccessibleRoles(client, RbacUtil.AUDITOR_ROLE, RbacUtil.SUPERUSER_ROLE); addDeployment2(client, Outcome.SUCCESS); addPath(client, Outcome.SUCCESS); testWFLY1916(client, Outcome.SUCCESS); } @Test public void testAuditor() throws Exception { ManagementInterface client = getClientForUser(AUDITOR_USER); whoami(client, AUDITOR_USER); readWholeConfig(client, Outcome.SUCCESS); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.SUCCESS); readResource(client, MANAGEMENT_REALM, Outcome.SUCCESS); checkSensitiveAttribute(client, true); runGC(client, Outcome.UNAUTHORIZED); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } modifyInaccessibleRoles(client, RbacUtil.allStandardRoles()); addDeployment2(client, Outcome.UNAUTHORIZED); addPath(client, Outcome.UNAUTHORIZED); testWFLY1916(client, Outcome.UNAUTHORIZED); // Auditor can't shutdown testWCORE1067(client); } @Test public void testSuperUser() throws Exception { ManagementInterface client = getClientForUser(SUPERUSER_USER); whoami(client, SUPERUSER_USER); readWholeConfig(client, Outcome.SUCCESS); checkStandardReads(client); readResource(client, AUTHORIZATION, Outcome.SUCCESS); readResource(client, MANAGEMENT_REALM, Outcome.SUCCESS); checkSensitiveAttribute(client, true); runGC(client, Outcome.SUCCESS); if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; // the 'add' operation is not implemented in JmxManagementInterface } modifyAccessibleRoles(client, RbacUtil.allStandardRoles()); addDeployment2(client, Outcome.SUCCESS); addPath(client, Outcome.SUCCESS); testWFLY1916(client, Outcome.SUCCESS); } private static void whoami(ManagementInterface client, String expectedUsername) throws IOException { ModelNode op = createOpNode(null, "whoami"); ModelNode result = RbacUtil.executeOperation(client, op, Outcome.SUCCESS); String returnedUsername = result.get(RESULT, "identity", USERNAME).asString(); assertEquals(expectedUsername, returnedUsername); } private void readWholeConfig(ManagementInterface client, Outcome expectedOutcome) throws IOException { ModelNode op = createOpNode(null, READ_CONFIG_AS_XML_OPERATION); RbacUtil.executeOperation(client, op, expectedOutcome); // the code below calls the non-published operation 'describe'; see WFLY-2379 for more info // once that issue is fixed, the test will only make sense for native mgmt interface // (or maybe not even that) if (this instanceof JmxInterfaceStandardRolesBasicTestCase) { return; } op = createOpNode(null, READ_CHILDREN_NAMES_OPERATION); op.get(CHILD_TYPE).set(SUBSYSTEM); ModelNode subsystems = RbacUtil.executeOperation(getManagementClient().getControllerClient(), op, Outcome.SUCCESS); for (ModelNode subsystem : subsystems.get(RESULT).asList()) { op = createOpNode("subsystem=" + subsystem.asString(), DESCRIBE); ModelNode result = RbacUtil.executeOperation(client, op, expectedOutcome); assertEquals(expectedOutcome == Outcome.SUCCESS, result.hasDefined(RESULT)); } } private static void checkStandardReads(ManagementInterface client) throws IOException { readResource(client, null, Outcome.SUCCESS); readResource(client, "core-service=platform-mbean/type=runtime", Outcome.SUCCESS); readResource(client, HTTP_BINDING, Outcome.SUCCESS); } private static ModelNode readResource(ManagementInterface client, String address, Outcome expectedOutcome) throws IOException { ModelNode op = createOpNode(address, READ_RESOURCE_OPERATION); return RbacUtil.executeOperation(client, op, expectedOutcome); } private static ModelNode readAttribute(ManagementInterface client, String address, String attributeName, Outcome expectedOutcome) throws IOException { ModelNode op = createOpNode(address, READ_ATTRIBUTE_OPERATION); op.get(NAME).set(attributeName); return RbacUtil.executeOperation(client, op, expectedOutcome); } private static void checkSensitiveAttribute(ManagementInterface client, boolean expectSuccess) throws IOException { ModelNode correct = new ModelNode(); if (expectSuccess) { correct.set("sa"); } ModelNode attrValue = readResource(client, EXAMPLE_DS, Outcome.SUCCESS).get(RESULT, PASSWORD); assertEquals(correct, attrValue); attrValue = readAttribute(client, EXAMPLE_DS, PASSWORD, expectSuccess ? Outcome.SUCCESS : Outcome.UNAUTHORIZED).get(RESULT); assertEquals(correct, attrValue); } private static void runGC(ManagementInterface client, Outcome expectedOutcome) throws IOException { ModelNode op = createOpNode(MEMORY_MBEAN, "gc"); RbacUtil.executeOperation(client, op, expectedOutcome); } private static void addDeployment2(ManagementInterface client, Outcome expectedOutcome) throws IOException { ModelNode op = createOpNode(DEPLOYMENT_2, ADD); op.get(ENABLED).set(false); ModelNode content = op.get(CONTENT).add(); content.get(BYTES).set(DEPLOYMENT_2_CONTENT); RbacUtil.executeOperation(client, op, expectedOutcome); } private static void addPath(ManagementInterface client, Outcome expectedOutcome) throws IOException { ModelNode op = createOpNode(TEST_PATH, ADD); op.get(PATH).set("/"); RbacUtil.executeOperation(client, op, expectedOutcome); } private void removeResource(String address) throws IOException { ModelNode op = createOpNode(address, READ_RESOURCE_OPERATION); ModelNode result = getManagementClient().getControllerClient().execute(op); if (SUCCESS.equals(result.get(OUTCOME).asString())) { op = createOpNode(address, REMOVE); result = getManagementClient().getControllerClient().execute(op); assertEquals(result.asString(), SUCCESS, result.get(OUTCOME).asString()); } } private static void modifyAccessibleRoles(ManagementInterface client, String... roleNames) throws IOException { for (String current : roleNames) { addRemoveIncldueForRole(client, current, true); } } private static void modifyInaccessibleRoles(ManagementInterface client, String... roleNames) throws IOException { for (String current : roleNames) { addRemoveIncldueForRole(client, current, false); } } private static void addRemoveIncldueForRole(final ManagementInterface client, final String roleName, boolean accessible) throws IOException { String includeAddress = ROLE_MAPPING_BASE + roleName + "/include=temp"; ModelNode add = createOpNode(includeAddress, ADD); add.get(NAME).set("temp"); add.get(TYPE).set(USER); RbacUtil.executeOperation(client, add, accessible ? Outcome.SUCCESS : Outcome.UNAUTHORIZED); if (accessible) { ModelNode remove = createOpNode(includeAddress, REMOVE); RbacUtil.executeOperation(client, remove, Outcome.SUCCESS); } } private void testWFLY1916(ManagementInterface client, Outcome expected) throws IOException { ModelNode op = WFLY_1916_OP.clone(); RbacUtil.executeOperation(client, op, expected); } private void testWCORE1067(ManagementInterface client) throws IOException { ModelNode op = Util.createEmptyOperation(SHUTDOWN, PathAddress.EMPTY_ADDRESS); RbacUtil.executeOperation(client, op, Outcome.UNAUTHORIZED); } }