import Surreal from 'surrealdb'; import { argv } from 'process'; const auth = { username: 'root', password: 'root', } const db_url = "http://localhost:8000"; const db_name = { namespace: "ts_test", database: "access_test" }; async function main() { const db = new Surreal(); try { await db.connect(db_url, { auth }); await db.use(db_name); } catch (err) { console.error("Could not connect to SurrealDB server: ", err instanceof Error ? err.message : String(err)); throw (err); } //define user table try { await db.query(` define table if not exists user schemafull; `); await db.query(` define field if not exists email on user type string; define field if not exists password on user type string; `); await db.query(` define index if not exists idx_email on user fields email unique; `); } catch (err) { console.error("Could not create table user or its fields: ", err instanceof Error ? err.message : String(err)); throw (err); } //insert test user entries try { await db.query(` insert into user [ { email: "appuser1@example.com", password: crypto::argon2::generate("test") }, { email: "appuser2@example.com", password: crypto::argon2::generate("test") } ]; `) } catch (err) { console.error("Could not create user entries: ", err instanceof Error ? err.message : String(err)); } // define role table try { await db.query(` define table role schemafull; `); await db.query(` define field name on role type string; `); } catch (err) { console.error("Could not create table role or its field: ", err instanceof Error ? err.message : String(err)); throw (err); } // define product_manager role try { await db.query(` 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) { console.error("Could not create table product or one of its fields: ", err instanceof Error ? err.message : String(err)); throw (err); } try { await db.query(` create product:testproduct content { code: "testproduct", available: true }; `) } catch (err) { console.error("Could not product entry: ", err instanceof Error ? err.message : String(err)); } } main();