| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- using Invercargill;
- using Invercargill.DataStructures;
- using Json;
- 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 {
- // =========================================================================
- // Identity Properties (required by Authorisation.Identity interface)
- // =========================================================================
- // Backing fields for identity properties
- private string _id = "";
- private string _username = "";
- /**
- * 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;
- }
- }
- }
|