Vertex-Level Access Control (Beta)

This feature is currently in Beta mode (not production-ready)

Updated December 21, 2020: Vertex Tag Functions described. Organization and examples improved.

Vertex Level Access Control, a.k.a. VLAC, is an important extension to TigerGraph's MultiGraph and Role-Based Access Control (RBAC) capabilities, providing finer grain data access control. The original MultiGraph feature allows an administrator to define multiple updatable views (called base graphs) of the global graph. Each view is a subgraph, defined by specifying a subset of vertex types and edge types to be included.

VLAC takes data access control to the next level by presenting updatable views based not only on types but also on tags, where tags are properties attached to individual vertices. Admin users can define tags, define tag-based graphs, grant privileges to other users on these tag-based graphs, and explicitly set and clear tags on data.

Figure 1. Tag-based graphs as subgraphs of a Base graph

For users operating on a tag-based graph, tags are an invisible aspect which silently filters how they load and query data. Just as in MultiGraph, a tag-based graph defines their view, and all data outside their view is invisible to them. VLAC therefore provides a fine-grained security and access control feature.

We will first describe how to manage tags: creating tags, dropping tags, and using tags to define tag-based graphs. Next, the three ways to tag vertices are described and illustrated. We summarize the privilege scheme of tag-based graphs in terms of GSQL's predefined roles. Finally, we give some use cases that can be solved by VLAC.

Features not yet supported:

  1. DDL tag operations can only be done in GSQL. They are not yet supported in GraphStudio. This includes create/drop tags, create/alter vertices which are taggable, define a tag-based graph.

  2. REST endpoints for loading and querying data do not yet apply tag-based access control.

  3. The privilege control for DDL operations (only admin and designer users should be able to explicitly manage tags) is not fully functional.

In summary, all necessary operations to set up VLAC graphs and users are supported in GSQL. Once set up, the implicit tagging and access control for loading and querying works in GSQL or GraphStudio. Due to a known bug, standard users (querywriters and queryreaders) can run some DDL operations which they should not be able to.

We’ll use the graph socialNet as an example in the following sections.

Workflow - Using Vertex Level Access Control

To use VLAC, a user with schema-editing privilege (e.g., the admin or designer built-in roles) first needs to define the tag set and the schema for one or more tag-based graphs. Then two more things need to happen: data needs to be assigned to a tag-based graph, and a user-admin needs to grant access privileges on the graph to some users. Then these users can begin routine access to the tag-based graph as though it were any other graph. This routine access includes adding more data to the graph.

Setup: Tag-based Graph Creation

  1. Define tags (usable by multiple graphs).

  2. Define a tag-based graph (a.k.a. a tag-view).

Setup and Ongoing: User Management

Grant users a role or privilege on a tag-based graph. This works the same as in MultiGraph.

Setup and Ongoing: Load Data

There are three main options for populating a tag-based graph with data:

  1. For existing data, a user with base-graph tagging privilege (e.g., an admin or designer) can create and run a DML query which sets tags on selected individual vertices.

  2. For new data, a user with base-graph loading and tagging privilege (e.g., an admin or designer) can create and run a Loading job which explicitly sets tags on the new vertices.

  3. For new data, a user with tag-based graph loading or insert privilege (e.g., a designer or querywriter) can create an ordinary Loading or Upsert Job which inserts new vertices. The new vertices will be automatically tagged according to the graph's schema definition.

Ongoing: Query and Update Data

Users with data read and write privilege (e.g., the querywriter and queryreader built-in roles) can query and update the graph. These queries do not explicitly mention the tags and can be written exactly like non-tagged based queries. The data filter is applied automatically due to the privilege they are currently asserting.

Tags

A tag is a special attribute of a vertex, which appears as a string for input and output purposes. If a vertex type is declared to be taggable, then each vertex of that type can have one or more tags. The maximum number of different tags is 64.

ADD DROP - Define a Tag

A tag name has to be defined before it can be used. Each base graph defined its own set of tags. However, there is a global maximum number of different tags, currently set at 64.

Syntax for ADD TAG
ADD TAG <tag_name> DESCRIPTION <tag_description>

ADD TAG can only be used inside a SCHEMA_CHANGE JOB. An example is below:

USE GRAPH socialNet
CREATE SCHEMA_CHANGE JOB add_tags {
ADD TAG public DESCRIPTION "Open for public";
ADD TAG tech DESCRIPTION "All about technology";
ADD TAG vip DESCRIPTION "Very Important Person";
ADD TAG dummy DESCRIPTION "Yeah, just a dummy";
}
RUN SCHEMA_CHANGE JOB add_tags

Listing the Defined Tags

Afterwards, we can see the newly created tags by running ls:

