You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jena.apache.org by Dick Murray <da...@gmail.com> on 2012/10/29 14:43:11 UTC

Extending UpdateVisitor to provide security within visit(UpdateVisitor visitor).

Hi all

I need to permit/deny certain SPARUL update operations e.g. deny create|
drop graph.

I've looked at the UpdateEngineMain and UpdateVisitor classes and was
wondering if anyone has extended or encapsulated these before? Ideally I'd
like to capture the "visit" just prior to the actual visit.

i.e the UpdateEngineWorker has...

    @Override
    public void visit(UpdateCreate update)
    {
        Node g = update.getGraph() ;
        if ( g == null )
            return ;
        if ( graphStore.containsGraph(g) )
        {
            if ( ! alwaysSilent && ! update.isSilent() )
                error("Graph store already contains graph : "+g) ;
            return ;
        }
        // In-memory specific
        graphStore.addGraph(g, GraphFactory.createDefaultGraph()) ;
    }

...and I need a...

    if (deny(...)) {
        error("update create denied");
        return;

Also need it too work whether the graphStore was from a Dataset or TDB...
ideally... :-)

Regards Dick.

Re: Extending UpdateVisitor to provide security within visit(UpdateVisitor visitor).

Posted by Dick Murray <da...@gmail.com>.
Thank you Rob/Andy.

Rob your code outline worked!

Andy I'd considered the part commit and the final code will have the option
to wrap the SPARUL update operations in a transaction. If wrapped I'll
create a custom UpdateWorker where the visit() is effectively a stub which
checks the operation for permit/deny. Effectively I'll call the execute
twice with the first pass atomically permitting or denying the operations.

For anyone visiting this thread I've included my r&d class which includes a
TDB quad filter (based on regex patterns) and SPARUL update operation
filter (based on operation type).

public class SPARUL {

    private static Logger logger = Logger.getLogger(SPARUL.class);

    final static Boolean PERMIT = true;
    final static Boolean DENY = false;

    /**
     * @param args
     */
    public static void main(String[] args) {
        BasicConfigurator.configure();
        PropertyConfigurator.configure("./log4j.properties");

        new SPARUL().go();
    }

    // Instance.

    static final String graphToHide = "http://example/g2";

