com.evolveum.midpoint.testing.wstest.AbstractWebserviceTest.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.testing.wstest.AbstractWebserviceTest.java

Source

/*
 * Copyright (c) 2013-2015 Evolveum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.evolveum.midpoint.testing.wstest;

import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;

import com.evolveum.midpoint.model.client.ModelClientUtil;
import com.evolveum.midpoint.test.util.LogfileTestTailer;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.GetOperationOptionsType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaOperationListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.SelectorQualifiedGetOptionsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelPortType;
import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelService;
import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage;
import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultType;
import com.evolveum.prism.xml.ns._public.query_3.PagingType;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.apache.wss4j.dom.WSConstants;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.w3c.dom.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.SOAPFault;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import javax.xml.ws.soap.SOAPFaultException;

/**
*   Test Framework Util Class
*
*   <p>
*       This class contains static methods and functionality needed
*       across all test suites in this framework
*       It takes care of initialization of modelPort - webService client,
*       which is used to communicate with midpoint in all tests
*   </p>
*
*
*   @author semancik
*   @author Erik Suta
*
* */

public abstract class AbstractWebserviceTest {

    protected static final Trace LOGGER = TraceManager.getTrace(AbstractWebserviceTest.class);

    public static final File COMMON_DIR = new File("src/test/resources/common");

    public static final String ENDPOINT = "http://localhost:8080/midpoint/ws/model-3";
    public static final String USER_ADMINISTRATOR_OID = SystemObjectsType.USER_ADMINISTRATOR.value();
    public static final String USER_ADMINISTRATOR_USERNAME = "administrator";
    public static final String USER_ADMINISTRATOR_PASSWORD = "5ecr3t";

    public static final File USER_JACK_FILE = new File(COMMON_DIR, "user-jack.xml");
    public static final String USER_JACK_OID = "c0c010c0-d34d-b33f-f00d-111111111111";
    public static final String USER_JACK_USERNAME = "jack";
    public static final String USER_JACK_GIVEN_NAME = "Jack";
    public static final String USER_JACK_FAMILY_NAME = "Sparrow";

    // No authorization
    public static final File USER_NOBODY_FILE = new File(COMMON_DIR, "user-nobody.xml");
    public static final String USER_NOBODY_USERNAME = "nobody";
    public static final String USER_NOBODY_GIVEN_NAME = "No";
    public static final String USER_NOBODY_FAMILY_NAME = "Body";
    public static final String USER_NOBODY_PASSWORD = "nopassword";

    // WS authorization only
    public static final File USER_CYCLOPS_FILE = new File(COMMON_DIR, "user-cyclops.xml");
    public static final String USER_CYCLOPS_USERNAME = "cyclops";
    public static final String USER_CYCLOPS_PASSWORD = "cyclopassword";

    // WS and reader authorization
    public static final File USER_SOMEBODY_FILE = new File(COMMON_DIR, "user-somebody.xml");
    public static final String USER_SOMEBODY_USERNAME = "somebody";
    public static final String USER_SOMEBODY_PASSWORD = "somepassword";

    // WS, reader and adder authorization
    public static final File USER_DARTHADDER_FILE = new File(COMMON_DIR, "user-darthadder.xml");
    public static final String USER_DARTHADDER_OID = "1696229e-d90a-11e4-9ce6-001e8c717e5b";
    public static final String USER_DARTHADDER_USERNAME = "darthadder";
    public static final String USER_DARTHADDER_PASSWORD = "iamyouruncle";

    // Authorizations, but no password
    public static final File USER_NOPASSWORD_FILE = new File(COMMON_DIR, "user-nopassword.xml");
    public static final String USER_NOPASSWORD_USERNAME = "nopassword";

    public static final File ROLE_WS_FILE = new File(COMMON_DIR, "role-ws.xml");
    public static final File ROLE_READER_FILE = new File(COMMON_DIR, "role-reader.xml");
    public static final File ROLE_ADDER_FILE = new File(COMMON_DIR, "role-adder.xml");

    public static final File ROLE_MODIFIER_FILE = new File(COMMON_DIR, "role-modifier.xml");
    public static final String ROLE_MODIFIER_OID = "82005ae4-d90b-11e4-bdcc-001e8c717e5b";

    public static final File RESOURCE_OPENDJ_FILE = new File(COMMON_DIR, "resource-opendj.xml");
    public static final String RESOURCE_OPENDJ_OID = "ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff";

