module auth.dao; import handy_httpd.components.optional; import auth.model; interface UserRepository { Optional!User findByUsername(string username); User createUser(string username, string passwordHash); void deleteByUsername(string username); } /** * User implementation that stores each user's data in a separate directory. */ class FileSystemUserRepository : UserRepository { import std.path; import std.file; import std.json; private string usersDir; this(string usersDir = "users") { this.usersDir = usersDir; } Optional!User findByUsername(string username) { if ( !validateUsername(username) || !exists(getUserDir(username)) || !exists(getUserDataFile(username)) ) { return Optional!User.empty; } JSONValue userObj = parseJSON(readText(getUserDataFile(username))); return Optional!User.of(User( username, userObj.object["passwordHash"].str )); } User createUser(string username, string passwordHash) { if (!validateUsername(username)) throw new Exception("Invalid username"); if (exists(getUserDir(username))) throw new Exception("User already exists."); JSONValue userObj = JSONValue.emptyObject; userObj.object["passwordHash"] = JSONValue(passwordHash); string jsonStr = userObj.toPrettyString(); if (!exists(this.usersDir)) mkdir(this.usersDir); mkdir(getUserDir(username)); std.file.write(getUserDataFile(username), cast(ubyte[]) jsonStr); return User(username, passwordHash); } void deleteByUsername(string username) { if (validateUsername(username) && exists(getUserDir(username))) { rmdirRecurse(getUserDir(username)); } } private string getUserDir(string username) { return buildPath(this.usersDir, username); } private string getUserDataFile(string username) { return buildPath(this.usersDir, username, "user-data.json"); } }