Partitioned Graphs

You can partition graphs using the Record-level Security feature. This allows you to separate database records as sandboxes, where "restricted" records are inaccessible to unauthorized users. For more information about other solution for Multi-Tenant applications, look at Multi-Tenant.

This tutorial provides a demonstration of sandboxing with the Graph API and the TinkerPop stack. Partitioned Graph Databases allow you to build Multi-tenant applications.

Requirements:

  • OrientDB 1.2.0-SNAPSHOT or higher
  • TinkerPop Blueprints 2.2.0 or higher

Creating a Graph Database

To create a Partitioned Graph Database, you first need to create a database in which to partition. For instance, the example below covers creating a blog database of the Graph type on the local file system:

$ cd $ORIENTDB_HOME/bin
$ ./console.sh
OrientDB console v.1.2.0-SNAPSHOT www.orientechnologies.com
Type 'help' to display all the commands supported.

Installing extensions for GREMLIN language v.2.2.0-SNAPSHOT

orientdb> CREATE DATABASE local::../databases/blog 
          admin admin_passwd local graph
Creating database [local:../databases/blog] using the storage type [local]...
Database created successfully.

Current database is: local:../databases/blog

Enabling Partitioned Graphs

Once you have created the Graph Database instance, you need to enable partitioning. Alter the V and E classes to extend the ORestricted. Doing so allows you to restrict vertex and edge instances.

orientdb {db=blog}> ALTER CLASS V SUPERCLASS ORestricted
Class updated successfully

orientdb {db=blog}> ALTER CLASS E SUPERCLASS ORestricted
Class updated successfully

Create Users

With the database online and the vertex and edge classes altered to restricted, create two test users, (luca and steve), to use in exploring how sandboxing works on a Partitioned Graph Database.

These users represent users who blog on the application you're building. Their level of permissions and authorization relates to the writer role. First, ensure that the writer role exists on your database:

orientdb {db=blog}> SELECT RID, name, rules FROM ORole 
                    WHERE name = "writer"

---+------+--------+-----------------------------------------------
 # | RID  | name   | rules
---+------+--------+-----------------------------------------------
 0 | #4:2 | writer | {database=2, database.schema=7, 
   |      |        | database.cluster.internal=2,
   |      |        | database.cluster.orole=2, 
   |      |        | database.cluster.ouser=2,
   |      |        | database.class.*=15, database.cluster.*=15,
   |      |        | database.command=15, database.hook.record=15}
---+------+--------+-----------------------------------------------

3 item(s) found. Query executed in 0.045 sec(s).

Running these commands shows that you do in fact have a writer role configured on your database and that it uses a Record ID of #4:2. You can now create the users. There are two methods available to you, depending on which version of OrientDB you use.

Creating Users

Beginning with version 2.1, OrientDB now features a CREATE USER command. This allows you to create a new user in the current database, as opposed to inserting the credentials into the OUser and ORole classes.

To create users for Luca and Steve, run the following commands:

orientdb {db=blog}> CREATE USER luca IDENTIFIED BY luca_passwd
                    ROLE writer

orientdb {db=blog}> CREATE USER steve IDENTIFIED BY steve_passwd 
                    ROLE writer

The users are now active on your database as luca and steve.

Inserting Users

For older implementations of OrientDB, there is no CREATE USER command available to you. To add users before version 2.1, you need to use INSERT statements to add the new values into the OUser class, using the record ID for the writer role, which you found above as #4:2:

orientdb {db=blog}> INSERT INTO OUser SET name = 'luca', 
                    status = 'ACTIVE', password = 'luca_passwd', 
					roles = [#4:2]

Inserted record 'OUser#5:4{name:luca,password: {SHA-256}D70F47790F
689414789EEFF231703429C7F88A10210775906460EDBF38589D90,roles:[1]} 
v1' in 0,001000 sec(s).

orientdb {db=blog}> INSERT INTO OUser SET name = 'steve', 
                    status = 'ACTIVE', password = 'steve_passwd', 
					roles = [#4:2]

Inserted record 'OUser#5:3{name:steve,password: {SHA-256}F148389D0
80CFE85952998A8A367E2F7EAF35F2D72D2599A5B0412FE4094D65C,roles:[1]}
v1' in 0,001000 sec(s).

Creating Graphs

In order to work with the partition, you need to create graphs for each user on the database. This requires that you log out from the admin user and log back in as Luca and then Steve, then create vertexes and edges with which to work.

Create a Graph for Luca

First, using DISCONNECT and CONNECT disconnect from your admin session on the blog database and reconnect as Luca's user.