    public void go() {
        UpdateEngineRegistry.get().add(new CustomUpdateEngineFactory());

        Dataset ds = TDBFactory.createDataset() ;

        DatasetGraph dsg = ds.asDatasetGraph() ;

        Quad q1 = SSE.parseQuad("(<http://example/g1> <http://example/s> <
http://example/p> <http://example/o1>)") ;
        Quad q2 = SSE.parseQuad("(<http://example/g2> <http://example/s> <
http://example/p> <http://example/o2>)") ;
        dsg.add(q1) ;
        dsg.add(q2) ;

        final Node graphAsNode = Node.createURI(graphToHide);
        final NodeId graphAsNodeId = TDBInternal.getNodeId(ds, graphAsNode);
        TDBInternal.getNode(ds, graphAsNodeId);
//        graph.

//        Filter<Tuple<NodeId>> filter = new CustomFilter(graph);
        List<ACLEntry> acl = new ArrayList<ACLEntry>();
        acl.add(new ACLEntry(PERMIT, "^http://example/g1$"));
        acl.add(new ACLEntry(DENY, "^http://example/g2$"));
        Filter<Tuple<NodeId>> filter = new ACLFilter(dsg, acl);

        String qs = "select * { graph ?g { ?s ?p ?o } }";
        Query query = QueryFactory.create(qs) ;

        QueryExecution qExec = QueryExecutionFactory.create(query, ds) ;
        qExec.getContext().set(SystemTDB.symTupleFilter, filter) ;
        ResultSetFormatter.out(qExec.execSelect()) ;
        qExec.close() ;

        GraphStore gs = GraphStoreFactory.create(ds);
        UpdateRequest ur = UpdateFactory.create();
        ur.add("drop all");
        ur.add("create graph <http://example/g2>");
//        ur.add("load <file:etc/update-data.ttl> into <http://example/g2>")
;
        UpdateAction.execute(ur, gs);
    }

    class ACLEntry {
        Boolean permit;
        Pattern graph, subject, predicate, object;
        public ACLEntry(Boolean permit, String graph) {
            this.permit = permit;
            this.graph = Pattern.compile(graph);
        }
    }

    class ACLFilter implements Filter<Tuple<NodeId>> {
        DatasetGraph dsg;
        List<ACLEntry> acl;
        public ACLFilter(DatasetGraph dsg, List<ACLEntry> acl) {
            this.dsg = dsg;
            this.acl = acl;
        }
        @Override
        public boolean accept(Tuple<NodeId> item) {
            Boolean result = DENY;
            if ( item.size() == 4) {
                final NodeId nodeId = item.get(0);
                final Node node = TDBInternal.getNode(dsg, nodeId);
                for (ACLEntry e : acl) {
                    if (e.graph.matcher(node.toString()).matches()) {
                        result = e.permit;
                        break;
                    }
                }
            }
            return result;
        }
    }

    class CustomFilter implements Filter<Tuple<NodeId>> {
        NodeId target;
        public CustomFilter(NodeId target) {
            this.target = target;
        }
        @Override
        public boolean accept(Tuple<NodeId> item) {
            if ( item.size() == 4 && item.get(0).equals(target) )
            {
                return false ;
            }
            return true ;
        }
    }

    class CustomUpdateEngineFactory implements UpdateEngineFactory {

        @Override
        public boolean accept(UpdateRequest request, GraphStore graphStore,
Context context) {
            return true;
        }

        @Override
        public UpdateEngine create(UpdateRequest request, GraphStore
graphStore, Binding inputBinding, Context context) {
            return new CustomUpdateEngine(graphStore, request,
inputBinding, context);
        }

    }

    class CustomUpdateEngine extends UpdateEngineBase {

        public CustomUpdateEngine(GraphStore graphStore, UpdateRequest
request, Binding input, Context context) {
            super(graphStore, request, input, context);
        }

        @Override
        public void execute() {
            graphStore.startRequest(request);
            CustomUpdateEngineWorker worker = new
CustomUpdateEngineWorker(graphStore, startBinding, context) ;
            for ( Update up : request ) {
                up.visit(worker) ;
            }
            graphStore.finishRequest(request) ;
        }
    }

    class CustomUpdateEngineWorker extends UpdateEngineWorker {

        public CustomUpdateEngineWorker(GraphStore graphStore, Binding
initialBinding, Context context) {
            super(graphStore, initialBinding, context);
        }

        @Override
        public void visit(UpdateDrop update) {
            logger.info(String.format("visit(%s)", update));
            super.visit(update);
        }

    }

}


On 29 October 2012 21:02, Andy Seaborne <an...@apache.org> wrote:

> There is a slightly tricky point here - if you deny an operations, then
> partial operation get done. That's OK on a transactional system - it simply
> aborts - but not if it isn't transaction storage.  It might be better to
> asses the update before dispatching it to the execution engine.
>
> That can be done with Rob's suggestion - do the whole of the request
> before any execution.
>
>         Andy
>
>
>
>
> On 29/10/12 18:02, Rob Vesse wrote:
>
>> Re Step 2 - I just made a commit to trunk so that with the latest code you
>> can extend UpdateEngineMain and simply override the protected
>> prepareWorker() method to return your custom UpdateVisitor rather than the
>> default UpdateEngineWorker I.e. this avoids the need to implement the
>> execute() method yourself
>>
>>
>> Also in Step 3 you should be extending from UpdateEngineWorker rather than
>> the non-existent UpdateWorker
>>
>> Hope this helps,
>>
>> Rob
>>
>> On 10/29/12 9:53 AM, "Rob Vesse" <rv...@cray.com> wrote:
>>
>>  Hey Dick
>>>
>>> Yes we do this in our product in a production environment to replace the
>>> standard update handling with our own completely custom one
>>>
>>> Your desired extension is actually even easier than ours, extending
>>> Update
>>> evaluation basically requires you to do three things as follows.
>>>
>>> 1 - Create and register a custom UpdateEngineFactory
>>>
>>> Create a class that implements the UpdateEngineFactory interface, this
>>> has
>>> two methods for you to implement.  Simply return true for the accept()
>>> method to indicate you wish to handle all updates and then for the
>>> create() method return a new instance of the class you create in Step 2
>>>
>>> Your code will need to ensure that this factory gets registered by
>>> calling
>>> UpdateEngineRegistry.add(new CustomUpdateEngineFactory()); in order for
>>> your code to intercept updates.
>>>
>>> 2 - Create a custom UpdateEngine
>>>
>>> Create a class that extends from UpdateEngineBase and implements the
>>> abstract execute() method, you can simply modify the default
>>> implementation found in UpdateEngineMain like so:
>>>
>>> @Override
>>>     public void execute()
>>>     {
>>>   graphStore.startRequest(**request) ;
>>>   CustomUpdateEngineWorker worker = new
>>> CustomUpdateEngineWorker(**graphStore, startBinding, context) ;
>>>   for ( Update up : request ) {
>>>     up.visit(worker) ;
>>>   }
>>>   graphStore.finishRequest(**request) ;
>>>     }
>>>
>>>
>>> 3 - Create your custom UpdateVisitor
>>>
>>> Create a class that extends from UpdateWorker, this is the class you are
>>> referencing as CustomUpdateEngineWorker from Step 2, I assume you pick a
>>> more appropriate name.  Then you simply override the methods that you
>>> want
>>> to add access control functionality to like so:
>>>
>>> @Override
>>> public void visit(UpdateCreate update)
>>> {
>>>   if (deny(args)) {
>>>     //Handle the error case
>>>   } else {
>>>     //Otherwise defer to normal logic
>>>     super.visit(update);
>>>   }
>>> }
>>>
>>> Hope this helps,
>>>
>>> Rob
>>>
>>>
>>>
>>> On 10/29/12 6:43 AM, "Dick Murray" <da...@gmail.com> wrote:
>>>
>>>  Hi all
>>>>
>>>> I need to permit/deny certain SPARUL update operations e.g. deny create|
>>>> drop graph.
>>>>
>>>> I've looked at the UpdateEngineMain and UpdateVisitor classes and was
>>>> wondering if anyone has extended or encapsulated these before? Ideally
>>>> I'd
>>>> like to capture the "visit" just prior to the actual visit.
>>>>
>>>> i.e the UpdateEngineWorker has...
>>>>
>>>>     @Override
>>>>     public void visit(UpdateCreate update)
>>>>     {
>>>>         Node g = update.getGraph() ;
>>>>         if ( g == null )
>>>>             return ;
>>>>         if ( graphStore.containsGraph(g) )
>>>>         {
>>>>             if ( ! alwaysSilent && ! update.isSilent() )
>>>>                 error("Graph store already contains graph : "+g) ;
>>>>             return ;
>>>>         }
>>>>         // In-memory specific
>>>>         graphStore.addGraph(g, GraphFactory.**createDefaultGraph()) ;
>>>>     }
>>>>
>>>> ...and I need a...
>>>>
>>>>     if (deny(...)) {
>>>>         error("update create denied");
>>>>         return;
>>>>
>>>> Also need it too work whether the graphStore was from a Dataset or
>>>> TDB...
>>>> ideally... :-)
>>>>
>>>> Regards Dick.
>>>>
>>>
>>>
>>
>

Re: Extending UpdateVisitor to provide security within visit(UpdateVisitor visitor).

Posted by Andy Seaborne <an...@apache.org>.
There is a slightly tricky point here - if you deny an operations, then 
partial operation get done. That's OK on a transactional system - it 
simply aborts - but not if it isn't transaction storage.  It might be 
better to asses the update before dispatching it to the execution engine.

That can be done with Rob's suggestion - do the whole of the request 
before any execution.

	Andy



On 29/10/12 18:02, Rob Vesse wrote:
> Re Step 2 - I just made a commit to trunk so that with the latest code you
> can extend UpdateEngineMain and simply override the protected
> prepareWorker() method to return your custom UpdateVisitor rather than the
> default UpdateEngineWorker I.e. this avoids the need to implement the
> execute() method yourself
>
>
> Also in Step 3 you should be extending from UpdateEngineWorker rather than
> the non-existent UpdateWorker
>
> Hope this helps,
>
> Rob
>
> On 10/29/12 9:53 AM, "Rob Vesse" <rv...@cray.com> wrote:
>
>> Hey Dick
>>
>> Yes we do this in our product in a production environment to replace the
>> standard update handling with our own completely custom one
>>
>> Your desired extension is actually even easier than ours, extending Update
>> evaluation basically requires you to do three things as follows.
>>
>> 1 - Create and register a custom UpdateEngineFactory
>>
>> Create a class that implements the UpdateEngineFactory interface, this has
>> two methods for you to implement.  Simply return true for the accept()
>> method to indicate you wish to handle all updates and then for the
>> create() method return a new instance of the class you create in Step 2
>>
>> Your code will need to ensure that this factory gets registered by calling
>> UpdateEngineRegistry.add(new CustomUpdateEngineFactory()); in order for
>> your code to intercept updates.
>>
>> 2 - Create a custom UpdateEngine
>>
>> Create a class that extends from UpdateEngineBase and implements the
>> abstract execute() method, you can simply modify the default
>> implementation found in UpdateEngineMain like so:
>>
>> @Override
>>     public void execute()
>>     {
>>   graphStore.startRequest(request) ;
>>   CustomUpdateEngineWorker worker = new
>> CustomUpdateEngineWorker(graphStore, startBinding, context) ;
>>   for ( Update up : request ) {
>>     up.visit(worker) ;
>>   }
>>   graphStore.finishRequest(request) ;
>>     }
>>
>>
>> 3 - Create your custom UpdateVisitor
>>
>> Create a class that extends from UpdateWorker, this is the class you are
>> referencing as CustomUpdateEngineWorker from Step 2, I assume you pick a
>> more appropriate name.  Then you simply override the methods that you want
>> to add access control functionality to like so:
>>
>> @Override
>> public void visit(UpdateCreate update)
>> {
>>   if (deny(args)) {
>>     //Handle the error case
>>   } else {
>>     //Otherwise defer to normal logic
>>     super.visit(update);
>>   }
>> }
>>
>> Hope this helps,
>>
>> Rob
>>
>>
>>
>> On 10/29/12 6:43 AM, "Dick Murray" <da...@gmail.com> wrote:
>>
>>> Hi all
>>>
>>> I need to permit/deny certain SPARUL update operations e.g. deny create|
>>> drop graph.
>>>
>>> I've looked at the UpdateEngineMain and UpdateVisitor classes and was
>>> wondering if anyone has extended or encapsulated these before? Ideally
>>> I'd
>>> like to capture the "visit" just prior to the actual visit.
>>>
>>> i.e the UpdateEngineWorker has...
>>>
>>>     @Override
>>>     public void visit(UpdateCreate update)
>>>     {
>>>         Node g = update.getGraph() ;
>>>         if ( g == null )
>>>             return ;
>>>         if ( graphStore.containsGraph(g) )
>>>         {
>>>             if ( ! alwaysSilent && ! update.isSilent() )
>>>                 error("Graph store already contains graph : "+g) ;
>>>             return ;
>>>         }
>>>         // In-memory specific
>>>         graphStore.addGraph(g, GraphFactory.createDefaultGraph()) ;
>>>     }
>>>
>>> ...and I need a...
>>>
>>>     if (deny(...)) {
>>>         error("update create denied");
>>>         return;
>>>
>>> Also need it too work whether the graphStore was from a Dataset or TDB...
>>> ideally... :-)
>>>
>>> Regards Dick.
>>
>


Re: Extending UpdateVisitor to provide security within visit(UpdateVisitor visitor).

Posted by Rob Vesse <rv...@yarcdata.com>.
Re Step 2 - I just made a commit to trunk so that with the latest code you
can extend UpdateEngineMain and simply override the protected
prepareWorker() method to return your custom UpdateVisitor rather than the
default UpdateEngineWorker I.e. this avoids the need to implement the
execute() method yourself


Also in Step 3 you should be extending from UpdateEngineWorker rather than
the non-existent UpdateWorker

Hope this helps,

Rob

On 10/29/12 9:53 AM, "Rob Vesse" <rv...@cray.com> wrote:

>Hey Dick
>
>Yes we do this in our product in a production environment to replace the
>standard update handling with our own completely custom one
>
>Your desired extension is actually even easier than ours, extending Update
>evaluation basically requires you to do three things as follows.
>
>1 - Create and register a custom UpdateEngineFactory
>
>Create a class that implements the UpdateEngineFactory interface, this has
>two methods for you to implement.  Simply return true for the accept()
>method to indicate you wish to handle all updates and then for the
>create() method return a new instance of the class you create in Step 2
>
>Your code will need to ensure that this factory gets registered by calling
>UpdateEngineRegistry.add(new CustomUpdateEngineFactory()); in order for
>your code to intercept updates.
>
>2 - Create a custom UpdateEngine
>
>Create a class that extends from UpdateEngineBase and implements the
>abstract execute() method, you can simply modify the default
>implementation found in UpdateEngineMain like so:
>
>@Override
>    public void execute()
>    {
>  graphStore.startRequest(request) ;
>  CustomUpdateEngineWorker worker = new
>CustomUpdateEngineWorker(graphStore, startBinding, context) ;
>  for ( Update up : request ) {
>    up.visit(worker) ;
>  }
>  graphStore.finishRequest(request) ;
>    }
>
>
>3 - Create your custom UpdateVisitor
>
>Create a class that extends from UpdateWorker, this is the class you are
>referencing as CustomUpdateEngineWorker from Step 2, I assume you pick a
>more appropriate name.  Then you simply override the methods that you want
>to add access control functionality to like so:
>
>@Override
>public void visit(UpdateCreate update)
>{
>  if (deny(args)) {
>    //Handle the error case
>  } else {
>    //Otherwise defer to normal logic
>    super.visit(update);
>  }
>}
>
>Hope this helps,
>
>Rob
>
>
>
>On 10/29/12 6:43 AM, "Dick Murray" <da...@gmail.com> wrote:
>
>>Hi all
>>
>>I need to permit/deny certain SPARUL update operations e.g. deny create|
>>drop graph.
>>
>>I've looked at the UpdateEngineMain and UpdateVisitor classes and was
>>wondering if anyone has extended or encapsulated these before? Ideally
>>I'd
>>like to capture the "visit" just prior to the actual visit.
>>
>>i.e the UpdateEngineWorker has...
>>
>>    @Override
>>    public void visit(UpdateCreate update)
>>    {
>>        Node g = update.getGraph() ;
>>        if ( g == null )
>>            return ;
>>        if ( graphStore.containsGraph(g) )
>>        {
>>            if ( ! alwaysSilent && ! update.isSilent() )
>>                error("Graph store already contains graph : "+g) ;
>>            return ;
>>        }
>>        // In-memory specific
>>        graphStore.addGraph(g, GraphFactory.createDefaultGraph()) ;
>>    }
>>
>>...and I need a...
>>
>>    if (deny(...)) {
>>        error("update create denied");
>>        return;
>>
>>Also need it too work whether the graphStore was from a Dataset or TDB...
>>ideally... :-)
>>
>>Regards Dick.
>


Re: Extending UpdateVisitor to provide security within visit(UpdateVisitor visitor).

Posted by Rob Vesse <rv...@yarcdata.com>.
Hey Dick

Yes we do this in our product in a production environment to replace the
standard update handling with our own completely custom one

Your desired extension is actually even easier than ours, extending Update
evaluation basically requires you to do three things as follows.

1 - Create and register a custom UpdateEngineFactory

Create a class that implements the UpdateEngineFactory interface, this has
two methods for you to implement.  Simply return true for the accept()
method to indicate you wish to handle all updates and then for the
create() method return a new instance of the class you create in Step 2

Your code will need to ensure that this factory gets registered by calling
UpdateEngineRegistry.add(new CustomUpdateEngineFactory()); in order for
your code to intercept updates.

2 - Create a custom UpdateEngine

Create a class that extends from UpdateEngineBase and implements the
abstract execute() method, you can simply modify the default
implementation found in UpdateEngineMain like so:

@Override
    public void execute()
    {
  graphStore.startRequest(request) ;
  CustomUpdateEngineWorker worker = new
CustomUpdateEngineWorker(graphStore, startBinding, context) ;
  for ( Update up : request ) {
    up.visit(worker) ;
  }
  graphStore.finishRequest(request) ;
    }


3 - Create your custom UpdateVisitor

Create a class that extends from UpdateWorker, this is the class you are
referencing as CustomUpdateEngineWorker from Step 2, I assume you pick a
more appropriate name.  Then you simply override the methods that you want
to add access control functionality to like so:

@Override
public void visit(UpdateCreate update)
{
  if (deny(args)) {
    //Handle the error case
  } else {
    //Otherwise defer to normal logic
    super.visit(update);
  }
}

Hope this helps,

Rob



On 10/29/12 6:43 AM, "Dick Murray" <da...@gmail.com> wrote:

>Hi all
>
>I need to permit/deny certain SPARUL update operations e.g. deny create|
>drop graph.
>
>I've looked at the UpdateEngineMain and UpdateVisitor classes and was
>wondering if anyone has extended or encapsulated these before? Ideally I'd
>like to capture the "visit" just prior to the actual visit.
>
>i.e the UpdateEngineWorker has...
>
>    @Override
>    public void visit(UpdateCreate update)
>    {
>        Node g = update.getGraph() ;
>        if ( g == null )
>            return ;
>        if ( graphStore.containsGraph(g) )
>        {
>            if ( ! alwaysSilent && ! update.isSilent() )
>                error("Graph store already contains graph : "+g) ;
>            return ;
>        }
>        // In-memory specific
>        graphStore.addGraph(g, GraphFactory.createDefaultGraph()) ;
>    }
>
>...and I need a...
>
>    if (deny(...)) {
>        error("update create denied");
>        return;
>
>Also need it too work whether the graphStore was from a Dataset or TDB...
>ideally... :-)
>
>Regards Dick.