All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.telicent.jena.abac.assembler.Secured Maven / Gradle / Ivy

/*
 *  Copyright (c) Telicent Ltd.
 *
 *  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 io.telicent.jena.abac.assembler;

import static io.telicent.jena.abac.core.VocabAuthzDataset.*;
import static org.apache.jena.sparql.util.graph.GraphUtils.getStringValue;

import java.util.Set;
import java.util.function.Predicate;

import io.telicent.jena.abac.ABAC;
import io.telicent.jena.abac.core.AttributesStore;
import io.telicent.jena.abac.core.DatasetGraphABAC;
import io.telicent.jena.abac.labels.Labels;
import io.telicent.jena.abac.labels.LabelsStore;
import io.telicent.jena.abac.labels.LabelsStoreMem;
import org.apache.jena.assembler.exceptions.AssemblerException;
import org.apache.jena.atlas.logging.FmtLog;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.mem.DatasetGraphInMemory;
import org.apache.jena.sparql.util.NotUniqueException;
import org.slf4j.Logger;

/** Functions for building ABAC datasets and the infrastructure around them. */
public class Secured {

    /*package*/ public static Logger BUILD_LOG = ABAC.AzLOG;

    /**
     * Build a DatasetGraphAuthz from configuration and a base dataset.
     * Return null if no configuration.
     */
    public static DatasetGraphABAC buildDatasetGraphAuthz(DatasetGraph base, Resource assemblerRoot) {
        String accessAttributes = getAccessAttributes(assemblerRoot);

        Resource labelsStoreRoot = findLabelsStore(assemblerRoot);
        LabelsStore labels = null;
        if ( labelsStoreRoot != null )
            labels = LabelStoreAssembler.labelsStore(labelsStoreRoot, assemblerRoot);

        checkStorageConfiguration(base, labels);

        if ( labels == null )
            // In-memory default.
            labels = Labels.createLabelsStoreMem();

        Resource attributesStoreRoot = findAttributesStore(assemblerRoot);
        if ( attributesStoreRoot == null )
            throw new AssemblerException(assemblerRoot, "No attribute store definition found");

        String tripleDefaultLabel = AttributeStoreBuildLib.getTripleDefaultLabel(attributesStoreRoot);
        AttributesStore attributesStore = AttributeStoreBuildLib.attributesStore(attributesStoreRoot);

        DatasetGraphABAC dsgAuthz = ABAC.authzDataset(base, accessAttributes, labels, tripleDefaultLabel, attributesStore);
        return dsgAuthz;
    }

    /**
     * Check consistency of database and labels storage.
     * Examine the base storage and check configuration consistency.
     */
    private static void checkStorageConfiguration(DatasetGraph base, LabelsStore labels) {
        if ( base instanceof DatasetGraphInMemory ) {
            // In-memory data => in-memory labels store.
            if ( labels == null ) {
                // If null, then put in some kind of label store so new data can be loaded.
                labels = Labels.createLabelsStoreMem();
            } else {
                if ( ! ( labels instanceof LabelsStoreMem ) )
                    FmtLog.warn(BUILD_LOG, "LabelsStore[%s] provided for in-memory database",
                                labels.getClass().getSimpleName() );
            }
        } else if ( org.apache.jena.tdb2.sys.TDBInternal.isTDB2(base) ) {
            // Persistent database -> labelStore required.
            if ( labels == null )
                FmtLog.error(BUILD_LOG, "No labelsStore provided for TDB2 persistent database");
        } else if ( org.apache.jena.tdb1.sys.TDBInternal.isTDB1(base) ) {
            if ( labels == null )
                FmtLog.error(BUILD_LOG, "No labelsStore provided for TDB1 persistent database");
        } else {
            FmtLog.error(BUILD_LOG, "No labelsStore provided for persistent database");
        }
    }

    private static Set inlineLabelsStoreProperties = Set.of(pLabels, pLabelsStorePath);
    /**
     * Get the resource for the labels store. This is either the dataset
     * or linked by {@code authz:labelsStore}.
     */
    private static Resource findLabelsStore(Resource assemblerRoot) {
        Predicate isInline = r -> hasPropertyOneOf(r, inlineLabelsStoreProperties);
        Resource labelStoreRoot = maybeLinked(assemblerRoot, pLabelsStore, isInline);
        if ( labelStoreRoot != null && labelStoreRoot != assemblerRoot ) {
            if ( ! hasProperties(labelStoreRoot) )
                throw new AssemblerException(assemblerRoot, "Empty label store description '"+pLabelsStore);
        }
        return labelStoreRoot;
    }

    private static Set inlineAttributeStoreProperties = Set.of(pAttributes, pAttributesURL);
    /**
     * Get the resource for the attributes store. This is either the dataset
     * or linked by {@code authz:attributesStore}.
     */
    private static Resource findAttributesStore(Resource root) {
        Predicate isInline = r -> hasPropertyOneOf(r, inlineAttributeStoreProperties);
        return maybeLinked(root, pAttributesStore, isInline);
    }

    /**
     * Definitions can be in simple form, where there is a property on the dataset
     * description, or in a linked resource, where this is property
     * to another resource which can have multiple properties describing the sub-unit
     * (e.g. label store or attribute store). There must be only one form; inline and
     * a linking property is illegal.
     * 

* Inline is "legacy" and the preferred form is as a linked resource. *

     *  :dataset rdf:type authz:DatasetAuthz ;
     *      authz:labels 
     * 
* or *
     *  :dataset rdf:type authz:DatasetAuthz ;
     *      authz:labelsStore [ authz:labels  ]
     * 
*

* This function determines the root resource for the sub-unit (the subject of * {@code authz:labels} in the example above. */ private static Resource maybeLinked(Resource root, Property linkingProperty, Predicate isInline) { boolean hasLink = hasOneProperty(root, linkingProperty); boolean hasInline = isInline.test(root); if ( hasLink && hasInline ) throw new AssemblerException(root, "Both property '"+linkingProperty+" and also inline defintion. Must be one or the other."); if ( !hasLink && !hasInline ) // Nothing. return null; if ( hasInline ) return root; // hasLink Statement s = root.getProperty(linkingProperty); if ( ! s.getObject().isResource() ) throw new AssemblerException(root, "Property '"+linkingProperty+"' must have a resource for its object"); Resource r = s.getObject().asResource(); return r; } /** Test for zero or one properties */ private static boolean hasProperties(Resource resource) { StmtIterator sIter = resource.listProperties(); try { return sIter.hasNext(); } finally { sIter.close(); } } /** Test for zero or one properties */ private static boolean hasOneProperty(Resource resource, Property property) { StmtIterator sIter = resource.listProperties(property); try { if ( !sIter.hasNext() ) return false; sIter.next(); if ( !sIter.hasNext() ) return true; throw new NotUniqueException(resource, property); } finally { sIter.close(); } } /** Does the resource have one of the properties? */ private static boolean hasPropertyOneOf(Resource resource, Set properties) { for (Property p : properties ) { if ( resource.hasProperty(p) ) return true; } return false; } /** * Get the optional label for the ABAC required to access this dataset. * This is applied during request setup. It is in API (request) security. */ private static String getAccessAttributes(Resource root) { String accessAttributes = getStringValue(root, pAccessAttributes); if ( accessAttributes != null && accessAttributes.isEmpty() ) throw new AssemblerException(root, ":accessAttributes is an empty string (use \"!\" for 'deny all')"); return accessAttributes; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy