au.edu.usq.fascinator.access.solr.SolrAccessControl.java Source code

Java tutorial

Introduction

Here is the source code for au.edu.usq.fascinator.access.solr.SolrAccessControl.java

Source

/*
 * The Fascinator - Solr Access Control plugin
 * Copyright (C) 2008-2010 University of Southern Queensland
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package au.edu.usq.fascinator.access.solr;

import au.edu.usq.fascinator.api.PluginDescription;
import au.edu.usq.fascinator.api.access.AccessControl;
import au.edu.usq.fascinator.api.access.AccessControlException;
import au.edu.usq.fascinator.api.access.AccessControlSchema;
import au.edu.usq.fascinator.api.authentication.AuthenticationException;
import au.edu.usq.fascinator.common.JsonConfig;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.request.DirectXmlRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A Fascinator access control plugin implementation using a security solr core
 *
 * @author Greg Pendlebury
 */
public class SolrAccessControl implements AccessControl {

    /** Logging */
    private final Logger log = LoggerFactory.getLogger(SolrAccessControl.class);

    /** Solr URI */
    private URI uri;

    /** Solr Core */
    private CommonsHttpSolrServer core;

    /**
     * Gets an identifier for this type of plugin. This should be a simple name
     * such as "file-system" for a storage plugin, for example.
     *
     * @return the plugin type id
     */
    @Override
    public String getId() {
        return "solr";
    }

    /**
     * Gets a name for this plugin. This should be a descriptive name.
     *
     * @return the plugin name
     */
    @Override
    public String getName() {
        return "Solr Access Control";
    }

    /**
     * Gets a PluginDescription object relating to this plugin.
     *
     * @return a PluginDescription
     */
    @Override
    public PluginDescription getPluginDetails() {
        return new PluginDescription(this);
    }

    /**
     * Initializes the plugin using the specified JSON String
     *
     * @param jsonString JSON configuration string
     * @throws PluginException if there was an error in initialization
     */
    @Override
    public void init(String jsonString) throws AccessControlException {
        try {
            JsonConfig config = new JsonConfig(new ByteArrayInputStream(jsonString.getBytes("UTF-8")));
            setConfig(config);
        } catch (UnsupportedEncodingException e) {
            throw new AccessControlException(e);
        } catch (IOException e) {
            throw new AccessControlException(e);
        }
    }

    /**
     * Initializes the plugin using the specified JSON configuration
     *
     * @param jsonFile JSON configuration file
     * @throws AccessControlException if there was an error in initialization
     */
    @Override
    public void init(File jsonFile) throws AccessControlException {
        try {
            JsonConfig config = new JsonConfig(jsonFile);
            setConfig(config);
        } catch (IOException ioe) {
            throw new AccessControlException(ioe);
        }
    }

    /**
     * Initialization of Solr Access Control plugin
     *
     * @param config The configuration to use
     * @throws AuthenticationException if fails to initialize
     */
    private void setConfig(JsonConfig config) throws AccessControlException {
        try {
            // Find our solr index
            uri = new URI(config.get("accesscontrol/solr/uri"));
            core = new CommonsHttpSolrServer(uri.toURL());
            // Small sleep whilst the solr index is still coming online
            Thread.sleep(200);
            // Make sure it is online
            core.ping();
        } catch (Exception ex) {
            throw new AccessControlException(ex);
        }
    }

    /**
     * Shuts down the plugin
     *
     * @throws AccessControlException if there was an error during shutdown
     */
    @Override
    public void shutdown() throws AccessControlException {
        // Don't need to do anything
    }

    /**
     * Return an empty security schema for the portal to investigate and/or
     * populate.
     *
     * @return An empty security schema
     */
    @Override
    public AccessControlSchema getEmptySchema() {
        return new SolrSchema();
    }

    /**
     * Get a list of schemas that have been applied to a record.
     *
     * @param recordId The record to retrieve information about.
     * @return A list of access control schemas, possibly zero length.
     * @throws AccessControlException if there was an error during retrieval.
     */
    @Override
    public List<AccessControlSchema> getSchemas(String recordId) throws AccessControlException {
        List<AccessControlSchema> result = new ArrayList();
        try {
            result = search(recordId);
        } catch (Exception ex) {
            log.error("Error searching security index: ", ex);
            throw new AccessControlException("Error searching security index");
        }
        return result;
    }

    /**
     * Apply/store a new security implementation. The schema will already have
     * a recordId as a property.
     *
     * @param newSecurity The new schema to apply.
     * @throws AccessControlException if storage of the schema fails.
     */
    @Override
    public void applySchema(AccessControlSchema newSecurity) throws AccessControlException {
        // Find the record
        String recordId = newSecurity.getRecordId();
        if (recordId == null || recordId.equals("")) {
            throw new AccessControlException("No record provided by schema.");
        }

        // Find the new role
        String role = newSecurity.get("role");
        if (role == null || role.equals("")) {
            throw new AccessControlException("No security role provided by schema.");
        }

        // Get record's current security
        List<String> role_list = getRoles(recordId);
        if (role_list != null && role_list.contains(role)) {
            throw new AccessControlException("Duplicate! That role has " + "already been applied to this record.");
        }

        // Add the new relationship to the index
        try {
            addToIndex(recordId, role);
        } catch (Exception ex) {
            log.error("Error updating security index: ", ex);
            throw new AccessControlException("Error updating security index");
        }
    }

    /**
     * Remove a security implementation. The schema will already have
     * a recordId as a property.
     *
     * @param oldSecurity The schema to remove.
     * @throws AccessControlException if removal of the schema fails.
     */
    @Override
    public void removeSchema(AccessControlSchema oldSecurity) throws AccessControlException {
        // Find the record
        String recordId = oldSecurity.getRecordId();
        if (recordId == null || recordId.equals("")) {
            throw new AccessControlException("No record provided by schema.");
        }

        // Find the new role
        String role = oldSecurity.get("role");
        if (role == null || role.equals("")) {
            throw new AccessControlException("No security role provided by schema.");
        }

        // Get its current security
        List<String> role_list = getRoles(recordId);
        if (role_list == null || !role_list.contains(role)) {
            throw new AccessControlException("That role does not have access to this record.");
        }

        // Remove from security
        try {
            removeFromIndex(recordId, role);
        } catch (Exception ex) {
            log.error("Error updating security index: ", ex);
            throw new AccessControlException("Error updating security index");
        }
    }

    /**
     * A basic wrapper for getSchemas() to return just the roles of the schemas.
     * Useful during index and/or audit when this is the only data required.
     *
     * @param recordId The record to retrieve roles for.
     * @return A list of Strings containing role names.
     * @throws AccessControlException if there was an error during retrieval.
     */
    @Override
    public List<String> getRoles(String recordId) throws AccessControlException {
        List<AccessControlSchema> schemas = getSchemas(recordId);
        List<String> roles = new ArrayList();

        for (AccessControlSchema schema : schemas) {
            roles.add(schema.get("role"));
        }

        if (roles.isEmpty()) {
            return null;
        }
        return roles;
    }

    /**
     * Retrieve a list of possible field values for a given field if the plugin
     * supports this feature.
     *
     * @param field The field name.
     * @return A list of String containing possible values
     * @throws AccessControlException if the field doesn't exist or there
     *          was an error during retrieval
     */
    @Override
    public List<String> getPossibilities(String field) throws AccessControlException {
        throw new AccessControlException("Not supported by this plugin. Use any freetext role name.");
    }

    private List<AccessControlSchema> search(String recordId) throws Exception {
        return search(recordId, null);
    }

    private List<AccessControlSchema> search(String recordId, String role) throws Exception {
        // An empty response
        List<AccessControlSchema> schemas = new ArrayList();
        SolrSchema schema;

        // Run the query
        String q = writeQueryString(recordId, role);
        SolrQuery query = new SolrQuery().setQuery(q);
        QueryResponse result = core.query(query);

        // Loop through the responses
        Iterator<SolrDocument> iter = result.getResults().iterator();
        while (iter.hasNext()) {
            // Get the details
            SolrDocument resultDoc = iter.next();
            String rRecord = (String) resultDoc.getFieldValue("recordId");
            String rRole = (String) resultDoc.getFieldValue("role");
            // Build a schema from details
            schema = new SolrSchema();
            schema.init(rRecord);
            schema.set("role", rRole);
            // Add to our response
            schemas.add(schema);
        }

        return schemas;
    }

    private void removeFromIndex(String recordId, String role) throws Exception {
        String doc = writeDeleteString(recordId, role);
        core.request(new DirectXmlRequest("/update", doc));
        core.commit();
    }

    private void addToIndex(String recordId, String role) throws Exception {
        String doc = writeUpdateString(recordId, role);
        core.request(new DirectXmlRequest("/update", doc));
        core.commit();
    }

    private String writeDeleteString(String recordId, String role) {
        String fRecord = "recordId:\"" + recordId + "\"";
        String fRole = "role:\"" + role + "\"";
        return "<delete><query>" + fRecord + " AND " + fRole + "</query></delete>";
    }

    private String writeQueryString(String recordId, String role) {
        String fRecord = "recordId:\"" + recordId + "\"";
        if (role == null) {
            return fRecord;
        } else {
            return fRecord + " AND role:\"" + role + "\"";
        }
    }

    private String writeUpdateString(String recordId, String role) {
        String fRecord = "<field name=\"recordId\">" + recordId + "</field>";
        String fRole = "<field name=\"role\">" + role + "</field>";
        return "<add><doc>" + fRecord + fRole + "</doc></add>";
    }
}