VAL
Virtuoso Authentication Layer
|
VAL provides a generic ACL layer which can be used to protect any resource. It is fully RDF-based, storing rules and groups in the triple store in private graphs, and describing rules via the W3C acl ontology and the OpenLink ACL ontology. The system provides both an internal API (accessible via VSP procedures) and a public HTTP API. In general it is recommended to use the latter as it includes VAL-powered authentication. The following chapters give an overview of the VAL ACL system and its usage in clients.
In addition to rules the system supports restrictions which, instead of granting access to resources, restrict arbitrary values based on the authenticated user. This can be used to limit the number of query results, enforce complex quotas, or any other numeric value. See ACL Restrictions for details.
VAL has special handling for two scopes (Rule Scopes) of resources: graphs in the triple store (scope IRI oplacl:PrivateGraphs, see also VAL.DBA.get_sparql_scope()) and Virtuoso DAV resources (scope oplacl:Dav, see also VAL.DBA.get_dav_scope()). For details see SPARQL ACL Rules - Defining access to private graphs and DAV ACL Rules.
The ACL rule system provides the possibility for any authenticated person (this includes 3rd-party service ids which have no corresponding Virtuoso SQL account) to create ACL rules for any resource they own or that they have been granted the right for. Each rule and each group lives in an application realm. An arbitrary number of application realms can be created or used. The public HTTP API will always use the realm from the authentication information (or fall back to the default oplacl:DefaultRealm
if the authentication information does not provide an application realm). The internal API has parameters to set the realm in addition to the rule or group details.
The most basic example of an ACL rule below grants read access for http://www.facebook.com/foobar
to resource http://me.com/bla:
In addition to individual persons ACL rules can grant access for a simple group, a conditional group, or everyone, i.e. make a resource public. The latter is achieved by granting access to foaf:Agent:
Conditional groups do not consist of a list of members but instead define a set of conditions. Every authenticated person matching these conditions is seen as part of the group. See Conditional Groups for details.
The ACL system is aware of the following permissions - there is, however, no restriction on the permissions to be stored, meaning an application can define their own permission and use that.
IRIs are used to denote (name or "refer to") agents (people, places, software, machines, or other entities capable of mechanized operation) in the ACL system. Any service supported by VAL is also supported in this system. In other words: any agent IRI that can be verified via VAL authentication can be used as the target of a resource access privilge grant. This ranges from Facebook URIs and G+ accounts to Mozilla Persona identifiers and WebIDs (See the ODS API documentation for a full list with examples.).
Internal Virtuoso SQL accounts are denoted by their personal data space URI, even if ODS is not installed. This means that SQL user foobar
is denoted by personal URI http://HOST/dataspace/person/foobar#this
.
Simple groups which consist of a set of members are defined as foaf:Group
(or more presise its sub-class oplacl:StaticGroup which is merely used for easy distinction from conditional groups - oplacl:ConditionalGroup).
Statis groups are defined as follows:
A conditional group does not consist of a set of members but a set of conditions. Each personal IRI matching all of these conditions is seen as being in that group. As such, the member of a conditional group are not a fixed set and can change at any point in time.
Conditional Groups have their roots in WebID authentication which means that many of the possible conditions refer to details of an X.509 client certificate. But the very generic SPARQL ASK query condition type allows to create virtually any condition type possible.
The following examples give an idea of the possibilities of conditional groups. The full set of possible conditions can be taken from the OpenLink ACL Ontology.
Example: Query condition
This condition consists of a simple sparql ask query which checks if the authenticated user is known to a certain other identity as claimed in a certain graph. If urn:mygraph
was a private graph controlled by the owner of the rule, then the group would be defined by the triples in that graph. The special notation ^{uri}^
is a placeholder for the authenticated personal IRI. Another supported placeholder is ^{graph}^
which refers to the graph containing the WebID profile data.
VAL supports recursive rules using oplacl:RecursiveAuthorizarion based on two concepts: file-system type URI-based recursion and relation-based recursion.
A recursive rule grants access to all sub-resources which start with the same prefix. This is typical for file systems: a recursive rule that grants access to http://HOST/DAV/home/foobar/test/
will also grant access to all children like http://HOST/DAV/home/foobar/test/hello.txt
.
dcterms:hasPart relations in the VAL ACL schema graph urn:virtuoso:val:acl:schema
(VAL.DBA.get_acl_schema_graph()) are taken into account. That means that a rule granting access to something like urn:test
in combination with a triple like
will grant access to urn:test:child
and urn:test:grandchild
also.
As mentioned above an authenticated user can only create ACL rules for resources they own or for which the owner granted them the corresponding grant permission (see Supported Rule Permission Modes for details). Resource ownership is stored in special graphs. These graphs are controlled by the application. Typically they are private graphs which need to be registered with the system via VAL.DBA.add_ownership_graph() and removed via VAL.DBA.remove_ownership_graph(). The resource ownership in these graphs is controlled by the application (not by VAL - the only exception is SPARQL, i.e. ownership of named graphs. see also SPARQL ACL Rules - Defining access to private graphs) via triples like
This triple states that the Google Plus account https://plus.google.com/1056872387
owns resource http://HOST/something
.
SQL
ownership of the resource and compare that to the authenticated user. Non SQL-users (3rd-party accounts) require oplacl:GrantRead
etc. permissions instead. VAL uses a hook into the Virtuoso DAV system which will create grant ACL rules should a 3rd-party account create a new resource.Tip: Since dba
is allowed to create rules for any resource without restrictions one could even do without ownership by granting oplacl:GrantRead
and friends to the "owner" or a resource. Even more important this allows for situations in which a user owns the data in a graph but is not allowed to change it via SPARQL - as is the case with ODS graphs which are controlled and populated through the ODS SQL tables.
Each ACL rule, group, and restriction is defined in a specific application realm which is stored with the rule or group using the oplacl:hasRealm and with each restriction using the oplres:hasRealm property. Each application realm defines a distinct set of rules, groups, and restrictions. In the case of the public HTTP API the realm is taken from the authentication information, meaning that all API functions will only handle rules, groups and restrictions from the current realm. The internal API allows to specifiy the realm as a parameter which makes it more powerful (but with great power comes great responsibiliy - do not use it lightly).
The default realm is oplacl:DefaultRealm
. This will use be used by VAL if no realm is specified.
The application realm can be configured in the virtual host options using the keyword app_realm
. This will make VAL default to the configured realm rather than oplacl:DefaultRealm
.
If one for example wanted to create a SPARQL endpoint which used a different set of ACL rules and groups from the default /sparql
endpoint, one could create the corresponding virtual dir as follows:
VAL.DBA.get_authentication_details_for_connection() will use the configured realm if no other is provided by the application page.
Each ACL rule has a scope like oplacl:hasScope oplacl:PrivateGraphs
. This scope groups ACL rules by the type of resource they protect.
Scopes can be any arbitrary URI the application chooses to use. Ideally, however, they should be defined in the private acl schema graph urn:virtuoso:val:acl:schema
(VAL.DBA.get_acl_schema_graph()). A typical scope definition looks as follows:
All scopes stored in the VAL scope graph can be accessed via convinience procedures VAL.DBA.acls_enabled_for_scope() and VAL.DBA.get_default_access_for_scope() which allow applications to support disabling of ACL checks.
VAL itself defines three scopes by default for which it contains optimizations:
private
named
graphs
(scope IRI oplacl:PrivateGraphs, see also VAL.DBA.get_sparql_scope()) groups rules protecting named graphs. This scope needs to be used for ACL rules if one wants to use VAL's SPARQL callback to enforce ACL rules. See SPARQL ACL Rules - Defining access to private graphs for details.query
(scope IRI oplacl:Query, see also VAL.DBA.get_query_scope()) groups ACL rules to grant general permission to perform SQL or SPARQL queries, including access to the Virtuoso Sponging engine.dav
(scope IRI oplacl:Dav, see also VAL.DBA.get_dav_scope()) groups rules protecting dav resources, i.e. files and folders.Additional scopes include:
Scopes can easily be enabled and disabled in a certain realm by setting the corresponding value in the scope graph. If one for example wanted to enable system-wide evaluation of SPARQL rules in the default realm:
Be aware though that VAL's ACL rule checking procedures typically do not evaluate these scope settings. It is up to applications to enforce them. The only exception are the Virtuoso ACL hook procedures and the /sparql
implementation which can be seen as clients to VAL themselves.
Rules and groups are stored in private named graphs within the triple store. Each application realm typically has its own set of graphs for rules, groups, and restrictions. These graph IRIs can be customized as explained in Customizing the ACL Graphs.
The default graphs use the default hostname (HOST
in the example below) of the Virtuoso instance and build a URI based on that and the realm URI.
Example: The default graph which stores the rules in the default realm is the following:
Ideally one does not have to care about the graphs or how rules are stored, unless one wants to manage them without the API for whatever reason (not recommended since it circumvents the ownership checks).
Should one choose to manually manage rules it is important to note that the API adds the used realm to all created entities. For rules and groups:
and for restrictions:
This essentially means that one could theoreticall use a single graph for all realms and all types of ACL resources.
The ACL API provides several ways to check for permissions. For internal use there are VAL.DBA.check_acls_for_resource() and friends which return a mapping of resources to their modes, as well as VAL.DBA.find_acl_permissions_basic() and friends which create a result set instead that can be looped over.
Alternatively HTTP clients can use VAL.VAL.acl.permissions.list() to get the permissions for a specific resource for the authenticated user.
As always using the HTTP API is recommended but vsp application developers might find it simpler to use the internal API.
Permissions for named graphs are a special case which is described in SPARQL ACL Rules - Defining access to private graphs and Enforcing SPARQL ACL Rules.
The ACL system is a generic system which allows to define rules for any type of resource. To make life easier for developers VAL does handle sparql ACL rules as a special case.
As such VAL defines one special scope oplacl:PrivateGraphs (VAL.DBA.get_sparql_scope()) which is used to store rules and groups pertaining named graphs. In addition ownership of named graphs is handled as a special case directly by VAL. Compare VAL.DBA.add_graph_ownership(), VAL.DBA.set_graph_ownership(), and VAL.DBA.remove_graph_ownership().
In order to make use of these ACL rules the sql:gs-app-callback
SPARQL pragma needs to be used as follows:
This will tell the query engine to use the VAL graph security callback for graph permission checks. In addition the authenticated service id needs to be set with the sql:gs-app-uid
pragma:
This is the id which is used to evaluate ACL rules.
The special uid
value "nobody"
is supported to represent the fact that no user has logged in. In that case only public rules will be applied, ie. only graphs that are public according to the acl rules are accessed:
If system permissions for SQL
users should be taken into account (in addition to the ACL rules) - obviously this only applies to service ids that actually map to a SQL
account - then the mapped username (or uid) needs to be set via:
Typically one would use the value provided by VAL.DBA.get_authentication_details_for_connection().
Also the application realm for the ACL rules can be changed from the default oplacl:DefaultRealm
via a connection setting before issuing the query:
Finally it is highly recommended to cache the WebID profile (in the case of WebID authentication) in a temporary graph and set that graph via:
That way the callback can pick it up and reuse it for all permission checks. Otherwise the profile has to be fetched for every permission check which results in a considerable performance drop. The simplest way to achieve this is to let VAL.DBA.get_authentication_details_for_connection() fetch the data by providing the temporary graph IRI to it and clearing the graph after executing the sparql query.
nobody
has access to ACLs will not be evaluated.See Enforcing SPARQL ACL Rules for an example.
It is recommended to run sparql queries as user dba
since the Virtuoso graph security system only allows to lower permissions via the callback, not raise them. Thus, we need a user that has access to all graphs, including private graphs.
The ACL system is generic in that it can be used to protect any resource clients with to protect. It does, however, include some special handling of Virtuoso DAV resources:
dav:/DAV/home/demo/foobar.txt
.http
vs. https
) and virtual dir. An example could look like http://www.host.com/files/demo/foobar.txt
which would use a virtual dir /files
-> /DAV/home
.By installing VAL three hook procedures are created which integrate the ACL system with DAV:
These procedures are used by the DAV system to check the ACLs setup in the DAV scope (VAL.DBA.get_dav_scope()). The most interesting hook is DB.DBA.DBEV_RES_CREATION_POST(). It will create the necessary grant rules which allows a 3rd-party account without a connected SQL account to control newly created resources. This is very important as account like that cannot own DAV resources in a classical way. In fact, new resources created in such a context will have classical unix-style permissions of 000000000–
and be owned by the nobody
user. This ensures that only the creating user can actually read and write those resources.
See ACL Rule Examples for examples of DAV access rules.
Besides classical ACL rules VAL allows to create restrictions. Restrictions can be seen as configuration values that are specific for a given person. Other than rules they do not grant rights but maximum or minimum limits on certain values. A typical example are request rates on an HTTP connection.
Restrictions, like rules, have a resource which they restrict. This is denotes by oplres:hasRestrictedResource
. In addition one resource can have multiple restrictions attached to it by using the optional oplres:hasRestrictedParameter
.
The internal VAL API provides some procedures to check restriction values:
In addition there is the public HTTP and RESTful APIs.
See The RESTful ACL API and VAL Public ACL HTTP API.
See VAL Internal ACL API.
See VAL Internal ACL Utility API.
Example: Simple ACL rule sharing named graph urn:foobar
with facebook
ID http://www.facebook.com/john.tester
:
The scope oplacl:PrivateGraphs is reserved by VAL to handle rules granting permissions to named graphs.
Example: ACL rule allowing Mozilla Persona identity harry @gma il.co m
to create ACL rules on named graph urn:foobar
:
Example: ACL rule granting read/write permissions to a group of ids:
Given that a group with IRI http://HOST/acl/groups/42
(substitute HOST
with the appropriate value) was created before.
For examples of groups including conditional ones see ACL Group Examples.
Example: ACL rule which grants generic access to DAV resource /DAV/home/demo/foobar
.txt for LinkedIn ID horstmeier:
This rule allows horstmeier
to access the DAV resource via any protocol and any virtual directory.
Example: ACL rule which grants http-only access to DAV resource /DAV/home/demo/foobar
.txt for LinkedIn ID hildemeier:
This rule allows hildemeier
to access the DAV resource only via http.
Example: ACL rule which grants http-only access to DAV resource /home/demo/foobar
.txt for SQL user joey:
This rule allows joey
to access the DAV resource only via http and only via the /home
virtual dir serving that path.
Example: Recursive ACL rule which grants access to all DAV resources below /DAV/home/demo/Public/
to everyone (Caution: recursive DAV rules should always use a trailing slash for the resource URL):
SPARQL
INSERT
(not recommended because it circumvents the security checks for ownership) be aware that the realm needs to be set on each rule: Example: A simple group which enumerates its members
Example: A conditional group which includes every validated X.509 client certificate. In other words: A rule which uses this group as an agent grants access to anyone who can provide a verifiable X.509 client certificate.
Example: A conditional group which includes every validated WebID. In other words: A rule which uses this group as an agent grants access to anyone who can provide a valid WebID certificate.
Example: A conditional group which includes every validated identity (aka NetID). In other words: A rule which uses this group as an agent grants access to anyone who can provide a valid identifier (NetID). This includes WebIDs, Facebook accounts, OpenIDs, etc..
Example: A conditional group which includes identities based on a SPARQL
ASK
query. The conditional group defines one condition that has a query which simply checks if the authenticated user is mentioned as an object in a foaf:knows
statement. So any identifier which is foaf:known
to ODS user trueg
will be part of the group.
Example: A conditional group which includes any WebID that claims to be a person in their profile. ^{graph}^
will be replaced with the profile graph corresponding to the authenticated identity, while ^{uri}^
will be replaced with the authenticated service identifier URI.
SPARQL
INSERT
(not recommended because it circumvents the security checks for ownership) be aware that the realm needs to be set on each group: Restrictions, like rules, always apply to an agent or agent class.
Example: Restriction which sets a maximum of 1000 result set rows for the Virtuoso /sparql endpoint:
Example: Restriction which makes an exception to the above rule by allowing a group of premium customers to query more rows:
This example depends on a static group which enumerates the premium customers:
SPARQL
INSERT
(not recommended because it circumvents the security checks for ownership) be aware that the realm needs to be set on each restriction: The following examples assume that a user session has been created via one of the login pages like /sparql
or, if using a non-browser client, via /val/api/login
(see VAL.VAL.login) or through ODS. Other authentication mechanisms like HTTP digest login or OAuth are also supported, but they will always result in usage of the default realm.
Example: Obtain a session ID using /val/api/login with WebID authentication:
Example: Obtain a session ID using /val/api/login with HTTP digest authentication:
Example: Create an ACL rule using the GET
-based ACL API:
Example: Create an ACL rule using the REST
ful API. reading the rule from file rule.ttl
:
Example: Add a member to a group using the REST
ful API:
Example: Set the member of a group, overwriting the existing ones:
Example: List the groups in the authentication realm scoped to the sid:
Example: Create an ACL rule using the REST
ful API:
We use a jQuery ajax
request to perform a POST
. We do authentication via a URL parameter. The dataType
should be set to text
to prevent jQuery from trying to hopelessly detect our default text/turtle
return type.
Example: Add a member to a group using the REST
ful API:
As before authentication information is set via a URL parameter.