Updated fuseki example files added documentation to example code.
Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/beb73c4a Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/beb73c4a Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/beb73c4a Branch: refs/heads/eliminate-assignments Commit: beb73c4a777c69dceb3317900476710399aa6bf0 Parents: e0e8dba Author: Claude Warren <[email protected]> Authored: Sun Feb 15 16:52:52 2015 +0000 Committer: Claude Warren <[email protected]> Committed: Sun Feb 15 16:52:52 2015 +0000 ---------------------------------------------------------------------- .../security/example/ShiroExampleEvaluator.java | 86 +++++++++++++++++++- .../jena/security/example/fuseki/config.ttl | 68 ++++++++++------ .../jena/security/example/fuseki/shiro.ini | 2 +- 3 files changed, 127 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/beb73c4a/jena-security/src/example/java/org/apache/jena/security/example/ShiroExampleEvaluator.java ---------------------------------------------------------------------- diff --git a/jena-security/src/example/java/org/apache/jena/security/example/ShiroExampleEvaluator.java b/jena-security/src/example/java/org/apache/jena/security/example/ShiroExampleEvaluator.java index 87fca4e..1df5678 100644 --- a/jena-security/src/example/java/org/apache/jena/security/example/ShiroExampleEvaluator.java +++ b/jena-security/src/example/java/org/apache/jena/security/example/ShiroExampleEvaluator.java @@ -36,12 +36,15 @@ import com.hp.hpl.jena.vocabulary.RDF; /** * Class to use Shiro to provide credentials. - * Used for same example as ExampleEvaluator + * + * An example evaluator that only provides access to messages in the graph that + * are from or to the principal. * */ public class ShiroExampleEvaluator implements SecurityEvaluator { private static final Logger LOG = LoggerFactory.getLogger(ShiroExampleEvaluator.class); + // the model that contains the messages. private Model model; private RDFNode msgType = ResourceFactory.createResource( "http://example.com/msg" ); private Property pTo = ResourceFactory.createProperty( "http://example.com/to" ); @@ -56,14 +59,29 @@ public class ShiroExampleEvaluator implements SecurityEvaluator { this.model = model; } + /** + * We allow any action on the graph itself, so this is always true. + */ @Override public boolean evaluate(Object principal, Action action, SecNode graphIRI) { // we allow any action on a graph. return true; } + /** + * This is our internal check to see if the user may access the resource. + * This method is called from the evauate(Object,SecNode) method. + * A user may only access the resource if they are authenticated, and are either the + * sender or the recipient. + * Additionally the admin can always see the messages. + * @param principalObj + * @param r + * @return + */ private boolean evaluate( Object principalObj, Resource r ) { + // cast to the Subject because we know that it comes from Shiro and that + // our getPrincipal() method returns a Subject. Subject subject = (Subject)principalObj; if (! subject.isAuthenticated()) { @@ -73,32 +91,49 @@ public class ShiroExampleEvaluator implements SecurityEvaluator { // a message is only available to sender or recipient LOG.debug( "checking {}", subject.getPrincipal()); Object principal = subject.getPrincipal(); + + // We put the admin check here but it could have been done much earlier. if ("admin".equals(principal.toString())) { return true; } + // if we are looking at a message object then check the restrictions. if (r.hasProperty( RDF.type, msgType )) { return r.hasProperty( pTo, subject.getPrincipal().toString() ) || r.hasProperty( pFrom, subject.getPrincipal().toString()); } + // otherwise user can see the object. return true; } + /** + * Check that the user can see a specific node. + * @param principal + * @param node + * @return + */ private boolean evaluate( Object principal, SecNode node ) { + // Access to wild card is false -- this forces checks to the acutal nodes + // to be returned. + // we could have checked for admin access here and returned true since the admin + // can see any node. if (node.equals( SecNode.ANY )) { - return false; // all wild cards are false + return false; } + // URI nodes are retrieved from the model and evaluated if (node.getType().equals( SecNode.Type.URI)) { Resource r = model.createResource( node.getValue() ); return evaluate( principal, r ); } + // anonymous nodes have to be retrieved from the model as anonymous nodes. else if (node.getType().equals( SecNode.Type.Anonymous)) { Resource r = model.getRDFNode( NodeFactory.createAnon( new AnonId( node.getValue()) ) ).asResource(); return evaluate( principal, r ); } + // anything else (literals) can be seen. else { return true; @@ -106,44 +141,91 @@ public class ShiroExampleEvaluator implements SecurityEvaluator { } + /** + * Evaluate if the user can see the triple. + * @param principal + * @param triple + * @return + */ private boolean evaluate( Object principal, SecTriple triple ) { + // we could have checked here to see if the principal was the admin and + // just returned true since the admin can perform any operation on any triple. return evaluate( principal, triple.getSubject()) && evaluate( principal, triple.getObject()) && evaluate( principal, triple.getPredicate()); } + /** + * As per our design, users can do anything with triples they have access to, so we just + * ignore the action parameter. If we were to implement rules restricted access based + * upon action this method would sort those out appropriately. + */ @Override public boolean evaluate(Object principal, Action action, SecNode graphIRI, SecTriple triple) { + // we could have checked here to see if the principal was the admin and + // just returned true since the admin can perform any operation on any triple. return evaluate( principal, triple ); } + /** + * As per our design, users can access any graph. If we were to implement rules that + * restricted user access to specific graphs, those checks would be here and we would + * return <code>false</code> if they were not allowed to access the graph. Note that this + * method is checking to see that the user may perform ALL the actions in the set on the + * graph. + */ @Override public boolean evaluate(Object principal, Set<Action> actions, SecNode graphIRI) { return true; } + /** + * As per our design, users can access any triple from a message that is from or to them. + * Since we don't have restrictions on actions this is no different then checking access + * for a single action. + */ @Override public boolean evaluate(Object principal, Set<Action> actions, SecNode graphIRI, SecTriple triple) { return evaluate( principal, triple ); } + /** + * As per our design, users can access any graph. If we were to implement rules that + * restricted user access to specific graphs, those checks would be here and we would + * return <code>false</code> if they were not allowed to access the graph. Note that this + * method is checking to see that the user may perform ANY of the actions in the set on the + * graph. + */ @Override public boolean evaluateAny(Object principal, Set<Action> actions, SecNode graphIRI) { return true; } + /** + * As per our design, users can access any triple from a message that is from or to them. + * Since we don't have restrictions on actions this is no different then checking access + * for a single action. + */ @Override public boolean evaluateAny(Object principal, Set<Action> actions, SecNode graphIRI, SecTriple triple) { return evaluate( principal, triple ); } + /** + * As per our design, users can access any triple from a message that is from or to them. + * So for an update they can only change triples they have access to into other triples + * they have access to. (e.g. they can not remvoe themself from the messsage). + */ @Override public boolean evaluateUpdate(Object principal, SecNode graphIRI, SecTriple from, SecTriple to) { return evaluate( principal, from ) && evaluate( principal, to ); } + /** + * Return the Shiro subject. This is the subject that Shiro currently has logged in. + */ @Override public Object getPrincipal() { return SecurityUtils.getSubject(); http://git-wip-us.apache.org/repos/asf/jena/blob/beb73c4a/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/config.ttl ---------------------------------------------------------------------- diff --git a/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/config.ttl b/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/config.ttl index 6f4a3af..a748f01 100644 --- a/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/config.ttl +++ b/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/config.ttl @@ -14,62 +14,77 @@ # See the License for the specific language governing permissions and # limitations under the License. +## +## Define all the prefixes +## + @prefix fuseki: <http://jena.apache.org/fuseki#> . -@prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> . @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . @prefix ja: <http://jena.hpl.hp.com/2005/11/Assembler#> . @prefix sec: <http://apache.org/jena/security/Assembler#> . @prefix my: <http://example.org/#> . -#[] ja:loadClass "com.hp.hpl.jena.tdb.TDB" . +## +## Load the SecuredAssembler class from the security library and define +## the sec:Model as a subclass of ja:NamedModel. +## [] ja:loadClass "org.apache.jena.security.SecuredAssembler" . - -tdb:DatasetTDB rdfs:subClassOf ja:RDFDataset . -tdb:GraphTDB rdfs:subClassOf ja:Model . sec:Model rdfs:subClassOf ja:NamedModel . -my:dataset rdf:type tdb:DatasetTDB; - tdb:location "/tmp/myApp" ; - tdb:unionDefaultGraph true ; - . - -my:baseModel rdf:type tdb:GraphTDB ; - tdb:dataset my:dataset . +## +## Define the base model that contains the unsecured data. +## +my:baseModel rdf:type ja:MemoryModel; + ja:content [ja:externalContent <file:./example.ttl>] + . +## +## Define the secured model. This is where permissions is applied to the +## my:baseModel to create a model that has permission restrictions. Note +## that it is using the security evaluator implementation (sec:evaluatorImpl) +## called my:secEvaluator which we will define next. +## my:securedModel rdf:type sec:Model ; sec:baseModel my:baseModel ; ja:modelName "https://example.org/securedModel" ; sec:evaluatorImpl my:secEvaluator . +## +## Define the security evaluator. This is where we use the example +## ShiroExampleEvaluator. For your production environment you will replace +## "org.apache.jena.security.example.ShiroExampleEvaluator" with your +## SecurityEvaluator implementation. Note that ShiroExampleEvaluator constructor +## takes a Model argument. We pass in the unsecured baseModel so that the evaluator +## can read it unencumbered. Your implementation of SecurityEvaluator may have different +## parameters to meet your specific needs. +## my:secEvaluator rdf:type sec:Evaluator ; sec:args [ rdf:_1 my:baseModel ; ] ; sec:evaluatorClass "org.apache.jena.security.example.ShiroExampleEvaluator" . +## +## Define the dataset that we will use for in the server. +## my:securedDataset rdf:type ja:RDFDataset ; ja:defaultGraph my:securedModel . -my:fuseki rdf:type fuseki:Server ; - # Server-wide context parameters can be given here. - # For example, to set query timeouts: on a server-wide basis: - # Format 1: "1000" -- 1 second timeout - # Format 2: "10000,60000" -- 10s timeout to first result, then 60s timeout to for rest of query. - # See java doc for ARQ.queryTimeout - # ja:context [ ja:cxtName "arq:queryTimeout" ; ja:cxtValue "10000" ] ; - - # Load custom code (rarely needed) - # ja:loadClass "your.code.Class" ; - - # Services available. Only explicitly listed services are configured. - # If there is a service description not linked from this list, it is ignored. +## +## Define the fuseki:Server. +## +my:fuskei rdf:type fuseki:Server ; fuseki:services ( my:service1 ) . - +## +## Define the service for the fuseki:Service. Note that the fuseki:dataset served by +## this server is the secured dataset defined above. +## my:service1 rdf:type fuseki:Service ; + rdfs:label "My Secured Data Service" ; fuseki:name "myAppFuseki" ; # http://host:port/myAppFuseki fuseki:serviceQuery "query" ; # SPARQL query service fuseki:serviceQuery "sparql" ; # SPARQL query service @@ -80,3 +95,4 @@ my:service1 rdf:type fuseki:Service ; fuseki:serviceReadGraphStore "get" ; # SPARQL Graph store protocol (read only) fuseki:dataset my:securedDataset ; . + http://git-wip-us.apache.org/repos/asf/jena/blob/beb73c4a/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/shiro.ini ---------------------------------------------------------------------- diff --git a/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/shiro.ini b/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/shiro.ini index d0ce2c3..a9fbf71 100644 --- a/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/shiro.ini +++ b/jena-security/src/example/resources/org/apache/jena/security/example/fuseki/shiro.ini @@ -40,7 +40,7 @@ darla=darla /$/ping = anon ## restrict access. Must log in with above. -/$/** = authcBasic,user[admin] +/$/** = authcBasic,user # Everything else