...
Tags:
- TAG public DESCRIPTION "Open for public"
- TAG tech DESCRIPTION "All about technology"
- TAG vip DESCRIPTION "Very Important Person"
- TAG dummy DESCRIPTION "Yeah, just a dummy"

DROP TAG

Dropping a tag not only removes it from the catalog of available tags, but it also deletes it from each vertex to which it is attached. You can drop multiple tags in one statement.

Syntax for DROP TAG
DROP TAG <tag_name> ["," <tag_name>]

Like ADD TAG, DROP TAG also needs to be inside a SCHEMA_CHANGE JOB:

USE GRAPH socialNet
CREATE SCHEMA_CHANGE JOB drop_dummy_tag {
DROP TAG dummy;
}
RUN SCHEMA_CHANGE JOB drop_dummy_tag

Notes:

  1. You cannot drop a tag that is used in the definition of a tag-based graph. You must drop the graph first.

  2. When a tag is dropped, an asynchronous query runs in the background to remove the tags from all data. In the meantime, the dropped tag still takes up one of the 64 slots for tags. The slot will become available once the background process finishes.

Tag-Based Graphs

A tag-based graph is a filtered view of a base graph, where a base graph is a simple collection of vertex types and edge types, without any tag specifiers. A tag-based graph can only be constructed using vertex types that are taggable.

TAGGABLE Vertex Types

A Vertex type has to be taggable to accept tags. TAGGABLE is a boolean property of a vertex type which can be set with CREATE VERTEX initially or with ALTER VERTEX in a schema change job:

USE GRAPH socialNet
CREATE VERTEX alien (PRIMARY_ID id STRING) WITH TAGGABLE="true";
# in general, this would be a local schema change job, but in socialNet, the
# vertex types are global, so this needs to be a global schema change job
CREATE GLOBAL SCHEMA_CHANGE JOB make_taggable {
ALTER VERTEX person WITH TAGGABLE="true";
ALTER VERTEX post WITH TAGGABLE="true";
}
RUN GLOBAL SCHEMA_CHANGE JOB make_taggable

Note: The property TAGGABLE is false by default.

Notes:

  1. To change a vertex type from taggable to untaggable, use WITH TAGGABLE="false".

  2. You cannot make a vertex type untaggable if it used in the definition of a tag-based graph.

  3. Edge types are never tagged. See the next section to see how we determine which edges to include in the tag-based graph.

Create a Tag-Based Graph

After a tag set and taggable vertex types have been created, we can use the tags to define a tag-based graph. For each vertex type we want to include, we may also specify a tag expression which must be satisfied in order for an individual vertex to be included. Currently, the only tag expressions supported are a conjunction of tags using the & operator, e.g., tech & vip. This means that a vertex must be tagged with both tech and vip to be included in the tag-based graph.

Examples

Here is an example of creating a tag-based graph from the base graph socialNet.

USE GRAPH socialNet
CREATE GRAPH vipNet AS socialNet(person:vip, post, friend, posted, liked)

The interpretation is: "Starting from the socialNet graph, create a tag-based graph called vipNet which includes person vertices which are tagged 'vip'. Also include all post vertices and all friend, posted and liked edges."

Note that edges may not have tag expressions. An edge will be included when both of its vertex endpoints are included (and its edge type is included).

Here is an example with more complex tag expressions:

USE GRAPH socialNet
CREATE GRAPH mixedNet AS socialNet(person:public&vip, post:public&tech, friend, posted, liked)

The graph mixedNet will only include the person vertices having both the public and viptags, and posts having both the public and techtags.

Same Tag Expression for All Vertex Types

If the desired tag-based graph includes all vertex and edge types of the base graph and applies the same tag expression to all vertex types, we have a convenient shortcut:

USE GRAPH socialNet
CREATE GRAPH publicNet2 AS socialNet:public

is the same as

USE GRAPH socialNet
CREATE GRAPH publicNet1 AS socialNet(person:public, post:public, friend, posted, liked)

General Syntax

The formal syntax for both the general form and the simplified form of creating a tag-based graph is shown below:

Syntax for CREATE GRAPH for a tag-based graph
<create_tag_graph> :=
CREATE GRAPH <tag_graph_name> AS <base_graph_name>
( "(" <tagged_element_name> ["," <tagged_element_name> ")" | ":" <tag_expr> )
<tagged_element_name> := <tagged_vertex_name> | <edge_name>
<tagged_vertex_name> := <vertex_name> [":" <tag_expr>]
<tag_expr> := <tag> ["&" <tag>]

Vertex Tag Functions

Users with base graph tag editing privilege (e.g., admin or designer built-in roles) can use functions to inspect and modify the tags on a vertex. Users without tag editing privilege (e.g., querywriter or queryreader roles) cannot use these functions.

Like other vertex functions in GSQL, they take the form of object-oriented methods on a vertex alias: <vertex_alias>> . <function> these are only available for vertex aliases (defined in the FROM clause of a SELECT statement); they cannot be applied to vertex variable in other contexts.

function

description

return type

v.isTaggable()

Return true if v is of a taggable vertex type.

BOOL

v.getTags()

Return v's set of tags. If v is untaggable, it returns an empty set.

SET<STRING>

v.hasTags(

STRING tag1,... STRING tagN)

Return true if v has every tag in the argument list of tags.

BOOL

v.intersectTags(

VERTEX v2)

Return the set of tags that v and v2 have in common.

SET<STRING>

v.differenceTags(

VERTEX v2)

Return the set of tags that v has but v2 does not have.

SET<STRING>

v.addTags(

STRING tag1,... STRING tagN)

Add the given tags to v.

n/a

v.removeTags(

STRING tag1,...

STRING tagN)

Remove the given tags from v.

n/a

v.removeAllTags()

Remove all tags from V.

n/a

Tip: getTags() can be used within a PRINT statement:

  • PRINT R[R.getTags()];

  • or PRINT R WITH TAGS which is a syntax sugar, except that it won’t print "R.gettags()": [] for non-taggable vertex.

Loading and Inserting Data

There are three main options for populating a tag-based graph with data:

  1. For existing data, a user with base-graph tagging privilege (e.g., an admin or designer) can create and run a DML query which sets tags on selected individual vertices.

  2. For new data, a user with base-graph loading and tagging privilege (e.g., an admin or designer) can create and run a Loading job which explicitly sets tags on the new vertices.

  3. For new data, a user with tag-based graph loading or insert privilege (e.g., a designer or querywriter) can create an ordinary Loading or Upsert Job. The new vertices will be automatically tagged according to the graph's schema definition.

Load Data with Explicit Tagging

When data is loaded to the base graph, if the user has tag editing privilege (e.g., the admin or designer roles), they can explicitly set tags. Depending on what tags are used, this may make the data visible to one or more tag-based graphs.

The LOAD statement has an optional clause for explicit tagging of loaded data. The tagging clause has two keywords, TAGS and BY:

  • TAGS(<tag_list>) specifies the tags to be set.

  • BY specifies what to do if a vertex already exists in the graph (e.g., the id of the new vertex matches the id of an existing vertex):

    • BY(OR): Add the given tags to the existing set of tags.

    • BY(OVERWRITE): Replace the existing tags with the given ones.

Example 1

Suppose we want to put the tags vip and public on the person vertex data coming from a certain file. The data is split into three files:

$ cat persons1
person1,Male
person2,Female
person3,Male
person4,Female
person5,Female
$ cat persons2
person6,Male
person7,Male
$ cat persons3
id,gender,label
person8,Male,vip

We create and run three loading jobs with the new syntax:

USE GRAPH socialNet
# person1 ~ person5 will be tagged as public.
CREATE LOADING JOB loadPersonPublic {
DEFINE filename f;
LOAD f TO VERTEX person VALUES($0, $0, $1) TAGS("public") BY(OR);
}
RUN LOADING JOB loadPersonPublic USING f="./persons1"
# person6 and person7 will be tagged as public and vip.
CREATE LOADING JOB loadPersonPublicVip {
DEFINE filename f;
LOAD f TO VERTEX person VALUES($0, $0, $1) TAGS("public", "vip") BY(OR);
}
RUN LOADING JOB loadPersonPublicVip USING f="./persons2"
# person8 will be tagged as vip which is derived from the file.
CREATE LOADING JOB loadPerson {
DEFINE filename f;
LOAD f TO VERTEX person VALUES($0, $0, $1) TAGS($2) BY(OR) USING HEADER="true";
}
RUN LOADING JOB loadPerson USING f="./persons3"

Note that the TAGS clause can specify a tag with a string literal ("vip") so every vertex gets the same tag, or with a token reference by position ($2) or by name ($"label") from the source file, so each vertex gets a data-dependent tag. Note that if the tag clause refers to a nonexistent tag, the loading job will still run, but the data will not be loaded at runtime. The loading job log will report these non-loaded vertices.

Example 2

The posts data file is shown below:

$ cat posts1
0,Graphs,2010-01-12 11:22:05
1,tigergraph,2011-03-03 23:02:00
2,query languages,2011-02-03 01:02:42
3,cats,2011-02-05 01:02:44
4,coffee,2011-02-07 05:02:51
5,tigergraph,2011-02-06 01:02:02
6,tigergraph,2011-02-05 02:02:05
7,Graphs,2011-02-04 17:02:41
8,cats,2011-02-03 17:05:52
9,cats,2011-02-05 23:12:42
10,cats,2011-02-04 03:02:31
11,cats,2011-02-03 01:02:21

This loading job uses the WHERE clause to select certain lines in the input file to load and to tag. The lines with ids 1, 4, 5, 6, and 7 are loaded. Posts 1, 5, and 6 are loaded with tag "public". Posts 4, 5, 6, and 7 are loaded with tag "tech". Therefore, posts 5 and 6 will have both tags. Some lists are not loaded at all. (If you wanted to load all lines, another LOAD statement with no tags and no WHERE clause would do the job.)

USE GRAPH socialNet
CREATE LOADING JOB loadPostTwoTagConditions {
DEFINE filename f;
# posts about tigergraph (1, 5, 6) will be tagged with "public".
LOAD f TO VERTEX post VALUES($0, $1, $2) TAGS("public") BY(OR)
WHERE $1 == "tigergraph";
# posts with id between 4 and 7 will be tagged with "tech".
LOAD f TO VERTEX post VALUES($0, $1, $2) TAGS("tech") BY(OR)
WHERE to_int($0) BETWEEN 4 AND 7;
}
RUN LOADING JOB loadPostTwoTagConditions USING f="./posts"

Restrictions on OVERWRITE

It is prohibited to have a loading job which loads file(s) to one vertex type from multiple statements and to use OVERWRITE , because GSQL's parallel processing cannot guarantee the sequence of execution. If the sequence of execution is not deterministic, then OVERWRITE will lead to nondeterministic results

For example, the following jobs are semantically invalid:

#Example of illegal combination of loading statements
USE GRAPH socialNet
# load the same file to the same vertex type with OVERWRITE
CREATE LOADING JOB loadInvalid1 { // overwrite to the same type
DEFINE filename f;
LOAD f
TO person VALUES($0, $0, $1) TAGS("vip") BY(OR),
TO person VALUES($0, $0, $1) TAGS("public") BY(OVERWRITE); // rejected
}
# load different files to the same vertex type with OVERWRITE
CREATE LOADING JOB loadInvalid2 { // overwrite to the same type
DEFINE filename f1;
DEFINE filename f2;
LOAD f1 TO v1 VALUES($0, $0, $1) TAGS("vip") BY(OR);
LOAD f2 TO v1 VALUES($0, $0, $1) TAGS("public") BY(OVERWRITE);
}

In the same manner, starting a loading job using OVERWRITE will be rejected when there is already an active loading job which is loading to the same vertex type.

Load Data with Implicit Tagging

A loading data to a tag-based graph automatically tags each vertex with the tags specified in the graph's definition. E.g., when loading to vipNet, the person vertices will automatically be tagged with vip.

Note that these vertices are actually being added to the parent base graph. If two tag-based graphs have overlapping views (e.g., if graph vipNet2 also includes person:vip), then when one adds a vertex, the other graph may also see it.

USE GRAPH vipNet
CREATE LOADING JOB loadMember {
DEFINE filename f;
# TAGS("vip") BY(OR) will be applied implicitly
LOAD f TO VERTEX person VALUES($0, $0, $1);
}
RUN LOADING JOB loadMember USING f="./persons3"
  • Portability and Reusability: The same loading job works for socialNet or any graph derived from socialNet which contains person. The difference is in the effect: running it with vipNet will apply the vip tag. Running it with a different tag-based graph would apply different tags. Users of a given tag-based graph automatically insert and query data for that tag-view.

  • Tagging Shared Data: The default behavior of GSQL loading is upsert: if you attempt to insert a vertex or edge which already exists (e.g., uses an existing ID), you will instead update the existing element with the new attribute values. If the attribute is a list or set, the new values will be added to the existing list/set. This applies to tags. If you attempt to load an existing vertex, the new tag(s) will be added to any existing tags. Loading a vertex which already exists extends the tag set to be visible to the current graph.

Query a Tag-based Graph

The graph vipNet can only see the person with the tag vip. We can check this out by running the simple query:

USE GRAPH vipNet
CREATE QUERY findAll() {
seed = {person.* ;
result =
SELECT v
FROM seed:v
ORDER BY v.id;
PRINT result;
}
INSTALL QUERY findAll
RUN QUERY findAll()

The output of the query would be:

{
"error": false,
"message": "",
"version": {
"schema": 2,
"edition": "enterprise",
"api": "v2"
},
"results": [{"res": [
{
"v_id": "person6",
"attributes": {
"gender": "Male",
"id": "person6"
},
"v_type": "person"
},
{
"v_id": "person7",
"attributes": {
"gender": "Male",
"id": "person7"
},
"v_type": "person"
},
{
"v_id": "person8",
"attributes": {
"gender": "Male",
"id": "person8"
},
"v_type": "person"
}
]}]
}