/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/testsuite/junit/store/org/apache/slide/store/tamino/security/XSecurityTest.java,v 1.3 2004/07/30 06:52:19 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:19 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * 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 org.apache.slide.store.tamino.security;

import com.softwareag.tamino.db.api.accessor.TAccessLocation;
import com.softwareag.tamino.db.api.accessor.TInsertException;
import com.softwareag.tamino.db.api.accessor.TQuery;
import com.softwareag.tamino.db.api.accessor.TSchemaDefinition2Accessor;
import com.softwareag.tamino.db.api.accessor.TSchemaDefinition3Accessor;
import com.softwareag.tamino.db.api.accessor.TXMLObjectAccessor;
import com.softwareag.tamino.db.api.common.TAccessFailureException;
import com.softwareag.tamino.db.api.common.TException;
import com.softwareag.tamino.db.api.connection.TConnection;
import com.softwareag.tamino.db.api.connection.TConnectionFactory;
import com.softwareag.tamino.db.api.connection.TLocalTransaction;
import com.softwareag.tamino.db.api.objectModel.TXMLObject;
import com.softwareag.tamino.db.api.objectModel.TXMLObjectIterator;
import com.softwareag.tamino.db.api.objectModel.dom.TDOMObjectModel;
import com.softwareag.tamino.db.api.response.TResponse;
import java.io.StringReader;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import junit.framework.TestCase;
import junit.textui.TestRunner;
import org.apache.slide.store.tamino.Configuration;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.XAuthenticator;
import org.apache.slide.store.tamino.datastore.XDbHandler;
import org.apache.slide.store.tamino.datastore.XSecurity;
import org.apache.slide.store.tamino.datastore.XUtilDbSession;
import org.apache.slide.store.tamino.datastore.schema.XSchemaFactory;
import org.apache.slide.store.tamino.tools.stores.XDomainFileHandler;
import org.apache.slide.store.tamino.tools.stores.XNamespaceConfig;
import org.apache.slide.store.tamino.tools.stores.XStore;
import org.apache.slide.store.tamino.tools.stores.XStoreGroup;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;


/**
 ** Test class for testing security.
 ** This class tests via junit, if the security enablement via Tamino 311
 ** works correctly. To do so, it creates via an authrised user some documents
 ** and via an unauthorised user some documents, which should fail.
 **
 ** @author    Juergen
 ** @version   $Revision: 1.3 $
 **
 **/
public class XSecurityTest extends TestCase implements XGlobals {
    
    private static final String PRINT_STRING = " SECURTIY TEST : ";
    
    /** root http address */
    String taminoBase       = Configuration.get().getTaminoUrl();
    /** database */
    String taminoDatabase   = Configuration.get().taminoDatabase;
    /** collection */
    String taminoCollection = Configuration.get().taminoCollection;
    /** schema */
    String taminoSchema     = "patient";
    
    private String getTaminoUrl() {
        return taminoBase + "/" + taminoDatabase;
    }
    
    
    
    
    /**
     ** Constructor.
     ** initialise Junit and check if relevant parameters are set.
     **
     **/
    public XSecurityTest ( String name ) {
        super ( name );
    }
    
    
    /**
     ** Test the security module.
     ** Create some resources with diffrerent user ids.
     **
     **/
    public void test() throws Exception {
        Iterator groups = Configuration.get().getNamespace().getPublicStoreGroups().iterator();
        while ( groups.hasNext() ) {
            XStore store = ((XStoreGroup) groups.next()).getMainStore();
            System.out.println(PRINT_STRING + "Enabling and checking Security for store " + store);
            taminoBase = store.getTaminoBase();
            taminoDatabase = store.getTaminoDatabase();
            taminoCollection = store.getTaminoCollection();
            executeSecurityOnSingleStore();
        }
    }
    
