Reputation: 1859
I'm trying to get Record Level Security working using the Document database (Java) API for OrientDB. I like the idea of restricting access for certain records in the database to particular roles; however, I could not find how to do this in the documentation. I'm new to OrientDB so I'm sure I'm just missing something.
Here is what I have so far.
// Create database.
ODatabaseDocumentTx db = new ODatabaseDocumentTx(path);
db.create();
// Create role with no permissions.
db.command(new OCommandSQL("INSERT INTO orole SET name = 'foobar', mode = 0;")).execute();
// Create a user with the new role.
OSecurity sm = db.getMetadata().getSecurity();
OUser user = sm.createUser("user", "user", "foobar");
ORole foobarRole = sm.getRole("foobar");
// Insert 2 records, one restricted, one is not.
OClass restricted = db.getMetadata().getSchema().getClass("ORestricted");
OClass docClass = db.getMetadata().getSchema().getOrCreateClass(TABLE_NAME, restricted);
ODocument doc1 = new ODocument(docClass);
ODocument doc2 = new ODocument(docClass);
// The restricted record...
doc1.field("name", TABLE_NAME);
doc1.field("Id", 1, OType.INTEGER);
doc1.field("Message", "Hello 1", OType.STRING);
doc1.save();
// The unrestricted record...
doc2.field("name", TABLE_NAME);
doc2.field("Id", 2, OType.INTEGER);
doc2.field("Message", "Hello 2", OType.STRING);
doc2.save();
// Allow "user" with "foobar" role to read record doc2.
String sql = String.format(
"UPDATE %s ADD _allowRead = %s",
doc2.getIdentity().toString(),
foobarRole.getDocument().getIdentity().toString());
db.command(new OCommandSQL(sql)).execute();
// Give foobar role permission to read from table.
db.command(new OCommandSQL(String.format("GRANT READ ON database.class.%s TO foobar", TABLE_NAME))).execute();
db.close();
// Open connection for "user".
ODatabaseDocumentTx userDb = new ODatabaseDocumentTx(path);
userDb.open("user", "user");
// Here I would expect to see the message from doc2 but not doc1.
// Nothing gets printed...
for (ODocument odoc : userDb.browseClass(TABLE_NAME))
{
System.out.println(odoc.field("Message"));
}
The documentation says that records that are not accessible to a role are skipped during a READ. In my case, the user cannot read anything.
Any ideas of how can I get this behavior working?
Upvotes: 2
Views: 367
Reputation: 1859
After a couple days of fiddling with it, this is how I got it to work.
// Create database.
ODatabaseDocumentTx db = new ODatabaseDocumentTx(path);
db.create();
// Create users/roles.
OSecurity sm = db.getMetadata().getSecurity();
restrictedRole = sm.createRole("foobar", OSecurityRole.ALLOW_MODES.DENY_ALL_BUT);
restrictedRole.addRule(ORule.ResourceGeneric.CLASS, TABLE_NAME, ORole.PERMISSION_READ);
restrictedRole.addRule(ORule.ResourceGeneric.DATABASE, TABLE_NAME, ORole.PERMISSION_READ);
restrictedRole.addRule(ORule.ResourceGeneric.CLUSTER, TABLE_NAME, ORole.PERMISSION_READ);
restrictedRole.save();
restrictedRole.reload();
admin = sm.getUser("admin");
user2 = sm.createUser("user2", "user2", "foobar");
// Insert 2 records, one restricted, one is not.
OClass restricted = db.getMetadata().getSchema().getClass("ORestricted");
OClass docClass = db.getMetadata().getSchema().getOrCreateClass(TABLE_NAME, restricted);
ODocument doc1 = new ODocument(docClass);
ODocument doc2 = new ODocument(docClass);
// The restricted record...
doc1.field("name", TABLE_NAME);
doc1.field("Id", 1, OType.INTEGER);
doc1.field("Message", "Hello 1", OType.STRING);
doc1.save();
// The unrestricted record...
doc2.field("name", TABLE_NAME);
doc2.field("Id", 2, OType.INTEGER);
doc2.field("Message", "Hello 2", OType.STRING);
db.getMetadata().getSecurity().allowRole(doc2, OSecurityShared.ALLOW_READ_FIELD, "foobar");
doc2.save();
//
// PRINT RESTRICTED
//
db.close();
db = new ODatabaseDocumentTx(path);
db.open("user2", "user2");
// Prints:
// Hello 2
for (ODocument doc : db.browseClass(TABLE_NAME))
{
System.out.println(doc.field("Message"));
}
//
// PRINT ADMIN
//
db.close();
db = new ODatabaseDocumentTx(path);
db.open("admin", "admin");
// Prints:
// Hello 1
// Hello 2
for (ODocument doc : db.browseClass(TABLE_NAME))
{
System.out.println(doc.field("Message"));
}
The addRule
method on the role allows users with that role to read from the class. The second document has an extra line with allowRole
which allows users with that role to read that particular document, this was left out of the first document on purpose. The result is that users with the "foobar" role only get the second document while reading. The admin role can read both documents.
Also, note the document class inheritance from "ORestricted".
OClass restricted = db.getMetadata().getSchema().getClass("ORestricted");
OClass docClass = db.getMetadata().getSchema().getOrCreateClass(TABLE_NAME, restricted);
The document needs to inherit from ORestricted
in order for the record level security to work. Personally I found no explanation of how to do this in code anywhere (maybe I did not look in the right places) and there is no document class in Java that directly inherits from an ORestricted
class. So we have to use the schema class metadata to tell the driver that the documents we are creating need to inherit from ORestricted
.
Upvotes: 2