blob: 62eb27d06a9f1f1b4437fd7654895694b8c4f4ff [file] [log] [blame]
/*
* 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.solr.security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class AuditLoggerPluginTest extends SolrTestCaseJ4 {
protected static final Date SAMPLE_DATE = new Date(1234567890);
protected static final AuditEvent EVENT_ANONYMOUS = new AuditEvent(AuditEvent.EventType.ANONYMOUS)
.setHttpMethod("GET")
.setMessage("Anonymous")
.setResource("/collection1")
.setDate(SAMPLE_DATE);
protected static final AuditEvent EVENT_WITH_URL = new AuditEvent(AuditEvent.EventType.ANONYMOUS)
.setHttpMethod("GET")
.setMessage("Anonymous")
.setResource("/collection1")
.setBaseUrl("http://myserver/mypath")
.setHttpQueryString("a=b&c=d")
.setDate(SAMPLE_DATE);
protected static final AuditEvent EVENT_ANONYMOUS_REJECTED = new AuditEvent(AuditEvent.EventType.ANONYMOUS_REJECTED)
.setHttpMethod("GET")
.setMessage("Anonymous rejected")
.setResource("/collection1");
protected static final AuditEvent EVENT_AUTHENTICATED = new AuditEvent(AuditEvent.EventType.AUTHENTICATED)
.setUsername("Jan")
.setHttpMethod("GET")
.setMessage("Authenticated")
.setDate(SAMPLE_DATE)
.setResource("/collection1");
protected static final AuditEvent EVENT_REJECTED = new AuditEvent(AuditEvent.EventType.REJECTED)
.setUsername("Jan")
.setHttpMethod("POST")
.setMessage("Wrong password")
.setDate(SAMPLE_DATE)
.setResource("/collection1");
protected static final AuditEvent EVENT_AUTHORIZED = new AuditEvent(AuditEvent.EventType.AUTHORIZED)
.setUsername("Per")
.setClientIp("192.168.0.10")
.setHttpMethod("GET")
.setMessage("Async")
.setDate(SAMPLE_DATE)
.setResource("/collection1");
protected static final AuditEvent EVENT_UNAUTHORIZED = new AuditEvent(AuditEvent.EventType.UNAUTHORIZED)
.setUsername("Jan")
.setHttpMethod("POST")
.setMessage("No access to collection1")
.setDate(SAMPLE_DATE)
.setResource("/collection1");
protected static final AuditEvent EVENT_ERROR = new AuditEvent(AuditEvent.EventType.ERROR)
.setUsername("Jan")
.setHttpMethod("POST")
.setMessage("Error occurred")
.setDate(SAMPLE_DATE)
.setSolrParams(Collections.singletonMap("action", Collections.singletonList("DELETE")))
.setResource("/admin/collections");
protected static final AuditEvent EVENT_UPDATE = new AuditEvent(AuditEvent.EventType.COMPLETED)
.setUsername("updateuser")
.setHttpMethod("POST")
.setRequestType(AuditEvent.RequestType.UPDATE)
.setMessage("Success")
.setDate(SAMPLE_DATE)
.setCollections(Collections.singletonList("updatecoll"))
.setRequestType(AuditEvent.RequestType.UPDATE)
.setResource("/update");
protected static final AuditEvent EVENT_STREAMING = new AuditEvent(AuditEvent.EventType.COMPLETED)
.setUsername("streaminguser")
.setHttpMethod("POST")
.setRequestType(AuditEvent.RequestType.STREAMING)
.setMessage("Success")
.setDate(SAMPLE_DATE)
.setCollections(Collections.singletonList("streamcoll"))
.setResource("/stream");
protected static final AuditEvent EVENT_HEALTH_API = new AuditEvent(AuditEvent.EventType.COMPLETED)
.setUsername("Jan")
.setHttpMethod("GET")
.setMessage("Healthy")
.setDate(SAMPLE_DATE)
.setResource("/api/node/health");
protected static final AuditEvent EVENT_HEALTH_V2 = new AuditEvent(AuditEvent.EventType.COMPLETED)
.setUsername("Jan")
.setHttpMethod("GET")
.setMessage("Healthy")
.setDate(SAMPLE_DATE)
.setResource("/____v2/node/health");
private MockAuditLoggerPlugin plugin;
private HashMap<String, Object> config;
@Before
public void setUp() throws Exception {
super.setUp();
plugin = new MockAuditLoggerPlugin();
config = new HashMap<>();
config.put("async", false);
plugin.init(config);
}
@Override
@After
public void tearDown() throws Exception {
if (null != plugin) {
plugin.close();
plugin = null;
}
super.tearDown();
}
@Test
public void init() {
config = new HashMap<>();
config.put("eventTypes", Collections.singletonList("REJECTED"));
config.put("async", false);
plugin.init(config);
assertTrue(plugin.shouldLog(EVENT_REJECTED.getEventType()));
assertFalse(plugin.shouldLog(EVENT_UNAUTHORIZED.getEventType()));
}
@Test
public void shouldLog() {
// Default types
assertTrue(plugin.shouldLog(EVENT_ANONYMOUS_REJECTED.getEventType()));
assertTrue(plugin.shouldLog(EVENT_REJECTED.getEventType()));
assertTrue(plugin.shouldLog(EVENT_UNAUTHORIZED.getEventType()));
assertTrue(plugin.shouldLog(EVENT_ERROR.getEventType()));
assertFalse(plugin.shouldLog(EVENT_ANONYMOUS.getEventType()));
assertFalse(plugin.shouldLog(EVENT_AUTHENTICATED.getEventType()));
assertFalse(plugin.shouldLog(EVENT_AUTHORIZED.getEventType()));
assertFalse(plugin.shouldLog(EVENT_AUTHORIZED.getEventType()));
}
@Test(expected = SolrException.class)
public void invalidMuteRule() {
config.put("muteRules", Collections.singletonList("foo:bar"));
plugin.init(config);
}
@Test
public void shouldMute() {
List<Object> rules = new ArrayList<>();
rules.add("type:STREAMING");
rules.add(Arrays.asList("user:updateuser", "collection:updatecoll"));
rules.add(Arrays.asList("path:/admin/collection", "param:action=DELETE"));
rules.add("ip:192.168.0.10");
config.put("muteRules",rules);
plugin.init(config);
assertFalse(plugin.shouldMute(EVENT_ANONYMOUS));
assertFalse(plugin.shouldMute(EVENT_AUTHENTICATED));
assertTrue(plugin.shouldMute(EVENT_STREAMING)); // type:STREAMING
assertTrue(plugin.shouldMute(EVENT_UPDATE)); // updateuser, updatecoll
assertTrue(plugin.shouldMute(EVENT_ERROR)); // admin/collection action=DELETE
assertTrue(plugin.shouldMute(EVENT_AUTHORIZED)); // ip
}
@Test
public void audit() {
plugin.doAudit(EVENT_ANONYMOUS_REJECTED);
plugin.doAudit(EVENT_REJECTED);
assertEquals(1, plugin.typeCounts.getOrDefault("ANONYMOUS_REJECTED", new AtomicInteger()).get());
assertEquals(1, plugin.typeCounts.getOrDefault("REJECTED", new AtomicInteger()).get());
assertEquals(2, plugin.events.size());
}
@Test
public void v2ApiPath() {
assertEquals("/api/node/health", EVENT_HEALTH_API.getResource());
// /____v2/ is mapped to /api/
assertEquals("/api/node/health", EVENT_HEALTH_V2.getResource());
}
@Test
public void jsonEventFormatter() {
assertEquals("{\"message\":\"Anonymous\",\"level\":\"INFO\",\"date\":" + SAMPLE_DATE.getTime() + ",\"solrParams\":{},\"solrPort\":0,\"resource\":\"/collection1\",\"httpMethod\":\"GET\",\"eventType\":\"ANONYMOUS\",\"status\":-1,\"qtime\":-1.0}",
plugin.formatter.formatEvent(EVENT_ANONYMOUS));
assertEquals("{\"message\":\"Authenticated\",\"level\":\"INFO\",\"date\":" + SAMPLE_DATE.getTime() + ",\"username\":\"Jan\",\"solrParams\":{},\"solrPort\":0,\"resource\":\"/collection1\",\"httpMethod\":\"GET\",\"eventType\":\"AUTHENTICATED\",\"status\":-1,\"qtime\":-1.0}",
plugin.formatter.formatEvent(EVENT_AUTHENTICATED));
}
@Test
public void getBaseUrl() {
assertEquals("http://myserver/mypath", EVENT_WITH_URL.getBaseUrl());
// Deprecated
assertEquals("http://myserver/mypath", EVENT_WITH_URL.getRequestUrl().toString());
}
@Test
public void getUrl() {
assertEquals("http://myserver/mypath?a=b&c=d",
EVENT_WITH_URL.getUrl());
}
}