    /**
     ** Execute the test case on a single store
     **/
    public void executeSecurityOnSingleStore() throws Exception {
        boolean result = true;
        
        System.out.println (PRINT_STRING + "Enabling Security");
        System.out.println (PRINT_STRING + "    Tamino base       = " + taminoBase);
        System.out.println (PRINT_STRING + "    Tamino database   = " + taminoDatabase);
        System.out.println (PRINT_STRING + "    Tamino collection = " + taminoCollection);
        
        
        XNamespaceConfig config = Configuration.get().getNamespace().getConfig();
        // check if we have the right version. if not return silently
        // TODO: TAMINO_DOMAIN or config.TAMINO_USER_DOMAIN
        if (!XAuthenticator.getNonAuthenticatedUserIsEnabled(taminoBase, taminoDatabase, config.getAdminDomain(), config.getAdminUser(), config.getAdminPwd())){
            return;
        }
        
        
        //Wipe security entries from Tamino
        assertTrue("Delete files prior to run", deleteFiles(config.getAdminUser(), config.getAdminPwd()));
        assertTrue("The collection should be empty to run the test", getCountDocuments()==0);
        assertTrue("read access for " + config.getAdminUser() + " user ", readAccessAllowed(config.getAdminUser(), config.getAdminPwd()));
        assertTrue("read access for some user ", readAccessAllowed("someUser", "dontCare"));
        
        
        assertTrue("Create the necessary Schema", createSchema("patient"));
        
        
        
        //Put a file
        result &= putFile( config.getAdminUser(), config.getAdminPwd());
        printResult(result);
        
        
        //Enable the security, fill in default and enable the collection
        XSecurity security = new XSecurity (config, taminoBase, taminoDatabase, taminoCollection);
        
        assertTrue("read access for " + config.getAdminUser() + " user ", readAccessAllowed(config.getAdminUser(), config.getAdminPwd()));
        assertTrue("read access for some user is not allowed", !readAccessAllowed("someUser", "dontCare"));
        
        
        //Put again using xdav username / password
        result &= putFile( "xdav", "xdav" );
        printResult(result);
        //Count number of documents, should be 1
        result &= countDocuments( 1 );
        printResult(result);
        //Put again using vaild user (tws)
        result &= putFile ( config.getAdminUser(), config.getAdminPwd());
        printResult(result);
        //Count number of documents, should be 2
        result &= countDocuments( 2 );
        printResult(result);
        
        
        
        //Put again using xdav username / password
        result &= putFile( "xdav", "xdav" );
        printResult(result);
        //Count number of documents, should be 1
        result &= countDocuments( 2 );
        printResult(result);
        //Put again using vaild user (tws)
        result &= putFile ( config.getAdminUser(), config.getAdminPwd());
        printResult(result);
        //Count number of documents, should be 2
        result &= countDocuments( 3 );
        printResult(result);
        
        
        security.deleteCollection(taminoCollection);
        
        
        
        assertTrue("read access for " + config.getAdminUser() + " user ", readAccessAllowed(config.getAdminUser(), config.getAdminPwd()));
        assertTrue("read access for some user is not allowed", readAccessAllowed("someUser", "dontCare"));
        
        assertTrue("Delete files without security", deleteFiles(config.getAdminUser(), config.getAdminPwd()));
        //Put a file
        result &= putFile( config.getAdminUser(), config.getAdminPwd());
        printResult(result);
        //Count number of documents, should be 1
        result &= countDocuments( 1 );
        printResult(result);
        //Put again using xdav username / password
        result &= putFile( "someUser", "dontCare" );
        printResult(result);
        //Count number of documents, should be 1
        result &= countDocuments( 2 );
        printResult(result);
        
        assertTrue("Clean up files created again", deleteFiles(config.getAdminUser(), config.getAdminPwd()));
        
        
        
        assertTrue( result );
    }
    
    
    
    
    /**
     ** Create a resource using different user ids.
     **
     ** @param      user   user id
     ** @param      password   The password for tamino
     ** @return     return true if the file could be created
     **
     **/
    public boolean putFile(String user, String password) throws Exception {
        TConnection connection = null;
        TXMLObjectAccessor accessor = null;
        boolean result = true;
        
        System.out.println (PRINT_STRING + "Test : Put file, user="+user+" password="+password);
        
        //Create connection
        TConnectionFactory factory = TConnectionFactory.getInstance();
        connection = factory.newConnection( getTaminoUrl(), user, password );
        TAccessLocation location = TAccessLocation.newInstance( taminoCollection );
        accessor = connection.newXMLObjectAccessor( location, TDOMObjectModel.getInstance() );
        
        //Create XML string
        String testString = "<patient><name><forname>" + user + "</forname><surename>bloggs</surename>"+
            "</name><address><street>BlueHouse</street><road>BlueRoad</road>"+
            "<town>BlueVille</town></address></patient>";
        
        //Create Document
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        docFactory.setNamespaceAware(false);
        docFactory.setValidating(false);
        
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
        Document testDoc = docBuilder.parse( new InputSource ( new StringReader( testString ) ) );
        
        //Place in Tamino and Commit
        TXMLObject xmlObject = TXMLObject.newInstance( testDoc.getDocumentElement() );
        connection.useAutoCommitMode();
        TLocalTransaction t = connection.useLocalTransactionMode();
        
        try {
            TResponse response = accessor.insert( xmlObject );
        }
        catch (TInsertException e) {
            TException nestedException = e.getRootTCause();
            if ( !(nestedException instanceof TAccessFailureException && ((TAccessFailureException) nestedException).getReturnValue().equals("8814"))) {
                e.printStackTrace();
                result = false;
            }
            
            
        }
        catch (Exception e) {
            e.printStackTrace();
            result = false;
        }
        
        t.commit();
        
        
        return result;
    }
    
    
    /**
     ** remove created resources again using different user ids.
     **
     ** @param      user   user id
     ** @param      password   The password for tamino
     ** @return     return true if the files could be deleted
     **
     **/
    public boolean deleteFiles(String user, String password) throws Exception {
        TConnection connection = null;
        TXMLObjectAccessor accessor = null;
        boolean result = true;
        
        System.out.println (PRINT_STRING + "Test : delete file, user="+user+" password="+password);
        
        //Create connection
        TConnectionFactory factory = TConnectionFactory.getInstance();
        connection = factory.newConnection( getTaminoUrl(), user, password );
        TAccessLocation location = TAccessLocation.newInstance( taminoCollection );
        accessor = connection.newXMLObjectAccessor( location, TDOMObjectModel.getInstance() );
        
        TLocalTransaction t = connection.useLocalTransactionMode();
        
        //Create query for documents
        TQuery query = TQuery.newInstance( "patient" );
        
        
        //Delete in Tamino and Commit
        try {
            TResponse response = accessor.delete( query );
        }
        catch (com.softwareag.tamino.db.api.accessor.TDeleteException e) {
            result = false;
            TException nestedException = e.getRootTCause();
            if ( nestedException instanceof TAccessFailureException ) {
                result = ((TAccessFailureException) nestedException).getReturnValue().equals("8300");
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            result = false;
        }
        
        t.commit();
        
        
        return result;
    }
    
    /**
     ** Count all created resources.
     **
     **/
    private int getCountDocuments()  throws Exception {
        TConnection connection = null;
        TXMLObjectAccessor accessor = null;
        int counter = 0;
        
        //Connect to Tamino
        TConnectionFactory factory = TConnectionFactory.getInstance();
        XNamespaceConfig config = Configuration.get().getNamespace().getConfig();
        connection = factory.newConnection( getTaminoUrl(), config.getAdminUser(), config.getAdminPwd());
        TAccessLocation location = TAccessLocation.newInstance( taminoCollection );
        accessor = connection.newXMLObjectAccessor( location, TDOMObjectModel.getInstance() );
        
        //Create query for documents
        TQuery query = TQuery.newInstance( taminoSchema );
        
        //Query Tamino and get Response
        TResponse response = accessor.query( query );
        TXMLObjectIterator i = response.getXMLObjectIterator();
        
        //Count Document(s)
        while (i.hasNext() ) {
            counter ++;
            i.next();
        }
        
        return counter;
    }
    
    
    /**
     ** Count all created resources.
     **
     **/
    private boolean readAccessAllowed(String user, String password)  throws Exception {
        
        boolean result = true;
        
        TConnection connection = null;
        TXMLObjectAccessor accessor = null;
        int counter = 0;
        
        //Connect to Tamino
        TConnectionFactory factory = TConnectionFactory.getInstance();
        connection = factory.newConnection( getTaminoUrl(), user, password);
        TAccessLocation location = TAccessLocation.newInstance( taminoCollection );
        accessor = connection.newXMLObjectAccessor( location, TDOMObjectModel.getInstance() );
        
        //Create query for documents
        try
        {
            TQuery query = TQuery.newInstance( taminoSchema );
            
            //Query Tamino and get Response
            TResponse response = accessor.query( query );
            TXMLObjectIterator i = response.getXMLObjectIterator();
            
            //Count Document(s)
            while (i.hasNext() ) {
                counter ++;
                i.next();
            }
        }
        catch (TException e) {
            result = false;
        }
        
        return result;
    }
    
    /**
     ** Count all created resources and compare it against the expected value.
     **
     **/
    private boolean countDocuments(int expected) throws Exception {
        System.out.println (PRINT_STRING + "Test : Count number of documents in Tamino");
        boolean result = true;
        
        if (getCountDocuments() == expected) {
            result = true;
        }
        else {
            System.out.println (" SECURITY TEST : Incorrect number of Documents found ");
            System.out.println (" SECURTIY TEST : "+getCountDocuments()+" document(s) found, should be "+expected);
            result = false;
        }
        
        return result;
    }
    
    
    
    
    
    /**
     ** Print a proper result string.
     **
     **/
    private void printResult(boolean result)    {
        if (result) {
            System.out.println (PRINT_STRING + "Test OK ");
        }
        else {
            System.out.println (PRINT_STRING + "Test FAILED! ");
        }
    }
    
    /**
     ** Create a schema.
     **
     **/
    private boolean createSchema(String schemaName)  {
        boolean result = false;
        TLocalTransaction t = null;
        try {
            
            TConnectionFactory factory = TConnectionFactory.getInstance();
            XNamespaceConfig config = Configuration.get().getNamespace().getConfig();
            TConnection     connection = factory.newConnection( getTaminoUrl(), config.getAdminUser(), config.getAdminPwd());
            t = connection.useLocalTransactionMode();
            TSchemaDefinition3Accessor schema3Accessor = null;
            TSchemaDefinition2Accessor schema2Accessor = null;
            
            
            XDbHandler dbHandler = new XDbHandler(Configuration.get().namespace, taminoBase, taminoDatabase, taminoCollection);
            dbHandler.initialize();
            XUtilDbSession dbSession = dbHandler.createUtilDbSession();
            
            String schemaLanguage = dbSession.getSchemaLanguage(taminoCollection);
            if ( schemaLanguage.equals(TSD3_SCHEMA_VERSION) ) {
                schema3Accessor = connection.newSchemaDefinition3Accessor(TDOMObjectModel.getInstance());
            } else {
                schema2Accessor = connection.newSchemaDefinition2Accessor(TDOMObjectModel.getInstance());
            }
            
            
            
            
            
            // create a non-xml schema in Tamino:
            TXMLObject xmlSchema = XSchemaFactory.getXMLContentSchema(taminoCollection, schemaName, schemaLanguage);
            if (schema3Accessor != null) {
                schema3Accessor.define(xmlSchema);
            } else {
                schema2Accessor.define(xmlSchema);
            }
            result = true;
        }
        catch (com.softwareag.tamino.db.api.accessor.TDefineException e) {
            result = "7931".equals(e.getRootTCause ().getMessageCode ());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            try {t.commit();} catch (Exception e) { result = false; e.printStackTrace();}
        }
        
        
        return result;
    }
    
    
    
    /**
     ** main entry to be able to start the test suite with out Junit.
     **
     **/
    
    public static void main (String args[]) {
        TestRunner.run( XSecurityTest.class );
    }
}