    public static final String CONNECTOR_LDAP_TYPE = "com.evolveum.polygon.connector.ldap.LdapConnector";

    protected static final Pattern PATTERN_AUDIT_EVENT_ID = Pattern.compile(".*\\seid=([^,]+),\\s.*");
    protected static final Pattern PATTERN_AUDIT_SESSION_ID = Pattern.compile(".*\\ssid=([^,]+),\\s.*");
    protected static final Pattern PATTERN_AUDIT_TASK_ID = Pattern.compile(".*\\stid=([^,]+),\\s.*");

    public static final String NS_COMMON = ModelClientUtil.NS_COMMON;
    public static final String NS_TYPES = ModelClientUtil.NS_TYPES;
    public static final String NS_RI = ModelClientUtil.NS_RI;
    public static final String NS_ICFS = ModelClientUtil.NS_ICFS;
    protected static final QName TYPES_POLYSTRING_ORIG = new QName(NS_TYPES, "orig");
    protected static final String CHANNEL_WS = "http://midpoint.evolveum.com/xml/ns/public/model/channels-3#webService";

    public static final QName ATTR_ICF_NAME_NAME = new QName(NS_ICFS, "name");

    protected static final QName COMMON_PATH = new QName(NS_COMMON, "path");
    protected static final QName COMMON_VALUE = new QName(NS_COMMON, "value");

    protected static DocumentBuilder domDocumentBuilder;
    protected static ModelPortType modelPort;
    protected static SystemConfigurationType configurationType;

    private static final File DEFAULT_SERVER_LOG_FILE = new File("/opt/tomcat/logs/idm.log");
    private static final String AUDIT_LOGGER_NAME = "com.evolveum.midpoint.audit.log";

    private File serverLogFile = null;

    @BeforeClass
    public void beforeTests() throws Exception {
        displayTestTitle("beforeTests");
        startResources();
    }

    /**
      * Takes care of system initialization. Need to be done before any tests are to be run.
      * */
    protected void startResources() throws Exception {
    }

    @AfterClass
    public void afterTests() throws Exception {
        displayTestTitle("afterTests");
        modelPort = createModelPort();
        cleanRepository();
        stopResources();
        LOGGER.info("WebService test suite finished.");
    }

    protected void stopResources() throws Exception {
    }

    protected static ModelPortType createModelPort() {
        return createModelPort(USER_ADMINISTRATOR_USERNAME, USER_ADMINISTRATOR_PASSWORD);
    }

    protected static ModelPortType createModelPort(String username, String password) {
        return createModelPort(username, password, WSConstants.PW_DIGEST);
    }

    /**
     * Creates webservice client connecting to midpoint
     * */
    protected static ModelPortType createModelPort(String username, String password, String passwordType) {

        String endpoint = ENDPOINT;
        if (System.getProperty("midpoint.endpoint") != null) {
            endpoint = System.getProperty("midpoint.endpoint");
        }
        LOGGER.info("Creating model client endpoint: {} , username={}, password={}",
                new Object[] { endpoint, username, password });

        ModelService modelService = new ModelService();
        ModelPortType modelPort = modelService.getModelPort();
        BindingProvider bp = (BindingProvider) modelPort;
        Map<String, Object> requestContext = bp.getRequestContext();
        requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);

        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(modelPort);
        org.apache.cxf.endpoint.Endpoint cxfEndpoint = client.getEndpoint();

        Map<String, Object> outProps = new HashMap<String, Object>();
        if (username != null) {
            outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
            outProps.put(WSHandlerConstants.USER, username);
            outProps.put(WSHandlerConstants.PASSWORD_TYPE, passwordType);
            ClientPasswordHandler.setPassword(password);
            outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordHandler.class.getName());

            WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
            cxfEndpoint.getOutInterceptors().add(wssOut);
        }

        cxfEndpoint.getInInterceptors().add(new LoggingInInterceptor());
        cxfEndpoint.getOutInterceptors().add(new LoggingOutInterceptor());

        return modelPort;
    }

    /**
     * Retrieves and returns actual system configuration
     * */
    protected SystemConfigurationType getConfiguration() throws FaultMessage {
        return getObject(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value());
    }

    protected <O extends ObjectType> O getObject(Class<O> type, String oid) throws FaultMessage {
        Holder<ObjectType> objectHolder = new Holder<ObjectType>();
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();

        modelPort.getObject(getTypeQName(type), oid, null, objectHolder, resultHolder);

        assertSuccess(resultHolder.value);
        return (O) objectHolder.value;
    }

    private File getServerLogFile() {
        if (serverLogFile == null) {
            if (System.getProperty("midpoint.serverLogFile") != null) {
                serverLogFile = new File(System.getProperty("midpoint.serverLogFile"));
            } else {
                serverLogFile = DEFAULT_SERVER_LOG_FILE;
            }
        }
        return serverLogFile;
    }

    /**
     * returns URI of type passed as argument
     * */
    protected static String getTypeUri(Class<? extends ObjectType> type) {
        return ModelClientUtil.getTypeUri(type);
    }

    protected static QName getTypeQName(Class<? extends ObjectType> type) {
        return ModelClientUtil.getTypeQName(type);
    }

    /**
     * Returns documentBuilder instance - used to create documents and polystrings
     * */
    protected static Document getDocument() {
        return DOMUtil.getDocument();
    }

    /**
     *  Creates polystring type with String value passed in argument
     * */
    protected static PolyStringType createPolyStringType(String string, Document doc) {
        PolyStringType polyStringType = new PolyStringType();
        Element origElement = createTextElement(TYPES_POLYSTRING_ORIG, string, doc);
        polyStringType.getContent().add(origElement);
        return polyStringType;
    }

    protected static Element createTextElement(QName qname, String value, Document doc) {
        Element element = doc.createElementNS(qname.getNamespaceURI(), qname.getLocalPart());
        element.setTextContent(value);
        return element;
    }

    //    /**
    //     * Creates CredentialsType - type used to store password. It's value is String password
    //     * representation passed via argument
    //     * */
    //    protected static CredentialsType createPasswordCredentials(String password) {
    //        CredentialsType credentialsType = new CredentialsType();
    //        credentialsType.setPassword(createPasswordType(password));
    //        return credentialsType;
    //    }
    //
    //    protected static PasswordType createPasswordType(String password) {
    //        PasswordType passwordType = new PasswordType();
    //        passwordType.setValue(createProtectedString(password));
    //        return passwordType;
    //    }
    //
    //    protected static ProtectedStringType createProtectedString(String clearValue) {
    //        ProtectedStringType protectedString = new ProtectedStringType();
    //        protectedString.setClearValue(clearValue);
    //        return protectedString;
    //    }

    protected static Element createPathElement(String stringPath, Document doc) {
        String pathDeclaration = "declare default namespace '" + NS_COMMON + "'; " + stringPath;
        return createTextElement(COMMON_PATH, pathDeclaration, doc);
    }

    protected static <T> JAXBElement<T> toJaxbElement(QName name, T value) {
        return new JAXBElement<T>(name, (Class<T>) value.getClass(), value);
    }

    protected static Element parseElement(String stringXml) throws SAXException, IOException {
        return DOMUtil.getFirstChildElement(DOMUtil.parseDocument(stringXml));
    }

    protected <O extends ObjectType> void deleteObject(Class<O> type, String oid) throws FaultMessage {
        deleteObject(type, oid, null);
    }

    protected <O extends ObjectType> void deleteObject(Class<O> type, String oid, ModelExecuteOptionsType options)
            throws FaultMessage {
        ObjectDeltaListType deltaList = new ObjectDeltaListType();
        ObjectDeltaType delta = new ObjectDeltaType();
        delta.setObjectType(getTypeQName(type));
        delta.setChangeType(ChangeTypeType.DELETE);
        delta.setOid(oid);
        deltaList.getDelta().add(delta);
        ObjectDeltaOperationListType deltaOpList = modelPort.executeChanges(deltaList, options);
        assertSuccess(deltaOpList);
    }

    protected <O extends ObjectType> String addObject(O object) throws FaultMessage {
        ObjectDeltaListType deltaList = new ObjectDeltaListType();
        ObjectDeltaType delta = new ObjectDeltaType();
        delta.setObjectType(getTypeQName(object.getClass()));
        delta.setChangeType(ChangeTypeType.ADD);
        delta.setObjectToAdd(object);
        deltaList.getDelta().add(delta);
        ObjectDeltaOperationListType deltaOpList = modelPort.executeChanges(deltaList, null);
        assertSuccess(deltaOpList);
        return deltaOpList.getDeltaOperation().get(0).getObjectDelta().getOid();
    }

    protected <O extends ObjectType> void assertObjectCount(Class<O> type, int expCount) throws FaultMessage {
        assertEquals("Unexpected count of " + type.getSimpleName(), expCount, countObjects(type));
    }

    protected <O extends ObjectType> int countObjects(Class<O> type) throws FaultMessage {
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();
        modelPort.searchObjects(getTypeQName(type), null, null, objectListHolder, resultHolder);
        assertSuccess(resultHolder.value);
        Integer count = objectListHolder.value.getCount();
        if (count != null) {
            assertEquals("Wrong count", (Integer) objectListHolder.value.getObject().size(), count);
        }
        return objectListHolder.value.getObject().size();
    }

    protected <F extends FocusType> String getSingleLinkOid(F focus) {
        List<ObjectReferenceType> linkRefs = focus.getLinkRef();
        assertEquals("Unexpected number of links for " + focus, 1, linkRefs.size());
        return linkRefs.get(0).getOid();
    }

    protected void assertUser(UserType user, String expOid, String expName) {
        assertEquals("Wrong user oid", expOid, user.getOid());
        assertEquals("Wrong user name", expName, ModelClientUtil.getOrig(user.getName()));
    }

    protected void assertUser(UserType user, String expOid, String expName, String expGivenName,
            String expFamilyName) {
        assertEquals("Wrong user oid", expOid, user.getOid());
        assertEquals("Wrong user name", expName, ModelClientUtil.getOrig(user.getName()));
        assertEquals("Wrong user givenName", expGivenName, ModelClientUtil.getOrig(user.getGivenName()));
        assertEquals("Wrong user familyName", expFamilyName, ModelClientUtil.getOrig(user.getFamilyName()));
    }

    protected void assertSuccess(ObjectDeltaOperationListType deltaOpList) {
        for (ObjectDeltaOperationType deltaOperation : deltaOpList.getDeltaOperation()) {
            OperationResultType result = deltaOperation.getExecutionResult();
            assertSuccess(result);
        }
    }

    protected void assertSuccess(Holder<OperationResultType> resultHolder) {
        assertSuccess(resultHolder.value);
    }

    protected void assertSuccess(OperationResultType result) {
        assertEquals(
                "Operation " + result.getOperation() + " failed:" + result.getStatus() + ": " + result.getMessage(),
                OperationResultStatusType.SUCCESS, result.getStatus());
        // TODO: look inside
    }

    protected <F extends FaultType> void assertFaultMessage(FaultMessage fault, Class<F> expectedFaultInfoClass,
            String expectedMessage) {
        FaultType faultInfo = fault.getFaultInfo();
        assertNotNull("No fault info in " + fault);
        if (expectedFaultInfoClass != null && !expectedFaultInfoClass.isAssignableFrom(faultInfo.getClass())) {
            AssertJUnit.fail("Expected that faultInfo will be of type " + expectedFaultInfoClass + ", but it was "
                    + faultInfo.getClass());
        }
        if (expectedMessage != null) {
            assertTrue("Wrong message in fault: " + fault.getMessage(),
                    fault.getMessage().contains(expectedMessage));
            assertTrue("Wrong message in fault info: " + faultInfo.getMessage(),
                    faultInfo.getMessage().contains(expectedMessage));
        }
        OperationResultType result = faultInfo.getOperationResult();
        assertNotNull("No result in faultInfo in " + fault, result);
        assertEquals("Expected that resut in FaultInfo will be fatal error, but it was " + result.getStatus(),
                OperationResultStatusType.FATAL_ERROR, result.getStatus());
    }

    protected void assertSoapFault(SOAPFaultException e, String expectedCode, String expectedMessage) {
        SOAPFault fault = e.getFault();
        String faultCode = fault.getFaultCode();
        display("SOAP fault code: " + faultCode);
        assertTrue("Unexpected fault code: " + faultCode, faultCode.endsWith(expectedCode));
        String message = e.getMessage();
        assertTrue("Unexpected fault message: " + message, message.contains(expectedMessage));
    }

    protected void displayFault(FaultMessage fault) {
        System.out.println("Got fault:");
        fault.printStackTrace(System.out);
        LOGGER.error("Got fault:\n{}", fault, fault);
    }

    protected void displayTestTitle(String testName) {
        TestUtil.displayTestTile(testName);
    }

    protected void display(String msg) {
        System.out.println(msg);
        LOGGER.info("{}", msg);
    }

    protected <O extends ObjectType> void display(O object) throws JAXBException {
        String xmlString = ModelClientUtil.marshallToSting(object);
        System.out.println(xmlString);
        LOGGER.info("{}", xmlString);
    }

    protected void display(OperationResultType result) throws JAXBException {
        String xmlString = ModelClientUtil.marshallToSting(new QName(NS_COMMON, "result"), result, true);
        System.out.println("Result:");
        System.out.println(xmlString);
        LOGGER.info("Result:\n{}", xmlString);
    }

    protected LogfileTestTailer createLogTailer() throws IOException {
        return new LogfileTestTailer(getServerLogFile(), AUDIT_LOGGER_NAME, true);
    }

    private void checkAuditEnabled(SystemConfigurationType configurationType) throws FaultMessage {
        LoggingConfigurationType loggingConfig = configurationType.getLogging();
        AuditingConfigurationType auditConfig = loggingConfig.getAuditing();
        if (auditConfig == null) {
            auditConfig = new AuditingConfigurationType();
            auditConfig.setEnabled(true);
            loggingConfig.setAuditing(auditConfig);
        } else {
            if (BooleanUtils.isTrue(auditConfig.isEnabled())) {
                return;
            }
            auditConfig.setEnabled(true);
        }

        ObjectDeltaListType deltaList = ModelClientUtil.createModificationDeltaList(SystemConfigurationType.class,
                SystemObjectsType.SYSTEM_CONFIGURATION.value(), "logging", ModificationTypeType.REPLACE,
                loggingConfig);

        ObjectDeltaOperationListType deltaOpList = modelPort.executeChanges(deltaList, null);

        assertSuccess(deltaOpList);
    }

    protected void assertAuditLoginFailed(LogfileTestTailer tailer, String expectedMessage) {
        tailer.assertAudit(1);
        String auditMessage = tailer.getAuditMessages().get(0);
        assertTrue("Audit: not login: " + auditMessage, auditMessage.contains("et=CREATE_SESSION"));
        assertTrue("Audit: not failure: " + auditMessage, auditMessage.contains("o=FATAL_ERROR"));
        assertTrue("Audit: wrong message: " + auditMessage, auditMessage.contains(expectedMessage));
    }

    protected void displayAudit(LogfileTestTailer tailer) {
        display("Audit:");
        for (String auditMessage : tailer.getAuditMessages()) {
            display(auditMessage);
        }
    }

    protected void assertAuditIds(LogfileTestTailer tailer) {
        List<String> eids = new ArrayList<String>();
        String sid = null;
        String tid = null;
        for (String auditMessage : tailer.getAuditMessages()) {

            String msgEid = getAuditEid(auditMessage);
            if (eids.contains(msgEid)) {
                AssertJUnit.fail("Event ID " + msgEid + " is not unique: " + auditMessage);
            }

            String msgTid = getAuditTid(auditMessage);
            if (msgTid == null) {
                AssertJUnit.fail("Audit message without task ID: " + auditMessage);
            }
            if (tid == null) {
                tid = msgTid;
            } else {
                assertEquals("Unmatched audit task ID: " + auditMessage, tid, msgTid);
            }

            String msgSid = getAuditSid(auditMessage);
            if (msgSid != null) {
                if (sid == null) {
                    sid = msgSid;
                } else {
                    assertEquals("Unmatched audit session ID: " + auditMessage, sid, msgSid);
                }
            }
        }
    }

    protected String getAuditEid(String auditMessage) {
        return getAuditId(auditMessage, PATTERN_AUDIT_EVENT_ID);
    }

    protected String getAuditSid(String auditMessage) {
        return getAuditId(auditMessage, PATTERN_AUDIT_SESSION_ID);
    }

    protected String getAuditTid(String auditMessage) {
        return getAuditId(auditMessage, PATTERN_AUDIT_TASK_ID);
    }

    protected String getAuditId(String auditMessage, Pattern pattern) {
        Matcher matcher = pattern.matcher(auditMessage);
        if (!matcher.matches()) {
            return null;
        }
        String match = matcher.group(1);
        if (match == null || match.equals("null")) {
            return null;
        }
        return match;
    }

    protected void assertAuditLoginLogout(LogfileTestTailer tailer) {
        List<String> msgs = tailer.getAuditMessages();
        String firstMsg = msgs.get(0);
        assertTrue("Audit: first: not login: " + firstMsg, firstMsg.contains("et=CREATE_SESSION"));
        assertTrue("Audit: first: not success: " + firstMsg, firstMsg.contains("o=SUCCESS"));

        String lastMsg = msgs.get(msgs.size() - 1);
        assertTrue("Audit: last: not logout: " + lastMsg, lastMsg.contains("et=TERMINATE_SESSION"));
        assertTrue("Audit: last: not success: " + lastMsg, lastMsg.contains("o=SUCCESS"));
    }

    protected void assertAuditOperation(LogfileTestTailer tailer, String expectedEventType) {
        assertAuditOperation(tailer, expectedEventType, OperationResultStatusType.SUCCESS, null);
    }

    protected void assertAuditOperation(LogfileTestTailer tailer, String expectedEventType,
            OperationResultStatusType expectedStatus, String expectedMessage) {
        List<String> msgs = tailer.getAuditMessages();
        String reqMsg = msgs.get(1);
        assertTrue("Audit: request: wrong operation: " + reqMsg, reqMsg.contains("et=" + expectedEventType));
        assertTrue("Audit: request: not request: " + reqMsg, reqMsg.contains("es=REQUEST"));

        String execMsg = msgs.get(2);
        assertTrue("Audit: exec: wrong operation: " + execMsg, execMsg.contains("et=" + expectedEventType));
        assertTrue("Audit: exec: not execution: " + execMsg, execMsg.contains("es=EXECUTION"));
        assertTrue("Audit: exec: wrong status, expected '" + expectedStatus + "': " + execMsg,
                execMsg.contains("o=" + expectedStatus));
        if (expectedMessage != null) {
            assertTrue("Audit: exec: wrong message, expected '" + expectedMessage + "': " + execMsg,
                    execMsg.matches(".*m=.*" + expectedMessage + ".*"));
        }
    }

    protected <O extends ObjectType> void assertModifyMetadata(O object, String actorOid,
            XMLGregorianCalendar startTs, XMLGregorianCalendar endTs) {
        MetadataType metadata = object.getMetadata();
        assertEquals("Wrong metadata modifierRef in " + object, actorOid, metadata.getModifierRef().getOid());
        assertEquals("Wrong metadata modify channel in " + object, CHANNEL_WS, metadata.getModifyChannel());
        TestUtil.assertBetween("Wrong password modifyTimestamp in " + object, startTs, endTs,
                metadata.getModifyTimestamp());
    }

    protected <O extends ObjectType> void assertCreateMetadata(O object, String actorOid,
            XMLGregorianCalendar startTs, XMLGregorianCalendar endTs) {
        MetadataType metadata = object.getMetadata();
        assertEquals("Wrong metadata creatorRef in " + object, actorOid, metadata.getCreatorRef().getOid());
        assertEquals("Wrong metadata create channel in " + object, CHANNEL_WS, metadata.getCreateChannel());
        TestUtil.assertBetween("Wrong createTimestamp in " + object, startTs, endTs, metadata.getCreateTimestamp());
    }

    protected void assertPasswordModifyMetadata(UserType user, String actorOid, XMLGregorianCalendar startTs,
            XMLGregorianCalendar endTs) {
        MetadataType passwordMetadata = user.getCredentials().getPassword().getMetadata();
        assertEquals("Wrong password metadata modifierRef", actorOid, passwordMetadata.getModifierRef().getOid());
        assertEquals("Wrong password metadata modify channel", CHANNEL_WS, passwordMetadata.getModifyChannel());
        TestUtil.assertBetween("Wrong password modifyTimestamp", startTs, endTs,
                passwordMetadata.getModifyTimestamp());
    }

    protected void assertPasswordCreateMetadata(UserType user, String actorOid, XMLGregorianCalendar startTs,
            XMLGregorianCalendar endTs) {
        MetadataType passwordMetadata = user.getCredentials().getPassword().getMetadata();
        assertEquals("Wrong password metadata creatorRef", actorOid, passwordMetadata.getCreatorRef().getOid());
        assertEquals("Wrong password metadata create channel", CHANNEL_WS, passwordMetadata.getCreateChannel());
        TestUtil.assertBetween("Wrong password createTimestamp", startTs, endTs,
                passwordMetadata.getCreateTimestamp());
    }

    protected void assertAttribute(ShadowType shadow, String attrName, String attrVal) {
        assertAttribute(shadow, new QName(NS_RI, attrName), attrVal);
    }

    protected void assertAttribute(ShadowType shadow, QName attrName, String attrVal) {
        ShadowAttributesType attributes = shadow.getAttributes();
        for (Object any : attributes.getAny()) {
            if (any instanceof Element) {
                Element element = (Element) any;
                if (DOMUtil.getQName(element).equals(attrName)) {
                    assertEquals("Wrong attribute " + attrName + " in shadow " + ModelClientUtil.toString(shadow),
                            attrVal, element.getTextContent());
                }
            } else if (any instanceof JAXBElement<?>) {
                JAXBElement<?> jaxbElement = (JAXBElement<?>) any;
                if (jaxbElement.getName().equals(attrName)) {
                    assertEquals("Wrong attribute " + attrName + " in shadow " + ModelClientUtil.toString(shadow),
                            attrVal, jaxbElement.getValue().toString());
                }
            } else {
                AssertJUnit.fail("Unexpected thing " + any + " in shadow attributes");
            }
        }
    }

    protected void assertNoAttribute(ShadowType shadow, String attrName) {
        assertNoAttribute(shadow, new QName(NS_RI, attrName));
    }

    protected void assertNoAttribute(ShadowType shadow, QName attrName) {
        ShadowAttributesType attributes = shadow.getAttributes();
        for (Object any : attributes.getAny()) {
            if (any instanceof Element) {
                Element element = (Element) any;
                if (DOMUtil.getQName(element).equals(attrName)) {
                    AssertJUnit.fail("Unexpected attribute " + attrName + " in shadow "
                            + ModelClientUtil.toString(shadow) + ": " + element.getTextContent());
                }
            } else if (any instanceof JAXBElement<?>) {
                JAXBElement<?> jaxbElement = (JAXBElement<?>) any;
                if (jaxbElement.getName().equals(attrName)) {
                    AssertJUnit.fail("Unexpected attribute " + attrName + " in shadow "
                            + ModelClientUtil.toString(shadow) + ": " + jaxbElement.getValue());
                }
            } else {
                AssertJUnit.fail("Unexpected thing " + any + " in shadow attributes");
            }
        }

    }

    /**
     *  Clean the repository after tests. Preserves user administrator
     * */
    protected void cleanRepository() throws FaultMessage {
        cleanObjects(UserType.class, false, SystemObjectsType.USER_ADMINISTRATOR.value());
        cleanObjects(RoleType.class, false, SystemObjectsType.ROLE_SUPERUSER.value(),
                SystemObjectsType.ROLE_END_USER.value());
        cleanObjects(ResourceType.class, false);
        cleanObjects(ShadowType.class, true);
    }

    private <O extends ObjectType> void cleanObjects(Class<O> type, boolean raw, String... protectedOids)
            throws FaultMessage {
        Holder<OperationResultType> resultHolder = new Holder<OperationResultType>();
        Holder<ObjectListType> objectListHolder = new Holder<ObjectListType>();

        SelectorQualifiedGetOptionsType rootOpts = null;
        ModelExecuteOptionsType execOpts = null;
        if (raw) {
            rootOpts = ModelClientUtil.createRootGetOptions(ModelClientUtil.createRawGetOption());
            execOpts = ModelClientUtil.createRawExecuteOption();
        }

        modelPort.searchObjects(getTypeQName(type), null, rootOpts, objectListHolder, resultHolder);

        List<String> protectedOidList = Arrays.asList(protectedOids);
        ObjectListType objectList = objectListHolder.value;
        for (ObjectType object : objectList.getObject()) {
            if (!protectedOidList.contains(object.getOid())) {
                display("Deleting " + type.getSimpleName() + " " + ModelClientUtil.toString(object));
                deleteObject(type, object.getOid(), execOpts);
            }
        }
    }

    @Test
    public void test000SanityAndCleanup() throws Exception {
        final String TEST_NAME = "test000SanityAndCleanup";
        displayTestTitle(TEST_NAME);
        modelPort = createModelPort();

        configurationType = getConfiguration();
        checkAuditEnabled(configurationType);

        cleanRepository();
    }

}