12. Access Control¶
This section introduces RDFox®’s access control system. The key concepts of the system are described first, followed by sections on authentication and authorization. Next, there is a walkthrough of how to secure and RDFox server. The section then ends with some examples of access control policies.
Access control settings may be persisted between sessions or for replication purposes. See Section 13 for more information.
12.1. Key Concepts¶
To work with RDFox access control, it is essential to understand the concepts privilege and agent which are described in the following sections.
12.1.1. Privileges¶
In RDFox, privileges to access the system are expressed as pairs of resource specifiers and access type lists. The resource specifier identifies a subset of the server’s resources, and the access type list specifies the ways which those resources may be accessed by holders of the privilege. The concepts resource, resource specifier, and access type are described in turn over the next three sections.
12.1.1.1. Resources¶
A resource represents a component of an RDFox server to which access can be controlled. As described in Section 4, the set of resources within each server form a containment hierarchy with the server itself at the root.
Every resource has a unique name relative to the server. To make it clear which resources are contained within other resources, the names of contained resources begin with the name of the resource that contains them. Combined with the recursivity specifier (see Resource Specifiers), this also makes it easy to specify simple policies succinctly.
The following table shows the different types of resource within an RDFox
server with the corresponding resource name format. Braces ({}
) indicate
list-element segments.
Description |
Resource name format |
---|---|
The server itself. |
|
The list of requests running on the RDFox endpoint. |
|
The list of data stores within a server. |
|
A specific data store within the server. |
|
The Datalog program loaded inside a data store. |
|
The OWL axioms loaded inside a data store. |
|
The commit procedure of a data store. |
|
The list of delta queries registered with a data store. |
|
A specific delta query registered with a data store. |
|
The list of data sources registered with a data store. |
|
A specific data source registered with a data store. |
|
The list of tuple tables within a data store. |
|
A specific tuple table within a data store. |
|
A specific named graph within a data store. |
|
The list of roles in the server’s role database. |
|
A specific role in the server’s role database. |
|
Note
Graph names appearing in access control resource names must be valid Turtle literals. For convenience, IRIs may be specified using prefixes defined within the referenced data store, or as relative IRIs against the data store’s base IRI. Named graph resource names will usually be output using fully-expanded IRIs in the graph name segment, regardless of how they were input.
12.1.1.2. Resource Specifiers¶
Resource specifiers enable administrators to grant access to sets of resources using a single privilege by defining a syntax for specifying groups of resources. All valid resource names are also valid resource specifiers. Additionally, resource specifiers may have the following elements:
*
, the list element wildcard. This may appear as the final segment of a resource specifier if and only if that segment is a list-element segment, and signifies that a privilege applies to all possible values in that segment as determined at the time authorization is checked. For example, in a server containing rolesa
,b
andc
and no other roles, the resource specifier|roles|*
specifies exactly the resources|roles|a
,|roles|b
and|roles|c
. If roled
is added, it too will be included in the set specified by|roles|*
.>
, the recursivity indicator. A specifier beginning with>
instead of the initial|
character specifies the same resources that would be specified by the non-recursive version plus all of their subordinate resources. For example, in a server with a data store calledds
containing data sourcesone
andtwo
, the resource specifier>datastores|ds|datasources
specifies exactly the resources|datastores|ds|datasources
,|datastores|ds|datasources|one
and|datastores|ds|datasources|two
. It is an error to use the recursivity indicator to begin the name of a resource with no subordinate resources.
The list element wildcard and recursivity indicator may be combined freely
(subject to their individual rules). For example, if we want to grant a
privilege over every part of every data store within a server but not over the
list of data stores itself, we can use the resource specifier
>datastores|*
. To also include the list of data stores, we can use
>datastores
.
Resource specifiers have their own set of escaping rules which ensure that they do not impose any restrictions on list element names. The rules are as follow:
Names beginning with
*
must have the initial*
escaped as**
. For example the correct specifier for a role with name*abc
would be|roles|**abc
. No escaping is required for*
s at any other position within a name.Names containing
|
must have each|
escaped as||
. For example, the correct specifier for a data store with namemy|store
would be|datastores|my||store
.
12.1.1.3. Access Types¶
An access type describes how a resource is accessed.
RDFox defines three access types; read
, write
and grant
. The
read
access type covers all retrieval of information from the system. The
write
access type covers addition, mutation and deletion of information.
The grant
access type is used to restrict which roles can grant and revoke
privileges over a resource to or from other roles.
Multiple access types can be specified at once in a comma-separated list, e.g.,
read,write
. To save typing in situations where a role should have full
control of one or more resources, RDFox accepts the access type name full
which allows all of the above access types. Note that the full
privilege
for a resource specifier is stored separately from the read
, write
and
grant
privileges. This means that revoking full
access type must be
done explicitly, i.e. revoking read
access from a role which has full
access has no impact on what the role can actually do.
12.1.2. Agents¶
In the context of RDFox access control, an agent is a person or system that uses an RDFox server. RDFox’s access control system enables the administrator of a server to control which agents can authenticate to the server and what level of access they have once authenticated.
There are two important classes of agent: roles, which are local to the RDFox server, and external agents. These concepts are explored in more detail in the next two sections. Authentication of agents is described further down this page in Section 12.2.
12.1.2.1. Roles¶
Roles are agents that are stored directly within an RDFox server. Roles can be created for individual users, or for groups of users. Although the concept of a role is common to many access control systems, this document uses the term to refer exclusively to RDFox roles.
Roles may have privileges to access the system assigned to
them directly. They can also gain privileges through inheritance by becoming
members of other roles. The effective privileges available to a role are the
union of those directly assigned to it with those directly assigned to all
roles of which it is a member. Note that membership is a transitive
relationship so that if role C
is a member of role B
and B
is a
member of of role A
then role C
is also a member of role A
.
Roles can be created, deleted, and organized hierarchically using APIs (see
Section 16.18) or shell commands (see Section 15.2.38).
When a role is created, it can either have a password or not. Roles created
with a password can authenticate in local shell sessions (i.e. using the RDFox
executable’s shell
mode) and may change their passwords later on whereas
roles created without a password cannot authenticate in local shell sessions
and cannot have a password set at any later time. If a password is needed for a
role that doesn’t have one, the role can be deleted and recreated with a
password.
12.1.2.2. External Agents¶
An external agent is any agent that does not have a corresponding role. Such agents must be authenticated by a trusted third-party system.
Unlike roles, external agents cannot hold privileges directly so must obtain privileges by inheritance from their super roles (the roles of which they are a member). This requires the third-party system that manages the agent identity to maintain the list of RDFox roles that the agent is a member of and to supply this list as part of the authentication process.
12.2. Authentication¶
Authentication is the process of verifying the identity of an agent. In RDFox, agent authentication is performed either by one of the configured authentication managers, or, in the case of the endpoint, by cryptographic verification of a client’s TLS certificate. These two alternatives are described in the following sections.
12.2.1. Authentication Managers¶
Standard HTTP authentication schemes are configured in RDFox using
authentication managers. Each authentication manager has a corresponding HTTP
authentication scheme and, when enabled, is responsible for verifying the
authentication information received in requests using that scheme. At present,
RDFox includes two authentication managers: the built-in role manager, which
contains all the server’s roles and also implements the Basic
HTTP
authentication scheme, and the OpenID Connect authentication manager, which
implements the Bearer
HTTP authentication scheme. These are described in
the following sections.
12.2.1.1. Role Manager¶
Every RDFox server has a built-in role manager that contains one or more roles, along with a salted hash of their password, their memberships and their privileges. The roles contained by the role manager can be modified using the appropriate APIs (see Section 16.18) or shell commands (see the role, grant, and revoke commands).
The role manager is initialized as part of the initialization of a server and
has a range of responsibilities. All authentication attempts made to a local
shell session use the role manager directly, irrespective of the server
configuration. The role manager is also responsible for implementing the
Basic
authentication scheme for use in RESTful API requests. For this
purpose, it must be configured as one of the server’s authentication managers
by including role-manager
in the comma-separated list specified for the
authentication-managers server
parameter.
Finally, the role manager provides two services used in all agent authentication workflows: role retrieval and external agent privilege compilation. These two services are described in the following sections, after which the configuration parameters for the role manager are listed.
12.2.1.1.1. Role Retrieval¶
Role retrieval is the process of looking up a role after its identity has been authenticated by a trusted third-party system. Role retrieval will be successful only if the following conditions are met, otherwise resulting in an error.
The role being authenticated exists (that is, there is a role with the same name).
The
externally-authenticatable-role
parameter is set to a role that also exists.The role being authenticated is a member of the role mentioned in condition 2.
The role being authenticated does not have any members.
12.2.1.1.2. External Agent Privilege Compilation¶
External agent privilege compilation is the process of compiling the privileges for an external agent after its identity has been authenticated by a trusted third-party system. Since external agents cannot hold privileges directly, this process always requires the third-party system to supply a non-empty list of roles from which the agent should inherit privileges. External agent privilege compilation will be successful only if the following conditions are met, otherwise resulting in an error.
The agent name does not match that of any of the server’s roles.
The role manager’s
externally-grantable-role
parameter is set to a role that does exist.Every role in the list of roles specified by the third-party system exists and is a member of the role mentioned in condition 2.
12.2.1.1.3. Role Manager Configuration Parameters¶
The following table lists the configuration parameters for the role manager. To
specify these parameters, pass them as server parameters, prefixed with role-manager.
.
Option |
Value |
Description |
---|---|---|
|
a non-negative integer |
The amount of memory the role manager will use to generate Argon2i password
hashes in kibibytes. If not zero, this parameter must be at least 8 times the
This parameter, and the others beginning |
|
a non-negative integer |
The number of iterations of the core Argon2i hashing algorithm that the role
manager will use to generate Argon2i password hashes. See the comments for the
|
|
a non-negative integer |
The number of independent computational chains that the role manager will use
to generate Argon2i password hashes. See the comments for the
|
|
a string |
The name of a role whose members may be authenticated by a third-party system (see conditions 2 and 3 in Section 12.2.1.1.1). Setting this parameter does not create the role automatically: this must be done separately using a role creation API call or shell command. |
|
a string |
The name of a role whose members may be granted to external agents (see Section 12.2.1.1.2). Setting this parameter does not create the role automatically: this must be done using a role creation API call or shell command. |
12.2.1.2. OpenID Connect Authentication Manager¶
The OpenID Connect (OIDC) Authentication Manager allows users to authenticate
as either a role or an external agent using OIDC tokens. To enable this role
manager, include oidc
in the comma-separated list specified for the
authentication-managers server
parameter.
When enabled, the role manager will be responsible for verifying the
authentication information received in requests that use the Bearer
HTTP
authentication scheme.
The following table list the configuration parameters for the OIDC
Authentication Manager. To specify these parameters, pass them as server
parameters, prefixed with oidc.
.
Option |
Value |
Description |
---|---|---|
|
a string |
The name of the claim from the received JWT
token which will be used as the agent name. The
default value of this parameter is |
|
a string |
The identifier for the current RDFox instance.
The |
|
a string |
The identifier for the OpenID provider’s issuer.
The |
|
a string |
The name of the claim from the received JWT token which will be used as the agent name. This parameter has no default and may be left unset (see comments below this table). |
When authenticating a JWT, the OIDC authentication manager uses the
availability of a roles claim to decide how to complete the authentication. If
the roles-claim
parameter is not set or if it is set but the JWT in
question does not have a claim with the specified name, the OIDC authentication
manager will ask the role manager to complete the authentication process using
role retrieval. If the roles-claim
parameter is set
and the JWT in question does have a claim with the specified name, the OIDC
authentication manager will ask the role manager to complete the authentication
process using external agent privilege compilation. Note that both of these options depend
on the configuration of the role manager.
12.2.2. Authentication Using Client TLS Certificates¶
In addition to the role managers described above, clients of the RDFox HTTP endpoint may authenticate using client TLS certificates. This authentication mechanism is described in detail in Section 16.2.1.2.
12.3. Authorization¶
Authorization is the process that ensures each agent can only use the system in the ways permitted by the administrator. This section first describes the general principles of authorization in RDFox and then explains three special cases that deviate from the general principles in some way.
12.3.1. General Authorization Principles¶
In general, RDFox ensures that agents cannot exceed their authorization by checking, at the beginning of an operation, that the agent has privileges to perform the accesses that will be performed by the operation. If the agent does not have the necessary privileges, the operation is aborted and an error message is returned describing the first missing privilege.
Creating new resources (data stores, data sources, tuple tables or roles)
requires a write privilege over the containing list resource, e.g., over
/datastores
for creating data stores. Deleting resources requires a write
privilege over both the containing list resource and the element to be
deleted.
Granting and revoking privileges requires two privileges: grant
over the
resource to which access is being granted, and write
over the role which
will receive or, in the case of revocation, lose the privilege.
Example: Privileges needed to grant privileges over a data store
Granting the power to create data stores to a role named
datastore-creators
requires privileges sufficient for the following
accesses:
Resource |
Type of access |
---|---|
|
|
|
|
It is important to note that a privilege granted using one resource specifier
can only be revoked with the exact same resource specifier. It is not, for
example, possible to grant read access over all data stores using resource
specific >datastores
but then block access to a specific data store by
revoking read access over a specific data store.
Granting and revoking membership of one role to another role works in a similar
way. To grant membership of a role requires two privileges: grant
over the
role being granted and write
over the role which will receive or, in the
case of revocation, lose the membership.
Example: Privileges needed to grant membership of a role
Granting role group
to role user1
, requires privileges sufficient to
allow these accesses:
Resource |
Type of access |
---|---|
|
|
|
|
12.3.2. Authorization Special Cases¶
This section documents four special cases where authorization does not follow the rules described above.
12.3.2.1. Distinct Privileges for List and List Elements¶
List operations follow the general authorization principle in that either the agent has read access to the list and thus can perform the list operation, or does not have read access and thus cannot. In the case the agent can perform the listing, the names of all list elements are returned. Additionally, if the agent has read privileges over a list element, the response may also include that element’s properties (e.g., whether the data store is online). This can result in agents with different privileges receiving different responses to the same operations.
This extra information may be added for data stores, data sources and tuple tables.
Consider an RDFox server with two data stores: ds1
and ds2
. Agent
admin
has read access over all resources, while agent client
has
read access to the list of data stores and to ds1
but not to ds2
.
When admin
queries the list of data stores, the response will name
ds1
and ds2
and include properties of both. When client
makes
the same query, their response will name both data stores but will only
include properties of ds1
.
12.3.2.2. Authorizing Named Graph Access¶
Access to named graphs is not enforced by up-front authorization checks which abort the operation if they fail. Instead, RDFox takes a different approach to ensuring that an agent cannot exceed its named graph access privileges. This is because it is not possible to predict with certainty which named graphs will be accessed by a query without evaluating the query.
For read access to named graphs, RDFox enforces the server’s access control policy by behaving as if any named graph or graphs that are not readable by the current agent do not exist. This has the consequence that different agents may receive different answers for the same query depending on which set of named graphs they have privileges to read.
For write accesses to name graphs, RDFox enforces the server’s access control policy by aborting the operation on the first attempt to write to an unwritable named graph.
Note
All operations that read or write to named graphs require privileges to
access the containing data store and its Quads
tuple table. This
requirement is enforced using an up-front check as described in the section
on general authorization principles.
SPARQL updates involving DELETE/INSERT WHERE
, COPY
, ADD
, or
MOVE
statements, are separately sensitive to which graphs are readable and
writable as illustrated in the following example.
The following SPARQL update copies all of the triples from graph :G1
to
graph :G2
. Let’s assume that graph :G1
is not empty and that the
prefix :
is defined in the data store.
INSERT { GRAPH :G2 { ?S ?P ?O } }
WHERE { GRAPH :G1 { ?S ?P ?O } }
Imagine we have a role with read and write privileges over the Quads
table within the data store but no privileges over any named graphs. When
the role invokes the above query, no authorization error is raised even
though the INSERT
clause specifies an unwritable graph. This is because
access control prevented the body of the query (the WHERE
clause) from
matching the triple in the unreadable graph :G1
so there was no attempt
to write a triple into the unwritable graph :G2
.
If we grant the role a read privilege over graph :G1
and reevaluate the
update (on a fresh connection), an error will be produced. This time, the
query body was allowed to match the triple in :G1
and an attempt to
write it to :G2
was made. This resulted in the operation being
immediately aborted.
Finally, if we grant a write privilege over graph :G2
and reevaluate
once more (again, on a fresh connection), no error is produced. If we were
to inspect graph :G2
as a role with read permission over it, we would
find the triples from :G1
there.
12.3.2.3. Authorizing Request Access¶
Requests running on the RDFox endpoint are transient (i.e., they come and go as they are submitted) and so it is not possible to control access to each request individually. Instead, RDFox observes the following rules for governing access to requests.
Each role has full control over all requests it creates.
For requests created by other roles, each role inherits the privileges of the
|requests
resource. Thus, a role with write access to the|requests
resource is allowed to cancel any request running on the system.
12.3.2.4. Authorizing a Role’s Access to Itself¶
In most situations, access to a resource is determined solely by the policies set by the user of the system; however, there are two built-in policies controlling a role’s access to itself which override the user’s policies. The first of these ensures that every role can read its own privileges and memberships and the second policy prevents any role from writing its own privileges and memberships.
12.4. Securing an RDFox Server¶
This section gives practical information on how to set up a secure, persistent RDFox server. Examples are given for the RDFox executable and shell but all steps can also be achieved programmatically using the APIs described in Section 16.
12.4.1. Initialization¶
At least one role must exist within an RDFox server’s role database before that
server can be used. When the role database is empty at startup, either because
the server directory has not been initialized for role persistence, or because
role persistence is disabled, RDFox will check the process’s command line
arguments and environment variables for the name and password to use for the
first role. If either value is missing after that, the behavior depends on the
executable mode in use. In init
mode, the user is prompted to provide the
missing values as follows:
Enter the name of the first role: admin
Enter the first role password:
Confirm the password:
See Section 18 for more details on how to start the RDFox executable.
When using role persistence, RDFox will next determine the parameters for hashing passwords on the local hardware. This may take a minute or more but will only happen the first time persistence is used with a given server directory. A successful initialization will continue as follows:
Initializing access control (may take a minute or more)...
Access control has been initialized by creating the first role with name "admin".
This concludes the initialization process and the process will now exit. At
this point, the first role holds the privilege full >
, giving it full
control over the server and all its resources, including the power to configure
access control. Next, we launch a shell
mode process which will ask for
login credentials. Once authentication is successful, the shell prompt is
returned.
A new server connection was opened as role 'admin' and stored with name 'sc1'.
>
Note
RDFox uses argon2i to compute hashes for storage when role persistence is enabled.
12.4.2. Managing Roles¶
In the shell, the role command provides several subcommands for managing the roles in the system. It uses the shell’s active server connection to perform operations. To understand how the shell manages server and data store connections, see Section 15.1.
The command role create <role-name>
creates a new role, prompting for the
role’s password to be provided and confirmed. For example:
> role create user1
Enter the password for the new role:
Confirm the password:
A new role was created with name "user1".
In some situations, it may be necessary to create a role without a password,
for example when the role will used solely to group other roles. This can be
achieved by adding the no-password
flag to the command:
> role create group no-password A new role was created with name “group”.
To list the roles in the server’s role database, use role list
. To delete a
role which is no longer needed, use role delete <role-name>
.
To change a role’s password, ensure that the active server connection is authenticated as the role whose password is to be changed and issue the password command. It is not possible to change the password of a role that was created without a password. If this is necessary, the role must be deleted and recreated with a password.
Note
Commands that require interactive password entry cannot be used in the remote shell.
12.4.3. Assigning Privileges Directly¶
In the shell, privileges are granted or revoked with the privileges
subcommand of the grant or revoke commands.
Privileges must be given as a list of access types followed by a resource
specifier. For example the following script grants read
, write
and
grant
privileges over all data stores to user1
and then revokes the
write
and grant
privileges:
> grant privileges read,write,grant >datastores|* to user1
The privileges 'read,write,grant' over the resource specifier ">datastores|*" were granted to the role "user1".
> revoke privileges write,grant >datastores|* from user1
The privileges 'write,grant' over resource specifier ">datastores|*" were revoked from the role "user1".
To check that the role’s privileges are now as we expect, we can use the
show
subcommand of the role command which prints all of the
specified role’s privileges, memberships and members:
> role show user1
Password hash for 'user1' is <password-hash>
'user1' has the following directly assigned privileges:
==============================================
Resource specifier Allowed access types
----------------------------------------------
>datastores|* read
==============================================
'user1' is a direct member of the following roles:
================
Memberships
----------------
================
The following roles are direct members of 'user1':
============
Members
------------
============
12.4.4. Discovering Privilege Requirements¶
Individual operations may require several privileges depending on which
resources they access. To determine which privileges are needed for a particular
operation, attempt the operation as a completely unprivileged role and examine
the resulting error message to identify the first required privilege. For
example, the error messages in the following shell session tells us that in
order to list the roles, a role must have a read
privilege over the
|roles
resource.
> srvconn open user1-connection as user1
Password for 'user1':
A new server connection was opened and stored with name 'user1-connection'.
> srvconn active user1-connection
Server connection 'user1-connection' is active.
> role list
An error occurred while executing the command:
The role 'user1' is not authorized to read the resource '|roles'.
Next, grant the missing privilege:
> srvconn active sc1
Server connection 'user1-connection' is active.
> grant privileges read |roles to user1
The privilege 'read' over the resource specifier "|roles" was granted to the role "user1".
Although the privileges were successfully granted to the role, the server
connection we opened earlier as user1
will not have been modified. To
exercise the new privileges available to user1
, we first close the existing
server connection and open and activate a new one:
> srvconn close
The active server connection was closed.
> srvconn open user1-connection as user1
Password for 'user1':
A new server connection was opened and stored with name 'user1-connection'.
> srvconn active user1-connection
Server connection 'user1-connection' is active.
Finally, retry the operation:
> role list
=========
Name
---------
admin
group
user1
=========
The command is now successful. For other commands, multiple iterations of the above process may be needed before the command succeeds. For security reasons, RDFox only reveals the missing privileges for one resource at a time.
12.4.5. Assigning Privileges via Membership¶
Roles can be arranged into hierarchies using the role
subcommand of the
grant command:
> srvconn active sc1
Server connection 'sc1' is active.
> grant role group to user1
Membership of the role 'group' was granted to the role 'user1'.
and removed from them using the role
subcommand of the revoke
command:
> revoke role group from user1
Membership of the role "group" was revoked from the role "user1" (if it was present).
A role may be a member of as many other roles as is necessary however RDFox will prevent cycles: a role cannot be a member of itself either directly or indirectly. RDFox will also not allow roles with one or more members to be deleted.
12.4.6. Securing the REST Endpoint¶
On receipt of an anonymous REST request (one with no Authorization
header
or TLS-authenticated role name), RDFox will attempt to create a new connection
for the request by authenticating with role name guest
and password
guest
. To enable anonymous REST access, administrators should therefore
create the guest
role and grant it the privileges to perform any operation
which should be available to any client that can reach the REST endpoint. As
a corollary, anonymous REST access can be completely disabled by ensuring the
absence of a role named guest
from the server’s role list. Note that RDFox
will not allow the guest
role to be created with any password other than
guest
, nor will it allow this role’s password to be changed.
12.4.7. Testing the Setup¶
Verifying that your access control policy is correct is an essential step in establishing a secure RDFox server. Oxford Semantic Technologies recommends comprehensive tests including both positive (checking that a given role can perform the operations it should be authorized to perform) and negative (check that a given role cannot perform the operations it should not be authorized to perform) tests.
12.4.8. Persistence¶
When persistence (see Section 13) is enabled, RDFox saves relevant parts of the server’s content to the server directory. An attacker with access to that directory will be able to tamper with any persisted access control policies. For this reason, it is vital to also tightly control access to the server directory when role persistence is enabled.
12.5. Example Access Control Policies¶
This section contains several additional examples of access control policies.
The scripts in this section are written assuming they are being run by a role
with the all-powerful full >
privilege as is assigned to the first server
role.
12.5.1. Anonymous, Read-only REST Access¶
The following shell example shows how to enable anonymous (unauthenticated), read-only access to the entire server:
> role create guest
A new role was created with name "guest".
> grant privileges read > to guest
The privilege 'read' over the resource specifier ">" was granted to the role "guest".
> endpoint start
The REST endpoint was successfully started at port number/service name 12110 with 11 threads.
See Securing the REST Endpoint for more details.
12.5.2. Delegating Administration of a Data Store¶
To minimize the work that must be done as the most powerful role, it may be desirable to create separate roles to administrate each data store. To do this, first create the data store and its administrator role and ensure that the role has full control over the data store as follows:
> dstore create ds
A new data store 'ds' was created and initialized.
> active ds
Data store connection 'ds' is active.
> role create ds-admin
...
A new role was created with name "ds-admin".
> grant privileges full >datastores|ds to ds-admin
The privilege 'full' over the resource specifier ">datastores|ds" was granted to the role "ds-admin".
Finally, to ensure that ds-admin
is able to grant and revoke privileges over
the data store to and from other roles, we grant it write access over all roles
as follows:
> grant privileges read |roles to ds-admin
The privilege 'read' over the resource specifier "|roles" was granted to the role "ds-admin".
> grant privileges read,write |roles|* to ds-admin
The privileges 'read,write' over the resource specifier "|roles|*" were granted to the role "ds-admin".