|
@@ -4,288 +4,17 @@ using Json;
|
|
|
|
|
|
|
|
namespace Spry.Authentication {
|
|
namespace Spry.Authentication {
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * User represents an authenticated user and implements the Identity interface
|
|
|
|
|
- * for integration with the Authorisation system.
|
|
|
|
|
- */
|
|
|
|
|
public class User : GLib.Object, Authorisation.Identity {
|
|
public class User : GLib.Object, Authorisation.Identity {
|
|
|
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Identity Properties (required by Authorisation.Identity interface)
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
|
|
+ public string id { get; }
|
|
|
|
|
+ public string username { get; }
|
|
|
|
|
+ public Invercargill.ImmutableLot<string> permissions { owned get; }
|
|
|
|
|
+ public Invercargill.Properties data { owned get; }
|
|
|
|
|
|
|
|
- // Backing fields for identity properties
|
|
|
|
|
- private string _id = "";
|
|
|
|
|
- private string _username = "";
|
|
|
|
|
|
|
+ public DateTime created_at { get; }
|
|
|
|
|
+ public DateTime updated_at { get; }
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Unique identifier for this user.
|
|
|
|
|
- * Read-only for Identity interface compliance.
|
|
|
|
|
- * Use set_id() to modify.
|
|
|
|
|
- */
|
|
|
|
|
- public string id {
|
|
|
|
|
- get { return _id; }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Human-readable name for this user.
|
|
|
|
|
- * Read-only for Identity interface compliance.
|
|
|
|
|
- * Use set_username() to modify.
|
|
|
|
|
- */
|
|
|
|
|
- public string username {
|
|
|
|
|
- get { return _username; }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Returns permissions as an array for the Identity interface.
|
|
|
|
|
- * Converts from internal Vector<string> to string[] for serialization.
|
|
|
|
|
- */
|
|
|
|
|
- public string[] permissions {
|
|
|
|
|
- owned get {
|
|
|
|
|
- var result = new string[0];
|
|
|
|
|
- foreach (var perm in _permissions) {
|
|
|
|
|
- result += perm;
|
|
|
|
|
- }
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Returns user data as a Variant for embedding in authorisation tokens.
|
|
|
|
|
- * Includes email, app_data, and timestamps.
|
|
|
|
|
- */
|
|
|
|
|
- public Variant data {
|
|
|
|
|
- owned get {
|
|
|
|
|
- return get_data_variant();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Additional Authentication Data
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
-
|
|
|
|
|
- public string email { get; set; }
|
|
|
|
|
- public string password_hash { get; set; }
|
|
|
|
|
-
|
|
|
|
|
- // Metadata
|
|
|
|
|
- public DateTime created_at { get; set; }
|
|
|
|
|
- public DateTime? updated_at { get; set; }
|
|
|
|
|
-
|
|
|
|
|
- // Application-specific data - stored as JSON object
|
|
|
|
|
- public Dictionary<string, string> app_data { get; set; default = new Dictionary<string, string>(); }
|
|
|
|
|
-
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Internal Storage
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
-
|
|
|
|
|
- // Internal vector storage for permissions (used for mutability)
|
|
|
|
|
- private Vector<string> _permissions = new Vector<string>();
|
|
|
|
|
-
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Setters for Identity Properties
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Sets the user ID.
|
|
|
|
|
- */
|
|
|
|
|
- public void set_id(string value) {
|
|
|
|
|
- _id = value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Sets the username.
|
|
|
|
|
- */
|
|
|
|
|
- public void set_username(string value) {
|
|
|
|
|
- _username = value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Permission Management (for internal use)
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Gets the internal permissions vector for mutation.
|
|
|
|
|
- * Used by PermissionService to add/remove permissions.
|
|
|
|
|
- */
|
|
|
|
|
- public Vector<string> get_permissions_vector() {
|
|
|
|
|
- return _permissions;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Adds a permission to the user.
|
|
|
|
|
- */
|
|
|
|
|
- public void add_permission(string permission) {
|
|
|
|
|
- _permissions.add(permission);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Removes a permission from the user.
|
|
|
|
|
- */
|
|
|
|
|
- public void remove_permission(string permission) {
|
|
|
|
|
- _permissions.remove(permission);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Clears all permissions from the user.
|
|
|
|
|
- */
|
|
|
|
|
- public void clear_permissions() {
|
|
|
|
|
- _permissions.clear();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Checks if the user has a specific permission.
|
|
|
|
|
- */
|
|
|
|
|
- public bool has_permission(string permission) {
|
|
|
|
|
- foreach (var perm in _permissions) {
|
|
|
|
|
- if (perm == permission) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Helper Methods
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * Helper to convert user data to Variant for token embedding.
|
|
|
|
|
- */
|
|
|
|
|
- private Variant get_data_variant() {
|
|
|
|
|
- var builder = new VariantBuilder(VariantType.VARDICT);
|
|
|
|
|
-
|
|
|
|
|
- builder.add("{sv}", "email", new Variant.string(email ?? ""));
|
|
|
|
|
- builder.add("{sv}", "created_at", new Variant.string(created_at != null ? created_at.format_iso8601() : ""));
|
|
|
|
|
-
|
|
|
|
|
- if (updated_at != null) {
|
|
|
|
|
- builder.add("{sv}", "updated_at", new Variant.string(((!)updated_at).format_iso8601()));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Add app_data as a dictionary
|
|
|
|
|
- var app_data_builder = new VariantBuilder(VariantType.VARDICT);
|
|
|
|
|
- var iter = app_data.iterator();
|
|
|
|
|
- while (iter.next()) {
|
|
|
|
|
- var pair = iter.get();
|
|
|
|
|
- app_data_builder.add("{sv}", pair.key, new Variant.string(pair.value ?? ""));
|
|
|
|
|
- }
|
|
|
|
|
- builder.add("{sv}", "app_data", app_data_builder.end());
|
|
|
|
|
-
|
|
|
|
|
- return builder.end();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
- // Constructors and JSON Serialization
|
|
|
|
|
- // =========================================================================
|
|
|
|
|
-
|
|
|
|
|
- public User() {
|
|
|
|
|
- _id = "";
|
|
|
|
|
- _username = "";
|
|
|
|
|
- email = "";
|
|
|
|
|
- password_hash = "";
|
|
|
|
|
- created_at = new DateTime.now_utc();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public static User from_json(Json.Object obj) {
|
|
|
|
|
- var user = new User();
|
|
|
|
|
-
|
|
|
|
|
- // Required string fields - use has_member and null coalescing for safety
|
|
|
|
|
- user.set_id(obj.has_member("id") ? (obj.get_string_member("id") ?? "") : "");
|
|
|
|
|
- user.set_username(obj.has_member("username") ? (obj.get_string_member("username") ?? "") : "");
|
|
|
|
|
- user.email = obj.has_member("email") ? (obj.get_string_member("email") ?? "") : "";
|
|
|
|
|
- user.password_hash = obj.has_member("password_hash") ? (obj.get_string_member("password_hash") ?? "") : "";
|
|
|
|
|
-
|
|
|
|
|
- // created_at - check member exists and value is not null/empty
|
|
|
|
|
- if (obj.has_member("created_at")) {
|
|
|
|
|
- var created_str = obj.get_string_member("created_at");
|
|
|
|
|
- if (created_str != null && created_str.length > 0) {
|
|
|
|
|
- user.created_at = new DateTime.from_iso8601(created_str, new TimeZone.utc());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // updated_at (optional) - check member exists, is not null, and has value
|
|
|
|
|
- if (obj.has_member("updated_at")) {
|
|
|
|
|
- var member = obj.get_member("updated_at");
|
|
|
|
|
- if (member != null && member.get_node_type() == Json.NodeType.VALUE) {
|
|
|
|
|
- var updated_str = obj.get_string_member("updated_at");
|
|
|
|
|
- if (updated_str != null && updated_str.length > 0) {
|
|
|
|
|
- user.updated_at = new DateTime.from_iso8601(updated_str, new TimeZone.utc());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // permissions (array of strings) - check member exists and is array
|
|
|
|
|
- if (obj.has_member("permissions")) {
|
|
|
|
|
- var member = obj.get_member("permissions");
|
|
|
|
|
- if (member != null && member.get_node_type() == Json.NodeType.ARRAY) {
|
|
|
|
|
- var perms_array = obj.get_array_member("permissions");
|
|
|
|
|
- if (perms_array != null) {
|
|
|
|
|
- foreach (var perm in perms_array.get_elements()) {
|
|
|
|
|
- var perm_str = perm.get_string();
|
|
|
|
|
- if (perm_str != null) {
|
|
|
|
|
- user._permissions.add(perm_str);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // app_data (object with string values) - check member exists and is object
|
|
|
|
|
- if (obj.has_member("app_data")) {
|
|
|
|
|
- var member = obj.get_member("app_data");
|
|
|
|
|
- if (member != null && member.get_node_type() == Json.NodeType.OBJECT) {
|
|
|
|
|
- var app_data_obj = obj.get_object_member("app_data");
|
|
|
|
|
- if (app_data_obj != null) {
|
|
|
|
|
- foreach (var key in app_data_obj.get_members()) {
|
|
|
|
|
- var value = app_data_obj.get_string_member(key);
|
|
|
|
|
- user.app_data.set(key, value ?? "");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return user;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- public Json.Object to_json() {
|
|
|
|
|
- var obj = new Json.Object();
|
|
|
|
|
-
|
|
|
|
|
- // Use null coalescing to ensure we never pass null to set_string_member
|
|
|
|
|
- obj.set_string_member("id", id ?? "");
|
|
|
|
|
- obj.set_string_member("username", username ?? "");
|
|
|
|
|
- obj.set_string_member("email", email ?? "");
|
|
|
|
|
- obj.set_string_member("password_hash", password_hash ?? "");
|
|
|
|
|
- obj.set_string_member("created_at", created_at != null ? created_at.format_iso8601() : new DateTime.now_utc().format_iso8601());
|
|
|
|
|
-
|
|
|
|
|
- // updated_at (optional)
|
|
|
|
|
- if (updated_at != null) {
|
|
|
|
|
- obj.set_string_member("updated_at", ((!)updated_at).format_iso8601());
|
|
|
|
|
- } else {
|
|
|
|
|
- obj.set_null_member("updated_at");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // permissions array - always create array even if empty
|
|
|
|
|
- var perms_array = new Json.Array();
|
|
|
|
|
- if (_permissions != null) {
|
|
|
|
|
- foreach (var perm in _permissions) {
|
|
|
|
|
- if (perm != null) {
|
|
|
|
|
- perms_array.add_string_element(perm);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- obj.set_array_member("permissions", perms_array);
|
|
|
|
|
-
|
|
|
|
|
- // app_data object - always create object even if empty
|
|
|
|
|
- var app_data_obj = new Json.Object();
|
|
|
|
|
- if (app_data != null) {
|
|
|
|
|
- var iter = app_data.iterator();
|
|
|
|
|
- while (iter.next()) {
|
|
|
|
|
- var pair = iter.get();
|
|
|
|
|
- app_data_obj.set_string_member(pair.key ?? "", pair.value ?? "");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- obj.set_object_member("app_data", app_data_obj);
|
|
|
|
|
-
|
|
|
|
|
- return obj;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|