org.apache.druid.server.audit.SQLAuditManager.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.druid.server.audit.SQLAuditManager.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.druid.server.audit;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Supplier;
import com.google.inject.Inject;
import org.apache.druid.audit.AuditEntry;
import org.apache.druid.audit.AuditManager;
import org.apache.druid.guice.ManageLifecycle;
import org.apache.druid.guice.annotations.Json;
import org.apache.druid.java.util.common.DateTimes;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.emitter.service.ServiceEmitter;
import org.apache.druid.java.util.emitter.service.ServiceMetricEvent;
import org.apache.druid.metadata.MetadataStorageTablesConfig;
import org.apache.druid.metadata.SQLMetadataConnector;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.IDBI;
import org.skife.jdbi.v2.Query;
import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.tweak.HandleCallback;
import org.skife.jdbi.v2.tweak.ResultSetMapper;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

@ManageLifecycle
public class SQLAuditManager implements AuditManager {
    private final IDBI dbi;
    private final Supplier<MetadataStorageTablesConfig> dbTables;
    private final ServiceEmitter emitter;
    private final ObjectMapper jsonMapper;
    private final SQLAuditManagerConfig config;

    @Inject
    public SQLAuditManager(SQLMetadataConnector connector, Supplier<MetadataStorageTablesConfig> dbTables,
            ServiceEmitter emitter, @Json ObjectMapper jsonMapper, SQLAuditManagerConfig config) {
        this.dbi = connector.getDBI();
        this.dbTables = dbTables;
        this.emitter = emitter;
        this.jsonMapper = jsonMapper;
        this.config = config;
    }

    public String getAuditTable() {
        return dbTables.get().getAuditTable();
    }

    @Override
    public void doAudit(final AuditEntry auditEntry) {
        dbi.withHandle(new HandleCallback<Void>() {
            @Override
            public Void withHandle(Handle handle) throws Exception {
                doAudit(auditEntry, handle);
                return null;
            }
        });
    }

    @Override
    public void doAudit(AuditEntry auditEntry, Handle handle) throws IOException {
        emitter.emit(new ServiceMetricEvent.Builder().setDimension("key", auditEntry.getKey())
                .setDimension("type", auditEntry.getType())
                .setDimension("author", auditEntry.getAuditInfo().getAuthor()).build("config/audit", 1));

        handle.createStatement(StringUtils.format(
                "INSERT INTO %s ( audit_key, type, author, comment, created_date, payload) VALUES (:audit_key, :type, :author, :comment, :created_date, :payload)",
                getAuditTable())).bind("audit_key", auditEntry.getKey()).bind("type", auditEntry.getType())
                .bind("author", auditEntry.getAuditInfo().getAuthor())
                .bind("comment", auditEntry.getAuditInfo().getComment())
                .bind("created_date", auditEntry.getAuditTime().toString())
                .bind("payload", jsonMapper.writeValueAsBytes(auditEntry)).execute();
    }

    @Override
    public List<AuditEntry> fetchAuditHistory(final String key, final String type, Interval interval) {
        final Interval theInterval = getIntervalOrDefault(interval);
        return dbi.withHandle(new HandleCallback<List<AuditEntry>>() {
            @Override
            public List<AuditEntry> withHandle(Handle handle) {
                return handle.createQuery(StringUtils.format(
                        "SELECT payload FROM %s WHERE audit_key = :audit_key and type = :type and created_date between :start_date and :end_date ORDER BY created_date",
                        getAuditTable())).bind("audit_key", key).bind("type", type)
                        .bind("start_date", theInterval.getStart().toString())
                        .bind("end_date", theInterval.getEnd().toString()).map(new ResultSetMapper<AuditEntry>() {
                            @Override
                            public AuditEntry map(int index, ResultSet r, StatementContext ctx)
                                    throws SQLException {
                                try {
                                    return jsonMapper.readValue(r.getBytes("payload"), AuditEntry.class);
                                } catch (IOException e) {
                                    throw new SQLException(e);
                                }
                            }
                        }).list();
            }
        });
    }

    private Interval getIntervalOrDefault(Interval interval) {
        final Interval theInterval;
        if (interval == null) {
            DateTime now = DateTimes.nowUtc();
            theInterval = new Interval(now.minus(config.getAuditHistoryMillis()), now);
        } else {
            theInterval = interval;
        }
        return theInterval;
    }

    private int getLimit(int limit) throws IllegalArgumentException {
        if (limit < 1) {
            throw new IllegalArgumentException("Limit must be greater than zero!");
        }
        return limit;
    }

    @Override
    public List<AuditEntry> fetchAuditHistory(final String type, Interval interval) {
        final Interval theInterval = getIntervalOrDefault(interval);
        return dbi.withHandle(new HandleCallback<List<AuditEntry>>() {
            @Override
            public List<AuditEntry> withHandle(Handle handle) {
                return handle.createQuery(StringUtils.format(
                        "SELECT payload FROM %s WHERE type = :type and created_date between :start_date and :end_date ORDER BY created_date",
                        getAuditTable())).bind("type", type).bind("start_date", theInterval.getStart().toString())
                        .bind("end_date", theInterval.getEnd().toString()).map(new ResultSetMapper<AuditEntry>() {
                            @Override
                            public AuditEntry map(int index, ResultSet r, StatementContext ctx)
                                    throws SQLException {
                                try {
                                    return jsonMapper.readValue(r.getBytes("payload"), AuditEntry.class);
                                } catch (IOException e) {
                                    throw new SQLException(e);
                                }
                            }
                        }).list();
            }
        });
    }

    @Override
    public List<AuditEntry> fetchAuditHistory(final String key, final String type, int limit)
            throws IllegalArgumentException {
        return fetchAuditHistoryLastEntries(key, type, limit);
    }

    @Override
    public List<AuditEntry> fetchAuditHistory(final String type, int limit) throws IllegalArgumentException {
        return fetchAuditHistoryLastEntries(null, type, limit);
    }

    private List<AuditEntry> fetchAuditHistoryLastEntries(final String key, final String type, int limit)
            throws IllegalArgumentException {
        final int theLimit = getLimit(limit);
        String queryString = StringUtils.format("SELECT payload FROM %s WHERE type = :type", getAuditTable());
        if (key != null) {
            queryString += " and audit_key = :audit_key";
        }
        queryString += " ORDER BY created_date DESC";
        final String theQueryString = queryString;

        return dbi.withHandle(new HandleCallback<List<AuditEntry>>() {
            @Override
            public List<AuditEntry> withHandle(Handle handle) {
                Query<Map<String, Object>> query = handle.createQuery(theQueryString);
                if (key != null) {
                    query.bind("audit_key", key);
                }
                return query.bind("type", type).setMaxRows(theLimit).map(new ResultSetMapper<AuditEntry>() {
                    @Override
                    public AuditEntry map(int index, ResultSet r, StatementContext ctx) throws SQLException {
                        try {
                            return jsonMapper.readValue(r.getBytes("payload"), AuditEntry.class);
                        } catch (IOException e) {
                            throw new SQLException(e);
                        }
                    }
                }).list();
            }
        });
    }

}