From fcf5dd4889206d7072c563d59940fa21f464cc58 Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 30 Oct 2025 13:39:03 -0400 Subject: [PATCH] Initial commit --- README.d | 4 + accounts.d | 303 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 README.d create mode 100644 accounts.d diff --git a/README.d b/README.d new file mode 100644 index 0000000..ee157dc --- /dev/null +++ b/README.d @@ -0,0 +1,4 @@ +This is an accounts/user library. +Backed in sqlite via arsd.sqlite + + diff --git a/accounts.d b/accounts.d new file mode 100644 index 0000000..f27e4e1 --- /dev/null +++ b/accounts.d @@ -0,0 +1,303 @@ +import std.stdio; + +//import passwd; +//import passwd.bcrypt; +import std.file; +import std.conv; +import std.process; +import arsd.sqlite; +import std.uuid; + +import std.file : isFile, isDir; + +class Accounts{ + private string salt; + private Database db; + + this(string dbName, string salt) { + this.salt = salt; + this.db = new Sqlite(dbName); + } + string get_salt(){ + return this.salt; + } + void provisiondb(){ + // The status should determine if a user is suspended, or banned, or in good standing. + this.db.query("CREATE TABLE IF NOT EXISTS users (username text, password text, alias text, email text, status integer)"); + this.db.query("CREATE TABLE IF NOT EXISTS sessions (username text, session text)"); + this.db.query("CREATE TABLE IF NOT EXISTS roles (role text)"); + this.db.query("CREATE TABLE IF NOT EXISTS userstoroles (userid integer, roleid integer)"); + // The folowing is expected to hold both directions of friendships. + // One way relation is contact request, two way is friendship. + // status determines blocked or not. + this.db.query("CREATE TABLE IF NOT EXISTS contacts (user integer, contact integer, status integer)"); + + //this.db.query("INSERT INTO users values (?, ?, ?)", "user1", "hello", "user1"); + + //foreach(line; db.query("SELECT * FROM tablename;")) { + //writefln("%s %s", line[0], line[1]); + //} + } + bool create_user(string username,string password,string email, string useralias = ""){ + try{ + string openssl_command = " openssl passwd -6 --salt \"" ~ this.salt ~ "\" \"" ~ password ~ "\""; + //writeln(openssl_command); + auto cont = executeShell(openssl_command); + if(cont.status != 0) { + writeln("failed to run openssl command"); + //return "Failed"; + return false; + } else { + writeln("Salted password: " ~ cont.output); + //auto file = File("users/"~ username, "w"); + //file.rawWrite(cont.output); + //file.close(); + // We don't need an ID column, we can use rowid, built in. + this.db.query("INSERT INTO users values (?, ?, ?, ?)", username, cont.output, email, useralias); + return true; + } + } catch (Exception e) { + return false; + } + } + bool delete_user(string username){ + try{ + // Delete users to role relation. + this.db.query("DELETE from userstoroles where userid = (select rowid from users where username = ?)", username); + this.db.query("DELETE from users where username = ?", username); + + return true; + } catch (Exception e) { + return false; + } + } + string get_user(string session, string role = ""){ + string username; + foreach(line; this.db.query("SELECT username FROM sessions where session = ?", session)) { + //writefln("get_user:: %s", line["username"]); + username = line["username"]; + } + return username; + } + bool assume_user(string username){ + //TODO: This should allow an admin to assume the account of another user. + try { + return true; + } catch (Exception e) { + return false; + } + } + string create_session(string username){ + try { + delete_session(username); + auto namespace = sha1UUID("Accounts"); + auto sessionid = sha1UUID(username, namespace); + this.db.query("INSERT INTO sessions values (?, ?)", username, sessionid); + return sessionid.toString(); + } catch (Exception e) { + writeln("failed to create session: " ~e.toString()); + return ""; + } + } + bool delete_session(string username){ + try{ + this.db.query("DELETE from sessions where username = ?", username); + return true; + } catch (Exception e) { + return false; + } + } + string login(string username,string password){ + string session = ""; + if(check_password(username,password)){ + session = this.create_session(username); + return session; + } + return session; + } + bool logout(string sessionid){ + try{ + this.db.query("DELETE FROM sessions where session = ?", sessionid); + return true; + } catch (Exception e) { + return false; + } + } + bool authorized_session(string session, string role = ""){ + //TODO: Rewrite this to be a single query, instead of calling a function?? + try{ + string username = get_user(session,role); + return authorized(username,role); + } catch (Exception e) { + return false; + } + } + bool authorized(string username, string role = ""){ + auto rows = db.query("SELECT count(*) FROM userstoroles LEFT JOIN users ON users.rowid = userstoroles.userid LEFT JOIN roles ON roles.rowid = userstoroles.roleid where users.username = ? and roles.role = ?", username, role); + if(rows.length ==1){ + return true; + } else{ + return false; + } + /*foreach(line;rows ) { + writefln("%s %s", line["password"]); + stored = line["password"]; + }*/ + } + + + bool check_password(string username,string password){ + try{ + string stored; + foreach(line; db.query("SELECT password FROM users where username = ?", username)) { + writefln("Password from DB: %s", line["password"]); + stored = line["password"]; + } + string openssl_command = " openssl passwd -6 --salt \"" ~ this.salt ~ "\" \"" ~ password ~ "\""; + //writeln(openssl_command); + auto cont = executeShell(openssl_command); + if(cont.status != 0) { + writeln("failed to run openssl command"); + //return "Failed"; + return false; + } else { + writeln("entered password: " ~cont.output); + if(cont.output == stored){ + return true; + } + return false; + } + } catch (Exception e) { + return false; + } + } + bool create_role(string role){ + try{ + this.db.query("INSERT INTO roles values (?)", role); + return true; + } catch (Exception e) { + return false; + } + } + bool remove_role(string role){ + try{ + this.db.query("DELETE FROM roles where role = ?", role); + return true; + } catch (Exception e) { + return false; + } + } + bool assign_role(string username, string role){ + try{ + this.db.query("insert into userstoroles values ((select rowid from users where username = ?), (select rowid from roles where role = ?) )", username, role); + return true; + } catch (Exception e) { + return false; + } + } + string[] get_roles(string username){ + string[] roles; + foreach(line; db.query("SELECT rowid,role FROM roles")) { + roles ~= line["role"]; + writefln("%s %s", line["rowid"], line["role"]); + } + return roles; + } + + /*bool request_contact(string username, string contact){ + //TODO: + try{ + this.db.query("INSERT into contacts ((select rowid from users where username = ?), (select rowid from users where username = ?), 1)", username, contact); + return true; + } catch (Exception e) { + return false; + } + } + bool remove_contact(string username, string contact){ + //TODO: + try{ + this.db.query(" role = ?", role); + return true; + } catch (Exception e) { + return false; + } + } + + bool block_contact(string username, string contact){ + //TODO: + try{ + this.db.query(" role = ?", role); + return true; + } catch (Exception e) { + return false; + } + } + bool unblock_contact(string username, string contact){ + //TODO: + try{ + this.db.query(" role = ?", role); + return true; + } catch (Exception e) { + return false; + } + } + string[] get_pending_contacts(string username, string contact){ + //TODO: + string[] roles; + foreach(line; db.query("SELECT rowid,role FROM roles")) { + roles ~= line["role"]; + writefln("%s %s", line["rowid"], line["role"]); + } + return roles; + } + // This one shouldn't be needed. + bool assign_contact(string username, string contact){ + //TODO: + try{ + this.db.query(" role = ?", role); + return true; + } catch (Exception e) { + return false; + } + } + string[] get_contacts(){ + //TODO: Fill this out, so we can get our contacts list.. + string[] roles; + foreach(line; db.query("SELECT rowid,role FROM roles")) { + roles ~= line["role"]; + writefln("%s %s", line["rowid"], line["role"]); + } + return roles; + }*/ +} + +/* +* NOTE: to use this, just change the main() to tests() so it doesn't run automatically. +*/ + +void main() { + writeln("Starting account tests."); + auto accounts = new Accounts("accounts.db", "SALTaosidhfaosihdfSALT"); + string testuser = "testuser"; + string testpass = "testpass"; + string testemail = "testemail"; + string testalias = "testalias"; + string testrole = "testrole"; + accounts.provisiondb(); + accounts.delete_user(testuser); + accounts.create_user(testuser,testpass,testemail,testalias); + //string sessionid = accounts.create_session(testuser); + string sessionid = accounts.login(testuser,testpass); + string username = accounts.get_user(sessionid); + writeln(username); + assert(username == testuser); + assert(accounts.remove_role(testrole) == true); + assert(accounts.create_role(testrole) == true); + writeln(accounts.get_roles(testuser)); + assert(accounts.authorized(testuser,testrole) == true); + assert(accounts.authorized_session(sessionid,testrole) == true); + assert(accounts.authorized_session(sessionid,"madeuprole") == true); + //writeln(sessionid); + assert(accounts.logout(sessionid) == true); +} +