|
@@ -10,7 +10,6 @@ const db_url = "http://localhost:8000";
|
|
const db_name = { namespace: "ts_test", database: "access_test" };
|
|
const db_name = { namespace: "ts_test", database: "access_test" };
|
|
|
|
|
|
async function main() {
|
|
async function main() {
|
|
- // Extract username and password from command-line arguments
|
|
|
|
const db = new Surreal();
|
|
const db = new Surreal();
|
|
try {
|
|
try {
|
|
await db.connect(db_url, { auth });
|
|
await db.connect(db_url, { auth });
|
|
@@ -20,77 +19,164 @@ async function main() {
|
|
throw (err);
|
|
throw (err);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ //define user table
|
|
try {
|
|
try {
|
|
await db.query(`
|
|
await db.query(`
|
|
define table if not exists user schemafull;
|
|
define table if not exists user schemafull;
|
|
`);
|
|
`);
|
|
|
|
|
|
await db.query(`
|
|
await db.query(`
|
|
- define field if not exists username on user type string;
|
|
|
|
|
|
+ define field if not exists email on user type string;
|
|
define field if not exists password on user type string;
|
|
define field if not exists password on user type string;
|
|
- define field if not exists roles on user type set<string>;
|
|
|
|
`);
|
|
`);
|
|
|
|
|
|
await db.query(`
|
|
await db.query(`
|
|
- define index if not exists idx_username on user fields username unique;
|
|
|
|
|
|
+ define index if not exists idx_email on user fields email unique;
|
|
`);
|
|
`);
|
|
} catch (err) {
|
|
} catch (err) {
|
|
- console.error("Could not create table user or its fields: ", err instanceof Error ? err.message : String(err));
|
|
|
|
|
|
+ console.error("Could not create table user or its fields: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
throw (err);
|
|
throw (err);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ //insert test user entries
|
|
try {
|
|
try {
|
|
await db.query(`
|
|
await db.query(`
|
|
insert into user [
|
|
insert into user [
|
|
{
|
|
{
|
|
- username: "test1",
|
|
|
|
- password: crypto::argon2::generate("test"),
|
|
|
|
- roles: []
|
|
|
|
|
|
+ email: "appuser1@example.com",
|
|
|
|
+ password: crypto::argon2::generate("test")
|
|
},
|
|
},
|
|
- {
|
|
|
|
- username: "test2",
|
|
|
|
- password: crypto::argon2::generate("test"),
|
|
|
|
- roles: ['product_manager']
|
|
|
|
|
|
+ {
|
|
|
|
+ email: "appuser2@example.com",
|
|
|
|
+ password: crypto::argon2::generate("test")
|
|
}
|
|
}
|
|
];
|
|
];
|
|
`)
|
|
`)
|
|
} catch (err) {
|
|
} catch (err) {
|
|
- console.error("Could not create user entries: ", err instanceof Error ? err.message : String(err));
|
|
|
|
|
|
+ console.error("Could not create user entries: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // define role table
|
|
try {
|
|
try {
|
|
await db.query(`
|
|
await db.query(`
|
|
- define table if not exists product schemafull
|
|
|
|
- permissions for select where $auth.roles contains 'product_manager';
|
|
|
|
- define field if not exists code on product type string;
|
|
|
|
- define field if not exists available on product type bool;
|
|
|
|
- define index if not exists idx_code on product fields code;
|
|
|
|
|
|
+ define table role schemafull;
|
|
|
|
+ `);
|
|
|
|
+
|
|
|
|
+ await db.query(`
|
|
|
|
+ define field name on role type string;
|
|
`);
|
|
`);
|
|
} catch (err) {
|
|
} catch (err) {
|
|
- console.error("Could not create table product: ", err instanceof Error ? err.message : String(err));
|
|
|
|
|
|
+ console.error("Could not create table role or its field: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
throw (err);
|
|
throw (err);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // define product_manager role
|
|
try {
|
|
try {
|
|
await db.query(`
|
|
await db.query(`
|
|
- insert into product {
|
|
|
|
- code: "test_product1",
|
|
|
|
- available: true
|
|
|
|
- };
|
|
|
|
- `)
|
|
|
|
|
|
+ create role:product_manager content {
|
|
|
|
+ name: "product_manager"
|
|
|
|
+ }
|
|
|
|
+ `);
|
|
|
|
+ } catch (err) {
|
|
|
|
+ console.error("Could not create role: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
|
|
+ throw (err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // define has_role table
|
|
|
|
+ try {
|
|
|
|
+ await db.query(`
|
|
|
|
+ define table has_role schemafull type relation from user to role enforced;
|
|
|
|
+ `);
|
|
|
|
+ } catch (err) {
|
|
|
|
+ console.error("Could not create has_role: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
|
|
+ throw (err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // define *can_do* tables
|
|
|
|
+ try {
|
|
|
|
+ // additional controlled tables could be added here like:
|
|
|
|
+ // define table can_select schemafull type relation from role to product|person|other_table enforced;
|
|
|
|
+ // or simpler:
|
|
|
|
+ // define table can_select type relation;
|
|
|
|
+ // with no restriction on the types of *in* and *out* tables.
|
|
|
|
+ await db.query(`
|
|
|
|
+ define table can_select schemafull type relation from role to product enforced;
|
|
|
|
+ define table can_create schemafull type relation from role to product enforced;
|
|
|
|
+ define table can_update schemafull type relation from role to product enforced;
|
|
|
|
+ define table can_delete schemafull type relation from role to product enforced;
|
|
|
|
+ `);
|
|
|
|
+ } catch (err) {
|
|
|
|
+ console.error("Could not create relation table: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
|
|
+ throw (err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // create relation entries
|
|
|
|
+ try {
|
|
|
|
+ await db.query(`
|
|
|
|
+ relate user:appuser1->has_role->role:product_manager;
|
|
|
|
+ relate role:product_manager->can_select->(select * from product);
|
|
|
|
+ `);
|
|
|
|
+ } catch (err) {
|
|
|
|
+ console.error("Could not create relation entry: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
|
|
+ throw (err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // define access *account* of type record
|
|
|
|
+ try {
|
|
|
|
+ await db.query(`
|
|
|
|
+ DEFINE ACCESS overwrite account ON DATABASE TYPE RECORD
|
|
|
|
+ SIGNUP ( CREATE user SET email = $email, password = crypto::argon2::generate($password) )
|
|
|
|
+ SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password) )
|
|
|
|
+ DURATION FOR TOKEN 15m, FOR SESSION 12h
|
|
|
|
+ `);
|
|
|
|
+ } catch (err) {
|
|
|
|
+ console.error("Could not define access method: ", err instanceof Error ? err.message : String(err));
|
|
|
|
+ throw (err);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // define product table
|
|
|
|
+ // This is where the permissions are ultimately defined.
|
|
|
|
+ // We only want users to be able to select from the product table that are assigned a role that itself
|
|
|
|
+ // is connected to the product table with the right relationship that is an entry in the *can_select* relation table.
|
|
|
|
+ // Additional permissions for create, update, delete are left out here for the sake of brevity.
|
|
|
|
+ // Additional permissions would make use of the same subquery though with *can_select* replaced by the respective
|
|
|
|
+ // relation table name (*can_create* etc.).
|
|
|
|
+ try {
|
|
|
|
+ await db.query(`
|
|
|
|
+ define table overwrite product schemafull
|
|
|
|
+ permissions for select
|
|
|
|
+ where $access = "account"
|
|
|
|
+ and (select <-can_select.in<-has_role.in[0]
|
|
|
|
+ from product)[0]["<-can_select"]["in"]["<-has_role"]["in"] contains $auth.id;
|
|
|
|
+ `);
|
|
|
|
+
|
|
|
|
+ await db.query(`
|
|
|
|
+ define field code on product type string;
|
|
|
|
+ define field available on product type bool;
|
|
|
|
+ `);
|
|
} catch (err) {
|
|
} catch (err) {
|
|
- console.error("Could not create user entries: ", err instanceof Error ? err.message : String(err));
|
|
|
|
|
|
+ console.error("Could not create table product or one of its fields: ",
|
|
|
|
+ err instanceof Error ? err.message : String(err));
|
|
|
|
+ throw (err);
|
|
}
|
|
}
|
|
|
|
|
|
try {
|
|
try {
|
|
await db.query(`
|
|
await db.query(`
|
|
- define access overwrite user on database type record
|
|
|
|
- signup (create user set username = $username, password = crypto::argon2::generate($password), roles=[])
|
|
|
|
- signin (select * from user where username = $username and crypto::argon2::compare(password, $password));
|
|
|
|
- `)
|
|
|
|
|
|
+ create product:testproduct content {
|
|
|
|
+ code: "testproduct",
|
|
|
|
+ available: true
|
|
|
|
+ };
|
|
|
|
+ `)
|
|
} catch (err) {
|
|
} catch (err) {
|
|
- console.error("Could not define access: ", err instanceof Error ? err.message : String(err));
|
|
|
|
- }
|
|
|
|
|
|
+ console.error("Could not product entry: ", err instanceof Error ? err.message : String(err));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
main();
|
|
main();
|