/*
*
* * 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.jmx.auditlog;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.QueryExp;
import org.jboss.as.controller.CompositeOperationHandler;
import org.jboss.as.controller.ManagementModel;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.ResourceDefinition;
import org.jboss.as.controller.RunningMode;
import org.jboss.as.controller.RunningModeControl;
import org.jboss.as.controller.access.management.DelegatingConfigurableAuthorizer;
import org.jboss.as.controller.access.management.ManagementSecurityIdentitySupplier;
import org.jboss.as.controller.audit.ManagedAuditLogger;
import org.jboss.as.controller.audit.ManagedAuditLoggerImpl;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.extension.ExtensionRegistry;
import org.jboss.as.controller.extension.ExtensionRegistryType;
import org.jboss.as.controller.extension.RuntimeHostControllerInfoAccessor;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.operations.global.GlobalNotifications;
import org.jboss.as.controller.operations.global.GlobalOperationHandlers;
import org.jboss.as.controller.persistence.NullConfigurationPersister;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.services.path.PathManagerService;
import org.jboss.as.controller.services.path.PathResourceDefinition;
import org.jboss.as.domain.management.CoreManagementResourceDefinition;
import org.jboss.as.domain.management.audit.AccessAuditResourceDefinition;
import org.jboss.as.domain.management.audit.AuditLogLoggerResourceDefinition;
import org.jboss.as.domain.management.audit.EnvironmentNameReader;
import org.jboss.as.jmx.ExposeModelResourceResolved;
import org.jboss.as.jmx.JMXExtension;
import org.jboss.as.jmx.JMXSubsystemRootResource;
import org.jboss.as.jmx.JmxAuditLogHandlerReferenceResourceDefinition;
import org.jboss.as.jmx.JmxAuditLoggerResourceDefinition;
import org.jboss.as.jmx.MBeanServerService;
import org.jboss.as.jmx.test.util.AbstractControllerTestBase;
import org.jboss.as.server.jmx.PluggableMBeanServer;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Don't use core-model test for this. It does not support runtime, and more importantly for backwards compatibility the audit logger cannot be used
*
* @author Kabir Khan
*/
public class JmxAuditLogHandlerTestCase extends AbstractControllerTestBase {
volatile PathManagerService pathManagerService;
volatile ManagedAuditLogger auditLogger;
volatile File logDir;
volatile MBeanServer server;
private static final String ANY_PLACEHOLDER = "$$$ ANY $$$ ANY $$$";
private static final ObjectName OBJECT_NAME;
static {
try {
OBJECT_NAME = new ObjectName("test:name=bean");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final List<ModelNode> bootOperations = new ArrayList<ModelNode>();
public JmxAuditLogHandlerTestCase() {
bootOperations.add(Util.createAddOperation(PathAddress.pathAddress(CoreManagementResourceDefinition.PATH_ELEMENT, AccessAuditResourceDefinition.PATH_ELEMENT)));
ModelNode add = Util.createAddOperation(
PathAddress.pathAddress(
CoreManagementResourceDefinition.PATH_ELEMENT,
AccessAuditResourceDefinition.PATH_ELEMENT,
PathElement.pathElement(ModelDescriptionConstants.JSON_FORMATTER, "test-formatter")));
bootOperations.add(add);
bootOperations.add(createAddFileHandlerOperation("test-file", "test-formatter", "test-file.log"));
add = Util.createAddOperation(
PathAddress.pathAddress(
CoreManagementResourceDefinition.PATH_ELEMENT,
AccessAuditResourceDefinition.PATH_ELEMENT,
AuditLogLoggerResourceDefinition.PATH_ELEMENT));
add.get(ModelDescriptionConstants.LOG_READ_ONLY).set(true);
bootOperations.add(add);
PathAddress address = PathAddress.pathAddress(JMXSubsystemRootResource.PATH_ELEMENT);
add = Util.createAddOperation(address);
bootOperations.add(add);
add = Util.createAddOperation(address.append(ExposeModelResourceResolved.PATH_ELEMENT));
add.get(ExposeModelResourceResolved.DOMAIN_NAME.getName()).set("wildfly.test");
bootOperations.add(add);
address = address.append(JmxAuditLoggerResourceDefinition.PATH_ELEMENT);
add = Util.createAddOperation(address);
add.get(ModelDescriptionConstants.LOG_READ_ONLY).set(true);
bootOperations.add(add);
bootOperations.add(createAddJmxHandlerReferenceOperation("test-file"));
}
@Before
public void installMBeans() throws Exception {
server = getMBeanServer();
server.registerMBean(new Bean(), OBJECT_NAME);
}
@After
public void clearDependencies() throws Exception {
auditLogger = null;
logDir = null;
if (server.isRegistered(OBJECT_NAME)) {
server.unregisterMBean(OBJECT_NAME);
}
server = null;
}
protected ManagedAuditLogger getAuditLogger(){
if (auditLogger == null){
auditLogger = new ManagedAuditLoggerImpl("8.0.0", true);
}
return auditLogger;
}
@Test
public void testAuditLoggerBootUp() throws Exception {
File file = new File(logDir, "test-file.log");
List<ModelNode> bootRecords = readFile(file, 1);
ModelNode bootRecord = bootRecords.get(0);
bootRecord = bootRecords.get(0);
checkJmxBootRecordHeader(bootRecord, false, new String[] {Object.class.getName(), ObjectName.class.getName()}, new String[] {ANY_PLACEHOLDER, OBJECT_NAME.toString()});
}
@Test
public void testAuditLoggerAddAndRemoveJmxReference() throws Exception {
File file = new File(logDir, "test-file.log");
readFile(file, 1);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
List<ModelNode> records = readFile(file, 2);
checkJmxBootRecordHeader(records.get(1), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
ModelNode op = createRemoveJmxHandlerReferenceOperation("test-file");
executeForResult(op);
readFile(file, 2);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 2);
op = createAddJmxHandlerReferenceOperation("test-file");
executeForResult(op);
readFile(file, 2);
//File gets recreated and backed up here
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 1);
checkJmxBootRecordHeader(records.get(0), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
}
@Test
public void testSameFileCoreAndJmxAuditLog() throws Exception {
File file = new File(logDir, "test-file.log");
readFile(file, 1); //Tested in boo
ModelNode op = createAddCoreHandlerReferenceOperation("test-file");
executeForResult(op);
List<ModelNode> records = readFile(file, 2);
List<ModelNode> ops = checkCoreBootRecordHeader(records.get(1), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 3);
ops = checkCoreBootRecordHeader(records.get(2), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 4);
checkJmxBootRecordHeader(records.get(3), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
//Remove and add the jmx handler reference, making sure that the core stuff still gets logged
op = createRemoveJmxHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 5);
ops = checkCoreBootRecordHeader(records.get(4), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 5);
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 6);
ops = checkCoreBootRecordHeader(records.get(5), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
op = createAddJmxHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 7);
ops = checkCoreBootRecordHeader(records.get(6), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Remove and add the core handler reference, making sure that the jmx stuff still gets logged
op = createRemoveCoreHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 8);
ops = checkCoreBootRecordHeader(records.get(7), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 8);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 9);
checkJmxBootRecordHeader(records.get(8), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = createAddCoreHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 10);
ops = checkCoreBootRecordHeader(records.get(9), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Now remove all handler references and make sure that the file gets recycled when adding the jmx reference
op = createRemoveJmxHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 11);
ops = checkCoreBootRecordHeader(records.get(10), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = createRemoveCoreHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 12);
ops = checkCoreBootRecordHeader(records.get(11), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 12);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 12);
op = createAddJmxHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 12);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 1); //File has been recycled
checkJmxBootRecordHeader(records.get(0), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = createAddCoreHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 2);
ops = checkCoreBootRecordHeader(records.get(1), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Now remove all handler references and make sure that the file gets recycled when adding the jmx reference
op = createRemoveCoreHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 3);
ops = checkCoreBootRecordHeader(records.get(2), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = createRemoveJmxHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 3);
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 3);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 3);
op = createAddJmxHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 3);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 1); //File has been recycled
checkJmxBootRecordHeader(records.get(0), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = createAddCoreHandlerReferenceOperation("test-file");
executeForResult(op);
records = readFile(file, 2);
ops = checkCoreBootRecordHeader(records.get(1), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 3);
ops = checkCoreBootRecordHeader(records.get(2), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
}
@Test
public void testSeparateFileCoreAndJmxAuditLog() throws Exception {
File fileJmx = new File(logDir, "test-file.log");
File fileCore = new File(logDir, "test-file2.log");
readFile(fileJmx, 1);
ModelNode op = createAddFileHandlerOperation("test-file2", "test-formatter", "test-file2.log");
executeForResult(op);
readFile(fileJmx, 1);
Assert.assertFalse(fileCore.exists());
op = createAddCoreHandlerReferenceOperation("test-file2");
executeForResult(op);
readFile(fileJmx, 1);
List<ModelNode> records = readFile(fileCore, 1);
List<ModelNode> ops = checkCoreBootRecordHeader(records.get(0), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Remove and add the jmx handler reference, making sure that core logging still gets logged
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
readFile(fileJmx, 1);
records = readFile(fileCore, 2);
ops = checkCoreBootRecordHeader(records.get(1), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
readFile(fileCore, 2);
records = readFile(fileJmx, 2);
checkJmxBootRecordHeader(records.get(1), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = createRemoveJmxHandlerReferenceOperation("test-file");
executeForResult(op);
readFile(fileJmx, 2);
records = readFile(fileCore, 3);
ops = checkCoreBootRecordHeader(records.get(2), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
readFile(fileJmx, 2);
op = createAddJmxHandlerReferenceOperation("test-file");
executeForResult(op);
readFile(fileJmx, 2);
records = readFile(fileCore, 4);
ops = checkCoreBootRecordHeader(records.get(3), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
readFile(fileCore, 4);
records = readFile(fileJmx, 1); //File has been recycled
checkJmxBootRecordHeader(records.get(0), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
//Remove and add the core handler reference, making sure that jmx logging still gets logged
op = createRemoveCoreHandlerReferenceOperation("test-file2");
executeForResult(op);
readFile(fileJmx, 1);
records = readFile(fileCore, 5);
ops = checkCoreBootRecordHeader(records.get(4), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
readFile(fileCore, 5);
records = readFile(fileJmx, 2); //File has been recycled
checkJmxBootRecordHeader(records.get(1), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
readFile(fileJmx, 2);
records = readFile(fileCore, 5);
op = createAddCoreHandlerReferenceOperation("test-file2");
executeForResult(op);
readFile(fileJmx, 2);
records = readFile(fileCore, 1); //File has been recycled
ops = checkCoreBootRecordHeader(records.get(0), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
}
@Test
public void testDisableAndEnableAuditLog() throws Exception {
File file = new File(logDir, "test-file.log");
ModelNode op = createAddCoreHandlerReferenceOperation("test-file");
executeForResult(op);
List<ModelNode> records = readFile(file, 2);
List<ModelNode> ops = checkCoreBootRecordHeader(records.get(1), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 3);
checkJmxBootRecordHeader(records.get(2), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
//Disable and reenable the core logger, making sure that jmx still gets logged
op = createCoreAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, false);
executeForResult(op);
records = readFile(file, 4);
ops = checkCoreBootRecordHeader(records.get(3), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
readFile(file, 4);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 5);
checkJmxBootRecordHeader(records.get(4), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = createCoreAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, true);
executeForResult(op);
records = readFile(file, 6);
ops = checkCoreBootRecordHeader(records.get(5), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Disable and reenable the jmx logger, making sure that core still gets logged
op = createJMXAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, false);
executeForResult(op);
records = readFile(file, 7);
ops = checkCoreBootRecordHeader(records.get(6), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 8);
ops = checkCoreBootRecordHeader(records.get(7), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 8);
op = createJMXAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, true);
executeForResult(op);
records = readFile(file, 9);
ops = checkCoreBootRecordHeader(records.get(8), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 10);
ops = checkCoreBootRecordHeader(records.get(9), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 11);
checkJmxBootRecordHeader(records.get(10), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
}
@Test
public void testToggleReadOnly() throws Exception {
File file = new File(logDir, "test-file.log");
ModelNode op = createAddCoreHandlerReferenceOperation("test-file");
executeForResult(op);
List<ModelNode> records = readFile(file, 2);
List<ModelNode> ops = checkCoreBootRecordHeader(records.get(1), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 3);
checkJmxBootRecordHeader(records.get(2), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 4);
ops = checkCoreBootRecordHeader(records.get(3), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
//Make the core logger not log read-only, making sure the jmx logger still does
op = createCoreAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, false);
executeForResult(op);
records = readFile(file, 5);
ops = checkCoreBootRecordHeader(records.get(4), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
readFile(file, 5);
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 6);
checkJmxBootRecordHeader(records.get(5), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = createCoreAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, true);
executeForResult(op);
records = readFile(file, 7);
ops = checkCoreBootRecordHeader(records.get(6), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Now read-only should be logged again for both
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 8);
checkJmxBootRecordHeader(records.get(7), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 9);
ops = checkCoreBootRecordHeader(records.get(8), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
//Make the jmx logger not log read-only, making sure the core logger still does
op = createJMXAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, false);
executeForResult(op);
records = readFile(file, 10);
ops = checkCoreBootRecordHeader(records.get(9), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 11);
ops = checkCoreBootRecordHeader(records.get(10), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 11);
op = createJMXAuditLogWriteAttributeOperation(ModelDescriptionConstants.ENABLED, true);
executeForResult(op);
records = readFile(file, 12);
ops = checkCoreBootRecordHeader(records.get(11), 1, false, false, true);
checkOpsEqual(op, ops.get(0));
//Now read-only should be logged again for both
Assert.assertTrue(server.queryNames(OBJECT_NAME, null).contains(OBJECT_NAME));
records = readFile(file, 13);
checkJmxBootRecordHeader(records.get(12), true, new String[] {ObjectName.class.getName(), QueryExp.class.getName()}, new String[] {OBJECT_NAME.toString(), null});
op = Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION, PathAddress.EMPTY_ADDRESS);
executeForResult(op);
records = readFile(file, 14);
ops = checkCoreBootRecordHeader(records.get(13), 1, true, false, true);
checkOpsEqual(op, ops.get(0));
}
private void checkOpsEqual(ModelNode rawDmr, ModelNode fromLog) {
ModelNode expected = ModelNode.fromJSONString(rawDmr.toJSONString(true));
Assert.assertEquals(expected, fromLog);
}
private final Pattern DATE_STAMP_PATTERN = Pattern.compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] - \\{");
private List<ModelNode> readFile(File file, int expectedRecords) throws IOException {
List<ModelNode> list = new ArrayList<ModelNode>();
try (final BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)){
StringWriter writer = null;
String line = reader.readLine();
while (line != null) {
if (DATE_STAMP_PATTERN.matcher(line).matches()) {
if (writer != null) {
list.add(ModelNode.fromJSONString(writer.getBuffer().toString()));
}
writer = new StringWriter();
writer.append("{");
} else {
writer.append("\n" + line);
}
line = reader.readLine();
}
if (writer != null) {
list.add(ModelNode.fromJSONString(writer.getBuffer().toString()));
}
}
Assert.assertEquals(list.toString(), expectedRecords, list.size());
return list;
}
private MBeanServer getMBeanServer() throws Exception {
ServiceController controller = getContainer().getRequiredService(MBeanServerService.SERVICE_NAME);
return (PluggableMBeanServer)controller.getValue();
}
private ModelNode createAddFileHandlerOperation(String handlerName, String formatterName, String fileName) {
ModelNode op = Util.createAddOperation(createFileHandlerAddress(handlerName));
op.get(ModelDescriptionConstants.RELATIVE_TO).set("log.dir");
op.get(ModelDescriptionConstants.PATH).set(fileName);
op.get(ModelDescriptionConstants.FORMATTER).set(formatterName);
return op;
}
private PathAddress createFileHandlerAddress(String handlerName){
return PathAddress.pathAddress(
CoreManagementResourceDefinition.PATH_ELEMENT,
AccessAuditResourceDefinition.PATH_ELEMENT,
PathElement.pathElement(ModelDescriptionConstants.FILE_HANDLER, handlerName));
}
private ModelNode createAddCoreHandlerReferenceOperation(String name){
return Util.createAddOperation(createCoreHandlerReferenceAddress(name));
}
private ModelNode createRemoveCoreHandlerReferenceOperation(String name){
return Util.createRemoveOperation(createCoreHandlerReferenceAddress(name));
}
private PathAddress createCoreHandlerReferenceAddress(String name){
return PathAddress.pathAddress(
CoreManagementResourceDefinition.PATH_ELEMENT,
AccessAuditResourceDefinition.PATH_ELEMENT,
AuditLogLoggerResourceDefinition.PATH_ELEMENT,
PathElement.pathElement(ModelDescriptionConstants.HANDLER, name));
}
private ModelNode createAddJmxHandlerReferenceOperation(String name){
return Util.createAddOperation(createJmxHandlerReferenceAddress(name));
}
private ModelNode createRemoveJmxHandlerReferenceOperation(String name){
return Util.createRemoveOperation(createJmxHandlerReferenceAddress(name));
}
private PathAddress createJmxHandlerReferenceAddress(String name){
return PathAddress.pathAddress(
JMXSubsystemRootResource.PATH_ELEMENT,
JmxAuditLoggerResourceDefinition.PATH_ELEMENT,
PathElement.pathElement(JmxAuditLogHandlerReferenceResourceDefinition.PATH_ELEMENT.getKey(), "test-file"));
}
private ModelNode createCoreAuditLogWriteAttributeOperation(String attr, boolean value) {
return Util.getWriteAttributeOperation(PathAddress.pathAddress(
CoreManagementResourceDefinition.PATH_ELEMENT,
AccessAuditResourceDefinition.PATH_ELEMENT,
AuditLogLoggerResourceDefinition.PATH_ELEMENT), attr, new ModelNode(value));
}
private ModelNode createJMXAuditLogWriteAttributeOperation(String attr, boolean value) {
return Util.getWriteAttributeOperation(PathAddress.pathAddress(JMXSubsystemRootResource.PATH_ELEMENT, JmxAuditLoggerResourceDefinition.PATH_ELEMENT), attr, new ModelNode(value));
}
private List<ModelNode> checkCoreBootRecordHeader(ModelNode bootRecord, int ops, boolean readOnly, boolean booting, boolean success) {
Assert.assertEquals("core", bootRecord.get("type").asString());
Assert.assertEquals(readOnly, bootRecord.get("r/o").asBoolean());
Assert.assertEquals(booting, bootRecord.get("booting").asBoolean());
Assert.assertEquals("anonymous", bootRecord.get("user").asString());
Assert.assertFalse(bootRecord.get("domainUUID").isDefined());
Assert.assertFalse(bootRecord.get("access").isDefined());
Assert.assertFalse(bootRecord.get("remote-address").isDefined());
Assert.assertEquals(success, bootRecord.get("success").asBoolean());
List<ModelNode> operations = bootRecord.get("ops").asList();
Assert.assertEquals(ops, operations.size());
return operations;
}
private void checkJmxBootRecordHeader(ModelNode bootRecord, boolean readOnly, String[] sig, String[] args) {
Assert.assertEquals("jmx", bootRecord.get("type").asString());
Assert.assertEquals(readOnly, bootRecord.get("r/o").asBoolean());
Assert.assertFalse(bootRecord.get("booting").asBoolean());
Assert.assertFalse(bootRecord.get("user").isDefined());
Assert.assertFalse(bootRecord.get("domainUUID").isDefined());
Assert.assertFalse(bootRecord.get("access").isDefined());
Assert.assertFalse(bootRecord.get("remote-address").isDefined());
//Assert.assertTrue(success, bootRecord.get("success").asBoolean());
List<ModelNode> sigs = bootRecord.get("sig").asList();
Assert.assertEquals(sig.length, sigs.size());
for (int i = 0 ; i < sig.length ; i++) {
Assert.assertEquals(sig[i], sigs.get(i).asString());
}
List<ModelNode> params = bootRecord.get("params").asList();
Assert.assertEquals(args.length, params.size());
for (int i = 0 ; i < args.length ; i++) {
if (args[i] == null) {
Assert.assertFalse(params.get(i).isDefined());
} else if (!args[i].equals(ANY_PLACEHOLDER)) {
Assert.assertEquals(args[i], params.get(i).asString());
}
}
}
@Override
protected void addBootOperations(List<ModelNode> bootOperations) {
bootOperations.addAll(this.bootOperations);
}
protected void initModel(ManagementModel managementModel) {
if (logDir == null){
logDir = new File(".");
logDir = new File(logDir, "target");
logDir = new File(logDir, "audit-log-test-log-dir").getAbsoluteFile();
if (!logDir.exists()){
logDir.mkdirs();
}
}
for (File file : logDir.listFiles()){
file.delete();
}
ManagementResourceRegistration registration = managementModel.getRootResourceRegistration();
pathManagerService = new PathManagerService() {
{
super.addHardcodedAbsolutePath(getContainer(), "log.dir", logDir.getAbsolutePath());
}
};
GlobalOperationHandlers.registerGlobalOperations(registration, processType);
registration.registerOperationHandler(CompositeOperationHandler.DEFINITION, CompositeOperationHandler.INSTANCE);
GlobalNotifications.registerGlobalNotifications(registration, processType);
TestServiceListener listener = new TestServiceListener();
listener.reset(1);
getContainer().addService(PathManagerService.SERVICE_NAME, pathManagerService)
.addListener(listener)
.install();
try {
listener.latch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
registration.registerSubModel(PathResourceDefinition.createSpecified(pathManagerService));
registration.registerSubModel(CoreManagementResourceDefinition.forStandaloneServer(new DelegatingConfigurableAuthorizer(), new ManagementSecurityIdentitySupplier(),
getAuditLogger(), pathManagerService, new EnvironmentNameReader() {
public boolean isServer() {
return true;
}
public String getServerName() {
return "Test";
}
public String getHostName() {
return null;
}
public String getProductName() {
return null;
}
}, null, new ResourceDefinition[0]));
Resource rootResource = managementModel.getRootResource();
pathManagerService.addPathManagerResources(rootResource);
ExtensionRegistry extensionRegistry = new ExtensionRegistry(ProcessType.STANDALONE_SERVER,
new RunningModeControl(RunningMode.NORMAL), auditLogger, null, null, RuntimeHostControllerInfoAccessor.SERVER);
extensionRegistry.setPathManager(pathManagerService);
extensionRegistry.setWriterRegistry(new NullConfigurationPersister());
JMXExtension extension = new JMXExtension();
extension.initialize(extensionRegistry.getExtensionContext("org.jboss.as.jmx", registration, ExtensionRegistryType.SLAVE));
rootResource.registerChild(CoreManagementResourceDefinition.PATH_ELEMENT, Resource.Factory.create());
//registration.registerSubModel(JMXSubsystemRootResource.create(auditLogger));
}
private class TestServiceListener extends AbstractServiceListener<Object> {
volatile CountDownLatch latch;
Map<ServiceController.Transition, ServiceName> services = Collections.synchronizedMap(new LinkedHashMap<ServiceController.Transition, ServiceName>());
void reset(int count) {
latch = new CountDownLatch(count);
services.clear();
}
public void transition(ServiceController<? extends Object> controller, ServiceController.Transition transition) {
if (transition == ServiceController.Transition.STARTING_to_UP || transition == ServiceController.Transition.REMOVING_to_REMOVED) {
services.put(transition, controller.getName());
latch.countDown();
}
}
}
public interface BeanMBean {
int getAttr();
void setAttr(int i);
}
public static class Bean implements BeanMBean {
volatile int attr;
@Override
public int getAttr() {
return attr;
}
@Override
public void setAttr(int i) {
attr = i;
}
}
}