diff --git a/src/BusinessLogic/Authorization.cs b/src/BusinessLogic/Authorization.cs index 92c3821..ae973e1 100644 --- a/src/BusinessLogic/Authorization.cs +++ b/src/BusinessLogic/Authorization.cs @@ -1,6 +1,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Text; using Microsoft.IdentityModel.Tokens; + namespace Passenger { class Authorization(string secretKey) @@ -14,6 +15,10 @@ public string GenerateToken(string username, string passphrase) { IssuedAt = DateTime.UtcNow, Issuer = "passenger-cli", + Claims = new Dictionary + { + { "username", username } + }, TokenType = "Bearer", Expires = DateTime.UtcNow.AddMinutes(10), SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature) @@ -46,14 +51,16 @@ public bool ValidateToken(string token) public static bool ValidatePassphrase(string username, string passphrase) { + Database database = new(username); + // TODO: This is a placeholder for a more complex validation algorithm - if (!Database.IsRegistered()) + if (!database.IsRegistered()) { Console.WriteLine("passenger: not registered yet"); Environment.Exit(1); } - string passphraseOnDB = Database.GetCredentials().Passphrase; - string usernameOnDB = Database.GetCredentials().Username; + string passphraseOnDB = database.GetCredentials().Passphrase; + string usernameOnDB = database.GetCredentials().Username; if (passphraseOnDB != passphrase || usernameOnDB != username) { @@ -62,5 +69,11 @@ public static bool ValidatePassphrase(string username, string passphrase) } return true; } + + public static string GetUserName(string token) => + new JwtSecurityTokenHandler( + ).ReadJwtToken(token + ).Claims.First(claim => claim.Type == "username" + ).Value; } } \ No newline at end of file diff --git a/src/BusinessLogic/Validate.cs b/src/BusinessLogic/Validate.cs index 2412e08..a4eb8be 100644 --- a/src/BusinessLogic/Validate.cs +++ b/src/BusinessLogic/Validate.cs @@ -26,15 +26,6 @@ public static bool EntryFields(ReadWritableDatabaseEntry databaseEntry) => _ => true }; - public static void ConstantPair(ConstantPair entry, bool checkExistence = true) - { - // Check if required fields are provided - if (string.IsNullOrEmpty(entry.Key)) Error.MissingField("key"); - if (string.IsNullOrEmpty(entry.Value)) Error.MissingField("value"); - if (!checkExistence) return; - if (Database.FetchConstant(entry.Key) != null) Error.ConstantExists(entry); - } - public static ReadWritableDatabaseEntry JsonAsDatabaseEntry(string json) { try { return JsonSerializer.Deserialize(json); } diff --git a/src/BusinessLogic/Worker.cs b/src/BusinessLogic/Worker.cs index 9f55686..446090f 100644 --- a/src/BusinessLogic/Worker.cs +++ b/src/BusinessLogic/Worker.cs @@ -7,14 +7,19 @@ public class Worker(string[] args, string piped) private readonly Authorization authorization = new(EnDeCoder.JSWSecret); private readonly string[] arguments = args.Skip(1).ToArray(); private readonly string piped = piped; + private Database Database; private void RoutineAuthControl(string verbName, int minOrActual, int max = -1) { + string token = Environment.GetEnvironmentVariable("JWT"); + if (arguments.Length < minOrActual || (max != -1 && arguments.Length > max)) Error.ArgumentCount(verbName, minOrActual, max); - if (!authorization.ValidateToken( - Environment.GetEnvironmentVariable("JWT") - )) Error.InvalidToken(); + if (!authorization.ValidateToken(token)) + Error.InvalidToken(); + + // Initialize database + Database = new Database(Authorization.GetUserName(token)); } private void RequirePipedInput() @@ -36,6 +41,8 @@ public void Login() public void Register() { if (arguments.Length != 2) Error.ArgumentCount("register", 2); + // Routine control is not called here, so initialize database manually + Database = new Database(arguments[0]); if (Database.IsRegistered()) Console.WriteLine("passenger: already registered"); else @@ -179,55 +186,6 @@ public void Export() )); } - /* - * Constant pairs - */ - - public void Declare() - { - RoutineAuthControl("declare", 2); - ConstantPair constantPair = new() - { - Key = arguments[0], - Value = arguments[1] - }; - Validate.ConstantPair(constantPair); - Database.DeclareConstant(constantPair); - } - - public void Modify() - { - RoutineAuthControl("modify", 3); - ConstantPair newPair = new() - { - Key = arguments[1], - Value = arguments[2] - }; - Database.ModifyConstant(arguments[0], newPair); - } - - public void Remember() - { - RoutineAuthControl("remember", 1); - ConstantPair constantPair = Database.FetchConstant(arguments[0]); - if (constantPair == null) Error.EntryNotFound(); - Console.WriteLine(JsonSerializer.Serialize(constantPair)); - } - - public void Forget() - { - RoutineAuthControl("forget", 1); - Database.ForgetConstant(arguments[0]); - } - - public void Constants() - { - RoutineAuthControl("constants", 0); - Console.WriteLine( - JsonSerializer.Serialize(Database.AllConstants) - ); - } - /* * Generation */ @@ -345,28 +303,6 @@ Method can be bare or encrypted. Base64 encryption will be used. Writes to stdout, can be redirected to a file. JWT=[jwt] passenger export [method] - declare -D - Declare a new key-value pair, requires a JWT. - Theses pairs are constant values that can be replaced - on response. - JWT=[jwt] passenger declare [key] [value] - - modify -M - Modify a key-value pair, requires a JWT. - JWT=[jwt] passenger modify [key] [value] - - remember -R - Fetch a key-value pair, requires a JWT. - JWT=[jwt] passenger remember [key] - - forget -F - Forget a key-value pair, requires a JWT. - JWT=[jwt] passenger forget [key] - - constants -C - List all declared constants, requires a JWT. - JWT=[jwt] passenger constants - generate -g Generate a passphrase with the given length. Default length is 32. @@ -426,11 +362,6 @@ passenger [command] [*args] detect -d : detect issues about security of passphrases import -i [browser] : import `chromium`, `firefox` or `safari` csv export -e [method] : export to `bare` or `encrypted` csv - declare -D [key] [value] : declare a new key-value pair - modify -M [key] [value] : modify a key-value pair - remember -R [key] [value] : fetch a key-value pair - forget -F [key] : delete a key-value pair - constants -C : list all declared constants generate -g [length] : generate a passphrase with the given length manipulate -m [passphrase] : manipulate a passphrase version -v --version : show the version and exit diff --git a/src/DataAccess/DataBase.cs b/src/DataAccess/DataBase.cs index 5a0aa01..bf11f25 100644 --- a/src/DataAccess/DataBase.cs +++ b/src/DataAccess/DataBase.cs @@ -2,12 +2,19 @@ namespace Passenger { - public static class Database + public class Database { - static Database() + private readonly string databaseFile; + private readonly DatabaseModel database; + + public Database(string username) { try { + databaseFile = Path.Combine( + OperatingSystem.StoragePath, + $"{username}.bus" + ); string data = File.Exists(databaseFile) ? FileSystem.Read(databaseFile) : "{}"; @@ -21,10 +28,7 @@ static Database() * Database file */ - private static readonly string databaseFile = OperatingSystem.StoragePath; - private static readonly DatabaseModel database; - - public static void SaveToFile() + public void SaveToFile() { try { @@ -42,26 +46,25 @@ public static void SaveToFile() * Authorization methods */ - public static Credentials GetCredentials() => new() + public Credentials GetCredentials() => new() { Username = database.Username, Passphrase = database.Passphrase }; - public static bool IsRegistered() => + public bool IsRegistered() => !string.IsNullOrEmpty(database.Passphrase) && !string.IsNullOrEmpty(database.Username); - public static void Register(string username, string passphrase) + public void Register(string username, string passphrase) { database.Passphrase = passphrase; database.Username = username; database.Entries = []; - database.Constants = []; SaveToFile(); } - public static void ResetPassphrase(string oldPassphrase, string newPassphrase) + public void ResetPassphrase(string oldPassphrase, string newPassphrase) { if (database.Passphrase != oldPassphrase) Error.InvalidPassphrase(); database.Passphrase = newPassphrase; @@ -72,7 +75,7 @@ public static void ResetPassphrase(string oldPassphrase, string newPassphrase) * CRUD operations */ - public static ListableDatabaseEntry Create(ReadWritableDatabaseEntry entry) + public ListableDatabaseEntry Create(ReadWritableDatabaseEntry entry) { Validate.Entry(entry); DatabaseEntry savedEntry = Mapper.CreateDatabaseEntry( @@ -85,7 +88,7 @@ public static ListableDatabaseEntry Create(ReadWritableDatabaseEntry entry) } /// Prevents re-reading and re-crypting the database file - public static string Import(List entries) + public string Import(List entries) { try { @@ -99,15 +102,15 @@ public static string Import(List entries) catch { Error.ImportFailed(); throw; } } - public static List FetchAll() => + public List FetchAll() => database.Entries.Select( Mapper.ToListable ).ToList(); - public static DatabaseEntry Fetch(string id) => + public DatabaseEntry Fetch(string id) => database.Entries.Find(entry => entry.Id == id); - public static List Query(string keyword) => + public List Query(string keyword) => database.Entries.Where(entry => (entry.Platform != null && entry.Platform.Contains(keyword)) || (entry.Identity != null && entry.Identity.Contains(keyword)) || @@ -115,7 +118,7 @@ public static List Query(string keyword) => ).Select(Mapper.ToListable ).ToList(); - public static ListableDatabaseEntry Update(string id, ReadWritableDatabaseEntry updatedEntry, bool preserveUpdatedAt = true) + public ListableDatabaseEntry Update(string id, ReadWritableDatabaseEntry updatedEntry, bool preserveUpdatedAt = true) { int index = database.Entries.FindIndex(entry => entry.Id == id); if (index == -1) Error.EntryNotFound(); @@ -148,59 +151,19 @@ public static ListableDatabaseEntry Update(string id, ReadWritableDatabaseEntry return Mapper.ToListable(existingEntry); } - public static void Delete(string id) + public void Delete(string id) { database.Entries.RemoveAll(entry => entry.Id == id); SaveToFile(); } - /* - * Constants pair methods - */ - - public static ConstantPair DeclareConstant(ConstantPair entry) - { - Validate.ConstantPair(entry); - database.Constants ??= []; - database.Constants.Add(entry); - SaveToFile(); - return entry; - } - - public static ConstantPair FetchConstant(string constant) => - (database.Constants ?? []).Find(pair => - pair.Key == constant - ); - - public static ConstantPair ModifyConstant(string key, ConstantPair newPair) - { - if (FetchConstant(key) == null) Error.EntryNotFound(); - Validate.ConstantPair(newPair, false); - database.Constants[database.Constants.FindIndex(pair => - pair.Key == key - )] = newPair; - SaveToFile(); - return newPair; - } - - public static void ForgetConstant(string constant) - { - if (FetchConstant(constant) == null) Error.EntryNotFound(); - database.Constants.RemoveAll((pair) => - pair.Key == constant - ); - SaveToFile(); - } - /* * Getters */ - public static List AllConstants => database.Constants ?? []; - - public static List AllEntries => database.Entries; + public List AllEntries => database.Entries; - public static List AllReadWritableEntries => + public List AllReadWritableEntries => database.Entries.Select( Mapper.ToReadWritable ).ToList(); diff --git a/src/DataAccess/Mapper.cs b/src/DataAccess/Mapper.cs index d11adf2..14b249c 100644 --- a/src/DataAccess/Mapper.cs +++ b/src/DataAccess/Mapper.cs @@ -21,7 +21,7 @@ public static class Mapper { Id = entry.Id, Platform = entry.Platform, - Identity = ResolveConstant(entry.Identity), + Identity = entry.Identity, Url = entry.Url, Created = entry.Created, Updated = entry.Updated, @@ -43,11 +43,6 @@ public static class Mapper Url = entry.Url }; - public static string ResolveConstant(string key) => - Database.AllConstants.Find((pair) => - $"_${pair.Key}" == key - )?.Value ?? key; - public static string ToCSVLine(ReadWritableDatabaseEntry entry) => $"{entry.Platform},{entry.Url},{entry.Identity},{entry.Passphrase},{entry.Notes}"; diff --git a/src/DataAccess/Models.cs b/src/DataAccess/Models.cs index eaeddea..7e5e8aa 100644 --- a/src/DataAccess/Models.cs +++ b/src/DataAccess/Models.cs @@ -12,8 +12,6 @@ public class DatabaseModel public string Passphrase { get; set; } [JsonPropertyName("entries")] public List Entries { get; set; } - [JsonPropertyName("constants")] - public List Constants { get; set; } } /// Model to track passphrase statistics changes over time @@ -34,15 +32,6 @@ public class Credentials public string Passphrase { get; set; } } - /// Key-value pair model for constants - public class ConstantPair - { - [JsonPropertyName("key"), Required] - public string Key { get; set; } - [JsonPropertyName("value"), Required] - public string Value { get; set; } - } - /// Minimum details to use on statistics public class MicroDatabaseEntry { diff --git a/src/Presentation/CommandLine.cs b/src/Presentation/CommandLine.cs index 462e7e3..557a087 100644 --- a/src/Presentation/CommandLine.cs +++ b/src/Presentation/CommandLine.cs @@ -25,11 +25,6 @@ public void Parse() case "detect" or "-t": worker.Detect(); break; case "import" or "-i": worker.Import(); break; case "export" or "-e": worker.Export(); break; - case "declare" or "-D": worker.Declare(); break; - case "modify" or "-M": worker.Modify(); break; - case "remember" or "-R": worker.Remember(); break; - case "forget" or "-F": worker.Forget(); break; - case "constants" or "-C": worker.Constants(); break; case "generate" or "-g": worker.Generate(); break; case "manipulate" or "-m": worker.Manipulate(); break; case "version" or "-v" or "--version": Worker.Version(); break; diff --git a/src/Presentation/Error.cs b/src/Presentation/Error.cs index c69b3cf..8402f02 100644 --- a/src/Presentation/Error.cs +++ b/src/Presentation/Error.cs @@ -58,12 +58,6 @@ public static void MissingField(string field) Environment.Exit(40); } - public static void ConstantExists(ConstantPair constant) - { - Console.Error.WriteLine($"passenger: constant '{constant.Key}' already exists"); - Environment.Exit(49); - } - public static void EntryNotFound() { Console.Error.WriteLine("passenger: entry not found"); diff --git a/src/Utilities/OpreatingSystem.cs b/src/Utilities/OpreatingSystem.cs index 4ad7963..1eb7b75 100644 --- a/src/Utilities/OpreatingSystem.cs +++ b/src/Utilities/OpreatingSystem.cs @@ -32,8 +32,8 @@ public enum OSType public static string StoragePath => Path.Combine( HomeDirectory, CurrentOS == OSType.Windows - ? Path.Combine("AppData", "Roaming", ".passenger", "passenger.bus") - : Path.Combine(".passenger", "passenger.bus") + ? Path.Combine("AppData", "Roaming", ".passenger") + : Path.Combine(".passenger") ); } }