orientdb {db=blog}> DISCONNECT
Disconnecting from the database [blog]...OK

orientdb> CONNECT local:../databases/blog luca luca_passwd
Connecting to database [local:../databases/blog] with user 'luca'...OK

orientdb {db=blog}>

Now that you're logged in under Luca's user, using the CREATE VERTEX command, create two vertices: one for restaurants and one for pizza.

orientdb {db=blog}> CREATE VERTEX SET label = 'food', 
                    name = 'Pizza'
Created vertex 'V#9:0{label:food,name:Pizza,_allow:[1]} v0' in 0,001000 sec(s).

orientdb {db=blog}> CREATE VERTEX SET label = 'restaurant', 
                    name = "Dante's Pizza"
Created vertex 'V#9:1{label:restaurant,name:Dante's Pizza,_allow:[1]} v0' in 0,000000 sec(s).

Connect these vertices with an edge for menus, using CREATE EDGE:

orientdb {db=blog}> CREATE EDGE FROM #9:0 TO #9:1 SET label = 'menu'
Created edge '[E#10:0{out:#9:0,in:#9:1,label:menu,_allow:[1]} 
v1]' in 0,003000 sec(s).

You can check the status using SELECT against these vertices:

orientdb {db=blog}> SELECT FROM V

 ---+------+------------+---------------+-----------------
  # | RID  | label      | name          | _allow | out
 ---+------+------------+---------------+--------+--------
  0 | #9:0 | food       | Pizza         | [1]    | [1]
  1 | #9:1 | restaurant | Dante's Pizza | [1]    | null
 ---+------+------------+---------------+--------+--------
 2 item(s) found. Query executed in 0.034 sec(s).

Creating a Graph for Steve

Now let's connect to the database using the 'Steve' user and check if there are vertices:

orientdb {db=blog}> DISCONNECT
Disconnecting from the database [blog]...OK

orientdb> CONNECT local:../databases/blog steve steve_passwd
Connecting to database [local:../databases/blog] with user 'steve'...OK

orientdb {db=blog}> SELECT FROM V

0 item(s) found. Query executed in 0.0 sec(s).

This confirms that the user Steve does not have access to vertices created by Luca. Now, create some as Steve:

orientdb {db=blog}> CREATE VERTEX SET label = 'car', 
                    name = 'Ferrari Modena'

Created vertex 'V#9:2{label:car,name:Ferrari Modena,_allow:[1]} v0' in 0,000000 sec(s).

orientdb {db=blog}> CREATE VERTEX SET label = 'driver', 
                    name = 'steve'

Created vertex 'V#9:3{label:driver,name:steve,_allow:[1]} v0' in 0,000000 sec(s).

orientdb {db=blog}> CREATE EDGE FROM #9:2 TO #9:3 
                    SET label = 'drive'
					
Created edge '[E#10:1{out:#9:2,in:#9:3,label:drive,_allow:[1]} v1]' in 0,002000 sec(s).

Run the SELECT query from earlier to see the vertices you've created:

orientdb {db=blog}> SELECT FROM V

---+------+--------+----------------+--------+------
 # | RID  | label  | name           | _allow | out
---+------+--------+----------------+--------+------
 0 | #9:2 | car    | Ferrari Modena | [1]    | [1]
 1 | #9:3 | driver | steve          | [1]    | null
---+------+--------+----------------+--------+------

2 item(s) found. Query executed in 0.034 sec(s).

As you can see, Steve's user still can't see vertices and edged that were created by other users. For the sake of example, see what happens when you try to create an edge that connects vertices from different users:

orientdb {db=blog}> CREATE EDGE FROM #9:2 TO #9:0 
                    SET label = 'security-test'

Error: com.orientechnologies.orient.core.exception.OCommandExecutionException: 
Error on execution of command: OCommandSQL [text=create edge from #9:2 to #
9:0 set label = 'security-test'] 
Error: java.lang.IllegalArgumentException: Source vertex '#9:0' does not exist

The partition used by Luca remains totally isolated from the one used by Steve. OrientDB operates on the assumption that the other partition doesn't exist, it remains invisible to the current user while still present in the database.

TinkerPop Stack

The Record-level Security feature is very powerful because it acts at a low-level within the OrientDB engine. This allows for better integration of security features with the Java API and the TinkerPop stack.

For instance, try to display all vertices and edges using Gremlin:

gremlin> g.V
==> [v[#9:2], v[#9:3]]
Script executed in 0,448000 sec(s).

gremlin> g.E
==> e[#10:1][#9:2-drive->#9:3]
Script executed in 0,123000 sec(s).

This feature works with other technologies that rely on TinkerPop Blueprints: