org.apache.ranger.audit.TestAuditQueue.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ranger.audit.TestAuditQueue.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.ranger.audit;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ranger.audit.destination.FileAuditDestination;
import org.apache.ranger.audit.model.AuthzAuditEvent;
import org.apache.ranger.audit.provider.AuditHandler;
import org.apache.ranger.audit.provider.AuditProviderFactory;
import org.apache.ranger.audit.provider.BaseAuditHandler;
import org.apache.ranger.audit.provider.MiscUtil;
import org.apache.ranger.audit.provider.MultiDestAuditProvider;
import org.apache.ranger.audit.queue.AuditAsyncQueue;
import org.apache.ranger.audit.queue.AuditBatchQueue;
import org.apache.ranger.audit.queue.AuditFileSpool;
import org.apache.ranger.audit.queue.AuditQueue;
import org.apache.ranger.audit.queue.AuditSummaryQueue;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestAuditQueue {

    private static final Log logger = LogFactory.getLog(TestAuditQueue.class);

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    static private int seqNum = 0;

    @Test
    public void testAuditAsyncQueue() {
        logger.debug("testAuditAsyncQueue()...");
        TestConsumer testConsumer = new TestConsumer();
        AuditAsyncQueue queue = new AuditAsyncQueue(testConsumer);
        Properties props = new Properties();
        queue.init(props);

        queue.start();

        int messageToSend = 10;
        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());
        }
        queue.stop();
        queue.waitToComplete();
        // Let's wait for second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignore
        }
        assertEquals(messageToSend, testConsumer.getCountTotal());
        assertEquals(messageToSend, testConsumer.getSumTotal());
        assertNull("Event not in sequnce", testConsumer.isInSequence());
    }

    @Test
    public void testAuditSummaryQueue() {
        logger.debug("testAuditSummaryQueue()...");
        TestConsumer testConsumer = new TestConsumer();
        AuditSummaryQueue queue = new AuditSummaryQueue(testConsumer);

        Properties props = new Properties();
        props.put(BaseAuditHandler.PROP_DEFAULT_PREFIX + "." + AuditSummaryQueue.PROP_SUMMARY_INTERVAL, "" + 300);
        queue.init(props, BaseAuditHandler.PROP_DEFAULT_PREFIX);

        queue.start();

        commonTestSummary(testConsumer, queue);
    }

    private void commonTestSummary(TestConsumer testConsumer, BaseAuditHandler queue) {
        int messageToSend = 0;
        int pauseMS = 330;

        int countToCheck = 0;
        try {

            queue.log(createEvent("john", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            queue.log(createEvent("john", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            countToCheck++;
            queue.log(createEvent("jane", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            countToCheck++;
            Thread.sleep(pauseMS);

            queue.log(createEvent("john", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            queue.log(createEvent("john", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            countToCheck++;
            queue.log(createEvent("jane", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            countToCheck++;
            Thread.sleep(pauseMS);

            queue.log(createEvent("john", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            countToCheck++;
            queue.log(createEvent("john", "select", "xademo/customer_details/imei", false));
            messageToSend++;
            countToCheck++;
            queue.log(createEvent("jane", "select", "xademo/customer_details/imei", true));
            messageToSend++;
            countToCheck++;
            Thread.sleep(pauseMS);

        } catch (InterruptedException e1) {
            logger.error("Sleep interupted", e1);
        }
        // Let's wait for second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete();
        queue.stop();
        queue.waitToComplete();
        // Let's wait for second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignore
        }
        assertEquals(messageToSend, testConsumer.getSumTotal());
        assertEquals(countToCheck, testConsumer.getCountTotal());
    }

    @Test
    public void testAuditSummaryByInfra() {
        logger.debug("testAuditSummaryByInfra()...");

        Properties props = new Properties();
        // Destination
        String propPrefix = AuditProviderFactory.AUDIT_DEST_BASE + ".test";
        props.put(propPrefix, "enable");
        props.put(BaseAuditHandler.PROP_DEFAULT_PREFIX + "." + "summary" + "." + "enabled", "true");
        props.put(propPrefix + "." + BaseAuditHandler.PROP_NAME, "test");
        props.put(propPrefix + "." + AuditQueue.PROP_QUEUE, "none");

        props.put(BaseAuditHandler.PROP_DEFAULT_PREFIX + "." + AuditSummaryQueue.PROP_SUMMARY_INTERVAL, "" + 300);
        props.put(propPrefix + "." + BaseAuditHandler.PROP_CLASS_NAME, TestConsumer.class.getName());

        AuditProviderFactory factory = AuditProviderFactory.getInstance();
        factory.init(props, "test");
        AuditQueue queue = (AuditQueue) factory.getProvider();
        BaseAuditHandler consumer = (BaseAuditHandler) queue.getConsumer();
        while (consumer != null && consumer instanceof AuditQueue) {
            AuditQueue cQueue = (AuditQueue) consumer;
            consumer = (BaseAuditHandler) cQueue.getConsumer();
        }
        assertTrue("Consumer should be TestConsumer. class=" + consumer.getClass().getName(),
                consumer instanceof TestConsumer);
        TestConsumer testConsumer = (TestConsumer) consumer;
        commonTestSummary(testConsumer, queue);
    }

    @Test
    public void testMultipleQueue() {
        logger.debug("testAuditAsyncQueue()...");
        int destCount = 3;
        TestConsumer[] testConsumer = new TestConsumer[destCount];

        MultiDestAuditProvider multiQueue = new MultiDestAuditProvider();
        for (int i = 0; i < destCount; i++) {
            testConsumer[i] = new TestConsumer();
            multiQueue.addAuditProvider(testConsumer[i]);
        }

        AuditAsyncQueue queue = new AuditAsyncQueue(multiQueue);
        Properties props = new Properties();
        queue.init(props);
        queue.start();

        int messageToSend = 10;
        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());
        }
        queue.stop();
        queue.waitToComplete();
        // Let's wait for second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignore
        }
        for (int i = 0; i < destCount; i++) {
            assertEquals("consumer" + i, messageToSend, testConsumer[i].getCountTotal());
            assertEquals("consumer" + i, messageToSend, testConsumer[i].getSumTotal());

        }
    }

    @Test
    public void testAuditBatchQueueBySize() {
        logger.debug("testAuditBatchQueue()...");
        int messageToSend = 10;

        String basePropName = "testAuditBatchQueueBySize_" + MiscUtil.generateUniqueId();
        int batchSize = messageToSend / 3;
        int expectedBatchSize = batchSize + (batchSize * 3 < messageToSend ? 1 : 0);
        int queueSize = messageToSend * 2;
        int intervalMS = messageToSend * 100; // Deliberately big interval
        Properties props = new Properties();
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_SIZE, "" + batchSize);
        props.put(basePropName + "." + AuditQueue.PROP_QUEUE_SIZE, "" + queueSize);
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_INTERVAL, "" + intervalMS);

        TestConsumer testConsumer = new TestConsumer();
        AuditBatchQueue queue = new AuditBatchQueue(testConsumer);
        queue.init(props, basePropName);
        queue.start();

        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());

        }
        // Let's wait for second
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete();
        queue.stop();
        queue.waitToComplete();

        assertEquals("Total count", messageToSend, testConsumer.getCountTotal());
        assertEquals("Total sum", messageToSend, testConsumer.getSumTotal());
        assertEquals("Total batch", expectedBatchSize, testConsumer.getBatchCount());
        assertNull("Event not in sequnce", testConsumer.isInSequence());

    }

    @Test
    public void testAuditBatchQueueByTime() {
        logger.debug("testAuditBatchQueue()...");

        int messageToSend = 10;

        String basePropName = "testAuditBatchQueueByTime_" + MiscUtil.generateUniqueId();
        int batchSize = messageToSend * 2; // Deliberately big size
        int queueSize = messageToSend * 2;
        int intervalMS = (1000 / messageToSend) * 3; // e.g (1000/10 * 3) = 300
        // ms
        int pauseMS = 1000 / messageToSend + 3; // e.g. 1000/10 + 3 = 103ms
        int expectedBatchSize = (messageToSend * pauseMS) / intervalMS + 1;

        Properties props = new Properties();
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_SIZE, "" + batchSize);
        props.put(basePropName + "." + AuditQueue.PROP_QUEUE_SIZE, "" + queueSize);
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_INTERVAL, "" + intervalMS);

        TestConsumer testConsumer = new TestConsumer();
        AuditBatchQueue queue = new AuditBatchQueue(testConsumer);
        queue.init(props, basePropName);
        queue.start();

        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());
            try {
                Thread.sleep(pauseMS);
            } catch (InterruptedException e) {
                // ignore
            }
        }
        // Let's wait for second
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ignore
        }
        queue.waitToComplete();
        queue.stop();
        queue.waitToComplete();

        assertEquals("Total count", messageToSend, testConsumer.getCountTotal());
        assertEquals("Total sum", messageToSend, testConsumer.getSumTotal());
        assertEquals("Total batch", expectedBatchSize, testConsumer.getBatchCount());
        assertNull("Event not in sequnce", testConsumer.isInSequence());
    }

    @Test
    public void testAuditBatchQueueDestDown() {
        logger.debug("testAuditBatchQueueDestDown()...");
        int messageToSend = 10;

        String basePropName = "testAuditBatchQueueDestDown_" + MiscUtil.generateUniqueId();
        int batchSize = messageToSend / 3;
        int queueSize = messageToSend * 2;
        int intervalMS = Integer.MAX_VALUE; // Deliberately big interval
        Properties props = new Properties();
        props.put(basePropName + "." + BaseAuditHandler.PROP_NAME, "testAuditBatchQueueDestDown");

        props.put(basePropName + "." + AuditQueue.PROP_BATCH_SIZE, "" + batchSize);
        props.put(basePropName + "." + AuditQueue.PROP_QUEUE_SIZE, "" + queueSize);
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_INTERVAL, "" + intervalMS);

        // Enable File Spooling
        props.put(basePropName + "." + "filespool.enable", "" + true);
        props.put(basePropName + "." + "filespool.dir", "target");

        TestConsumer testConsumer = new TestConsumer();
        testConsumer.isDown = true;

        AuditBatchQueue queue = new AuditBatchQueue(testConsumer);
        queue.init(props, basePropName);
        queue.start();

        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());

        }
        // Let's wait for second
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete(5000);
        queue.stop();
        queue.waitToComplete();

        assertEquals("Total count", 0, testConsumer.getCountTotal());
        assertEquals("Total sum", 0, testConsumer.getSumTotal());
        assertEquals("Total batch", 0, testConsumer.getBatchCount());
        assertNull("Event not in sequnce", testConsumer.isInSequence());
    }

    @Test
    public void testAuditBatchQueueDestDownFlipFlop() {
        logger.debug("testAuditBatchQueueDestDownFlipFlop()...");
        int messageToSend = 10;

        String basePropName = "testAuditBatchQueueDestDownFlipFlop_" + MiscUtil.generateUniqueId();
        int batchSize = messageToSend / 3;
        int queueSize = messageToSend * 2;
        int intervalMS = 5000; // Deliberately big interval
        Properties props = new Properties();
        props.put(basePropName + "." + BaseAuditHandler.PROP_NAME,
                "testAuditBatchQueueDestDownFlipFlop_" + MiscUtil.generateUniqueId());

        props.put(basePropName + "." + AuditQueue.PROP_BATCH_SIZE, "" + batchSize);
        props.put(basePropName + "." + AuditQueue.PROP_QUEUE_SIZE, "" + queueSize);
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_INTERVAL, "" + intervalMS);

        // Enable File Spooling
        int destRetryMS = 10;
        props.put(basePropName + "." + AuditQueue.PROP_FILE_SPOOL_ENABLE, "" + true);
        props.put(basePropName + "." + AuditFileSpool.PROP_FILE_SPOOL_LOCAL_DIR, "target");
        props.put(basePropName + "." + AuditFileSpool.PROP_FILE_SPOOL_DEST_RETRY_MS, "" + destRetryMS);

        TestConsumer testConsumer = new TestConsumer();
        testConsumer.isDown = false;

        AuditBatchQueue queue = new AuditBatchQueue(testConsumer);
        queue.init(props, basePropName);
        queue.start();

        try {
            queue.log(createEvent());
            queue.log(createEvent());
            queue.log(createEvent());
            Thread.sleep(1000);
            testConsumer.isDown = true;
            Thread.sleep(1000);
            queue.log(createEvent());
            queue.log(createEvent());
            queue.log(createEvent());
            Thread.sleep(1000);
            testConsumer.isDown = false;
            Thread.sleep(1000);
            queue.log(createEvent());
            queue.log(createEvent());
            queue.log(createEvent());
            Thread.sleep(1000);
            testConsumer.isDown = true;
            Thread.sleep(1000);
            queue.log(createEvent());
            Thread.sleep(1000);
            testConsumer.isDown = false;
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignore
        }
        // Let's wait for second
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete(5000);
        queue.stop();
        queue.waitToComplete();

        assertEquals("Total count", messageToSend, testConsumer.getCountTotal());
        assertEquals("Total sum", messageToSend, testConsumer.getSumTotal());
        assertNull("Event not in sequnce", testConsumer.isInSequence());

    }

    /**
     * See if we recover after restart
     */
    @Test
    public void testAuditBatchQueueDestDownRestart() {
        logger.debug("testAuditBatchQueueDestDownRestart()...");
        int messageToSend = 10;

        String basePropName = "testAuditBatchQueueDestDownRestart_" + MiscUtil.generateUniqueId();
        int batchSize = messageToSend / 3;
        int queueSize = messageToSend * 2;
        int intervalMS = 3000; // Deliberately big interval
        int maxArchivedFiles = 1;
        Properties props = new Properties();
        props.put(basePropName + "." + BaseAuditHandler.PROP_NAME,
                "testAuditBatchQueueDestDownRestart_" + MiscUtil.generateUniqueId());

        props.put(basePropName + "." + AuditQueue.PROP_BATCH_SIZE, "" + batchSize);
        props.put(basePropName + "." + AuditQueue.PROP_QUEUE_SIZE, "" + queueSize);
        props.put(basePropName + "." + AuditQueue.PROP_BATCH_INTERVAL, "" + intervalMS);

        // Enable File Spooling
        int destRetryMS = 10;
        props.put(basePropName + "." + AuditQueue.PROP_FILE_SPOOL_ENABLE, "" + true);
        props.put(basePropName + "." + AuditFileSpool.PROP_FILE_SPOOL_LOCAL_DIR, "target");
        props.put(basePropName + "." + AuditFileSpool.PROP_FILE_SPOOL_DEST_RETRY_MS, "" + destRetryMS);
        props.put(basePropName + "." + AuditFileSpool.PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT,
                "" + maxArchivedFiles);

        TestConsumer testConsumer = new TestConsumer();
        testConsumer.isDown = true;

        AuditBatchQueue queue = new AuditBatchQueue(testConsumer);
        queue.init(props, basePropName);
        queue.start();

        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());

        }
        // Let's wait for second or two
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete(5000);
        queue.stop();
        queue.waitToComplete();

        testConsumer.isDown = true;

        // Let's wait for second or two
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // ignore
        }

        // Let's now recreate the objects
        testConsumer = new TestConsumer();

        queue = new AuditBatchQueue(testConsumer);
        queue.init(props, basePropName);
        queue.start();

        // Let's wait for second
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete(5000);
        queue.stop();
        queue.waitToComplete();

        assertEquals("Total count", messageToSend, testConsumer.getCountTotal());
        assertEquals("Total sum", messageToSend, testConsumer.getSumTotal());
        assertNull("Event not in sequnce", testConsumer.isInSequence());

    }

    @Test
    public void testFileDestination() {
        logger.debug("testFileDestination()...");

        int messageToSend = 10;
        int batchSize = messageToSend / 3;
        int queueSize = messageToSend * 2;
        int intervalMS = 500; // Should be less than final sleep time

        String logFolderName = "target/testFileDestination";
        File logFolder = new File(logFolderName);
        String logFileName = "test_ranger_audit.log";
        File logFile = new File(logFolder, logFileName);

        Properties props = new Properties();
        // Destination
        String filePropPrefix = AuditProviderFactory.AUDIT_DEST_BASE + ".file";
        props.put(filePropPrefix, "enable");
        props.put(filePropPrefix + "." + AuditQueue.PROP_NAME, "file");
        props.put(filePropPrefix + "." + FileAuditDestination.PROP_FILE_LOCAL_DIR, logFolderName);
        props.put(filePropPrefix + "." + FileAuditDestination.PROP_FILE_LOCAL_FILE_NAME_FORMAT,
                "%app-type%_ranger_audit.log");
        props.put(filePropPrefix + "." + FileAuditDestination.PROP_FILE_FILE_ROLLOVER, "" + 10);

        props.put(filePropPrefix + "." + AuditQueue.PROP_QUEUE, "batch");
        String batchPropPrefix = filePropPrefix + "." + "batch";

        props.put(batchPropPrefix + "." + AuditQueue.PROP_BATCH_SIZE, "" + batchSize);
        props.put(batchPropPrefix + "." + AuditQueue.PROP_QUEUE_SIZE, "" + queueSize);
        props.put(batchPropPrefix + "." + AuditQueue.PROP_BATCH_INTERVAL, "" + intervalMS);

        // Enable File Spooling
        int destRetryMS = 10;
        props.put(batchPropPrefix + "." + AuditQueue.PROP_FILE_SPOOL_ENABLE, "" + true);
        props.put(batchPropPrefix + "." + AuditFileSpool.PROP_FILE_SPOOL_LOCAL_DIR, "target");
        props.put(batchPropPrefix + "." + AuditFileSpool.PROP_FILE_SPOOL_DEST_RETRY_MS, "" + destRetryMS);

        AuditProviderFactory factory = AuditProviderFactory.getInstance();
        factory.init(props, "test");

        // FileAuditDestination fileDest = new FileAuditDestination();
        // fileDest.init(props, filePropPrefix);
        //
        // AuditBatchQueue queue = new AuditBatchQueue(fileDest);
        // queue.init(props, batchPropPrefix);
        // queue.start();

        AuditHandler queue = factory.getProvider();

        for (int i = 0; i < messageToSend; i++) {
            queue.log(createEvent());
        }
        // Let's wait for second
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // ignore
        }

        queue.waitToComplete();
        queue.stop();
        queue.waitToComplete();

        assertTrue("File created", logFile.exists());
        try {
            List<AuthzAuditEvent> eventList = new ArrayList<AuthzAuditEvent>();
            int totalSum = 0;
            BufferedReader br = new BufferedReader(new FileReader(logFile));
            String line;
            int lastSeq = -1;
            boolean outOfSeq = false;
            while ((line = br.readLine()) != null) {
                AuthzAuditEvent event = MiscUtil.fromJson(line, AuthzAuditEvent.class);
                eventList.add(event);
                totalSum += event.getEventCount();
                if (event.getSeqNum() <= lastSeq) {
                    outOfSeq = true;
                }
            }
            br.close();
            assertEquals("Total count", messageToSend, eventList.size());
            assertEquals("Total sum", messageToSend, totalSum);
            assertFalse("Event not in sequnce", outOfSeq);

        } catch (Throwable e) {
            logger.error("Error opening file for reading.", e);
            assertTrue("Error reading file. fileName=" + logFile + ", error=" + e.toString(), true);
        }

    }

    private AuthzAuditEvent createEvent() {
        AuthzAuditEvent event = new AuthzAuditEvent();
        event.setSeqNum(++seqNum);
        return event;
    }

    private AuthzAuditEvent createEvent(String user, String accessType, String resource, boolean isAllowed) {
        AuthzAuditEvent event = new AuthzAuditEvent();
        event.setUser(user);
        event.setAccessType(accessType);
        event.setResourcePath(resource);
        event.setAccessResult(isAllowed ? (short) 1 : (short) 0);

        event.setSeqNum(++seqNum);
        return event;
    }
}