|
@@ -1,6 +1,7 @@
|
|
|
using Implexus.Core;
|
|
using Implexus.Core;
|
|
|
using Invercargill;
|
|
using Invercargill;
|
|
|
using Invercargill.DataStructures;
|
|
using Invercargill.DataStructures;
|
|
|
|
|
+using Inversion;
|
|
|
using InvercargillJson;
|
|
using InvercargillJson;
|
|
|
using Json;
|
|
using Json;
|
|
|
using Astralis;
|
|
using Astralis;
|
|
@@ -129,11 +130,14 @@ namespace Spry.Users {
|
|
|
* - Secure: true (configurable for development)
|
|
* - Secure: true (configurable for development)
|
|
|
* - SameSite: Strict
|
|
* - SameSite: Strict
|
|
|
* - Path: /
|
|
* - Path: /
|
|
|
|
|
+ *
|
|
|
|
|
+ * This service uses the inject<> pattern for dependency injection.
|
|
|
|
|
+ * All methods are async to work with the Implexus async API.
|
|
|
*/
|
|
*/
|
|
|
public class SessionService : GLib.Object {
|
|
public class SessionService : GLib.Object {
|
|
|
|
|
|
|
|
- private Engine _engine;
|
|
|
|
|
- private CryptographyProvider _crypto;
|
|
|
|
|
|
|
+ private Engine _engine = inject<Engine>();
|
|
|
|
|
+ private CryptographyProvider _crypto = inject<CryptographyProvider>();
|
|
|
|
|
|
|
|
// Storage paths
|
|
// Storage paths
|
|
|
private const string BASE_PATH = "/spry/users";
|
|
private const string BASE_PATH = "/spry/users";
|
|
@@ -142,37 +146,40 @@ namespace Spry.Users {
|
|
|
private const string SESSION_TYPE_LABEL = "Session";
|
|
private const string SESSION_TYPE_LABEL = "Session";
|
|
|
|
|
|
|
|
// Cookie configuration
|
|
// Cookie configuration
|
|
|
- private string _cookie_name;
|
|
|
|
|
- private bool _cookie_secure;
|
|
|
|
|
- private TimeSpan _session_duration;
|
|
|
|
|
|
|
+ private string _cookie_name = "spry_session";
|
|
|
|
|
+ private bool _cookie_secure = true;
|
|
|
|
|
+ private TimeSpan _session_duration = TimeSpan.HOUR * 24;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Creates a new SessionService instance.
|
|
|
|
|
- *
|
|
|
|
|
- * @param engine The Implexus engine for data storage
|
|
|
|
|
- * @param crypto The cryptography provider for token operations
|
|
|
|
|
- * @param session_duration Session expiry duration (default 24 hours)
|
|
|
|
|
- * @param cookie_name Name of the session cookie (default "spry_session")
|
|
|
|
|
- * @param cookie_secure Whether cookie should be Secure (default true)
|
|
|
|
|
|
|
+ * The name of the session cookie.
|
|
|
*/
|
|
*/
|
|
|
- public SessionService(
|
|
|
|
|
- Engine engine,
|
|
|
|
|
- CryptographyProvider crypto,
|
|
|
|
|
- TimeSpan? session_duration = null,
|
|
|
|
|
- string cookie_name = "spry_session",
|
|
|
|
|
- bool cookie_secure = true
|
|
|
|
|
- ) {
|
|
|
|
|
- _engine = engine;
|
|
|
|
|
- _crypto = crypto;
|
|
|
|
|
- _cookie_name = cookie_name;
|
|
|
|
|
- _cookie_secure = cookie_secure;
|
|
|
|
|
-
|
|
|
|
|
- if (session_duration != null) {
|
|
|
|
|
- _session_duration = (!)session_duration;
|
|
|
|
|
- } else {
|
|
|
|
|
- // Default: 24 hours
|
|
|
|
|
- _session_duration = TimeSpan.HOUR * 24;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public string cookie_name {
|
|
|
|
|
+ get { return _cookie_name; }
|
|
|
|
|
+ set { _cookie_name = value; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Whether the session cookie should be Secure.
|
|
|
|
|
+ */
|
|
|
|
|
+ public bool cookie_secure {
|
|
|
|
|
+ get { return _cookie_secure; }
|
|
|
|
|
+ set { _cookie_secure = value; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * The session expiry duration.
|
|
|
|
|
+ */
|
|
|
|
|
+ public TimeSpan session_duration {
|
|
|
|
|
+ get { return _session_duration; }
|
|
|
|
|
+ set { _session_duration = value; }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new SessionService instance with default configuration.
|
|
|
|
|
+ * Use properties to customize cookie name, security, and duration.
|
|
|
|
|
+ */
|
|
|
|
|
+ public SessionService() {
|
|
|
|
|
+ // Default configuration - use properties to customize
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =========================================================================
|
|
// =========================================================================
|
|
@@ -191,42 +198,50 @@ namespace Spry.Users {
|
|
|
* @param user_id The user's unique identifier
|
|
* @param user_id The user's unique identifier
|
|
|
* @param ip_address Optional IP address for tracking
|
|
* @param ip_address Optional IP address for tracking
|
|
|
* @param user_agent Optional user agent for tracking
|
|
* @param user_agent Optional user agent for tracking
|
|
|
- * @return The created Session, or null on failure
|
|
|
|
|
|
|
+ * @return The created Session
|
|
|
|
|
+ * @throws Error on failure
|
|
|
*/
|
|
*/
|
|
|
- public Session? create_session(string user_id, string? ip_address = null, string? user_agent = null) {
|
|
|
|
|
- try {
|
|
|
|
|
- // Generate UUID for session
|
|
|
|
|
- var session_id = generate_uuid();
|
|
|
|
|
-
|
|
|
|
|
- // Create session object
|
|
|
|
|
- var session = new Session();
|
|
|
|
|
- session.id = session_id;
|
|
|
|
|
- session.user_id = user_id;
|
|
|
|
|
- session.created_at = new DateTime.now_utc();
|
|
|
|
|
- session.expires_at = new DateTime.now_utc().add(_session_duration);
|
|
|
|
|
- session.ip_address = ip_address;
|
|
|
|
|
- session.user_agent = user_agent;
|
|
|
|
|
-
|
|
|
|
|
- // Get or create storage containers
|
|
|
|
|
- var sessions_container = get_or_create_sessions_container();
|
|
|
|
|
-
|
|
|
|
|
- // Create session document
|
|
|
|
|
- var session_doc = sessions_container.create_document(session_id, SESSION_TYPE_LABEL);
|
|
|
|
|
-
|
|
|
|
|
- // Store session properties
|
|
|
|
|
- store_session_in_document(session_doc, session);
|
|
|
|
|
-
|
|
|
|
|
- // Update user sessions index
|
|
|
|
|
- add_session_to_user_index(user_id, session_id);
|
|
|
|
|
-
|
|
|
|
|
- return session;
|
|
|
|
|
- } catch (EngineError e) {
|
|
|
|
|
- warning("Failed to create session: %s", e.message);
|
|
|
|
|
- return null;
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to create session: %s", e.message);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public async Session create_session_async(string user_id, string? ip_address = null, string? user_agent = null) throws Error {
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: create_session_async() called for user: %s\n", user_id);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: IP address: %s, User-Agent: %s\n",
|
|
|
|
|
+ ip_address ?? "null", user_agent ?? "null");
|
|
|
|
|
+
|
|
|
|
|
+ // Generate UUID for session
|
|
|
|
|
+ var session_id = generate_uuid();
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Generated session ID: %s\n", session_id);
|
|
|
|
|
+
|
|
|
|
|
+ // Create session object
|
|
|
|
|
+ var session = new Session();
|
|
|
|
|
+ session.id = session_id;
|
|
|
|
|
+ session.user_id = user_id;
|
|
|
|
|
+ session.created_at = new DateTime.now_utc();
|
|
|
|
|
+ session.expires_at = new DateTime.now_utc().add(_session_duration);
|
|
|
|
|
+ session.ip_address = ip_address;
|
|
|
|
|
+ session.user_agent = user_agent;
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Session expires at: %s\n", session.expires_at.format_iso8601());
|
|
|
|
|
+
|
|
|
|
|
+ // Get or create storage containers
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Getting sessions container...\n");
|
|
|
|
|
+ var sessions_container = yield get_sessions_container_async();
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Sessions container obtained\n");
|
|
|
|
|
+
|
|
|
|
|
+ // Create session document
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Creating session document...\n");
|
|
|
|
|
+ var session_doc = yield sessions_container.create_document_async(session_id, SESSION_TYPE_LABEL);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Session document created\n");
|
|
|
|
|
+
|
|
|
|
|
+ // Store session properties
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Storing session properties...\n");
|
|
|
|
|
+ yield store_session_in_document_async(session_doc, session);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Session properties stored\n");
|
|
|
|
|
+
|
|
|
|
|
+ // Update user sessions index
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Updating user sessions index...\n");
|
|
|
|
|
+ yield add_session_to_user_index_async(user_id, session_id);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: User sessions index updated\n");
|
|
|
|
|
+
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Session created successfully: %s\n", session_id);
|
|
|
|
|
+ return session;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =========================================================================
|
|
// =========================================================================
|
|
@@ -243,6 +258,10 @@ namespace Spry.Users {
|
|
|
* @return The encrypted token string
|
|
* @return The encrypted token string
|
|
|
*/
|
|
*/
|
|
|
public string generate_session_token(Session session) {
|
|
public string generate_session_token(Session session) {
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: generate_session_token() called for session: %s\n", session.id);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Session user_id: %s, expires_at: %s\n",
|
|
|
|
|
+ session.user_id, session.expires_at.format_iso8601());
|
|
|
|
|
+
|
|
|
// Create JSON payload
|
|
// Create JSON payload
|
|
|
var payload_obj = new Json.Object();
|
|
var payload_obj = new Json.Object();
|
|
|
payload_obj.set_string_member("session_id", session.id);
|
|
payload_obj.set_string_member("session_id", session.id);
|
|
@@ -253,9 +272,13 @@ namespace Spry.Users {
|
|
|
var node = new Json.Node(Json.NodeType.OBJECT);
|
|
var node = new Json.Node(Json.NodeType.OBJECT);
|
|
|
node.set_object(payload_obj);
|
|
node.set_object(payload_obj);
|
|
|
var payload = Json.to_string(node, false);
|
|
var payload = Json.to_string(node, false);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Token payload JSON: %s\n", payload);
|
|
|
|
|
|
|
|
// Sign and seal the token with expiry
|
|
// Sign and seal the token with expiry
|
|
|
- return _crypto.sign_then_seal_token(payload, session.expires_at);
|
|
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Calling sign_then_seal_token()...\n");
|
|
|
|
|
+ var token = _crypto.sign_then_seal_token(payload, session.expires_at);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Token generated successfully (length: %d)\n", token.length);
|
|
|
|
|
+ return token;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =========================================================================
|
|
// =========================================================================
|
|
@@ -274,7 +297,7 @@ namespace Spry.Users {
|
|
|
* @param token The encrypted token string
|
|
* @param token The encrypted token string
|
|
|
* @return A SessionValidationResult with session and user info
|
|
* @return A SessionValidationResult with session and user info
|
|
|
*/
|
|
*/
|
|
|
- public SessionValidationResult validate_session_token(string token) {
|
|
|
|
|
|
|
+ public async SessionValidationResult validate_session_token_async(string token) throws Error {
|
|
|
// Decrypt and verify the token
|
|
// Decrypt and verify the token
|
|
|
var token_result = _crypto.unseal_then_verify_token(token);
|
|
var token_result = _crypto.unseal_then_verify_token(token);
|
|
|
|
|
|
|
@@ -290,40 +313,36 @@ namespace Spry.Users {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Parse the payload
|
|
// Parse the payload
|
|
|
- try {
|
|
|
|
|
- var payload = token_result.payload;
|
|
|
|
|
- if (payload == null) {
|
|
|
|
|
- return new SessionValidationResult.failure("Empty token payload");
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var json = new JsonElement.from_string((!)payload);
|
|
|
|
|
- var obj = json.as<JsonObject>();
|
|
|
|
|
|
|
+ var payload = token_result.payload;
|
|
|
|
|
+ if (payload == null) {
|
|
|
|
|
+ return new SessionValidationResult.failure("Empty token payload");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- var session_id = obj.get("session_id").as<string>();
|
|
|
|
|
- var user_id = obj.get("user_id").as<string>();
|
|
|
|
|
- var expires_at_str = obj.get("expires_at").as<string>();
|
|
|
|
|
- var expires_at = new DateTime.from_iso8601(expires_at_str, new TimeZone.utc());
|
|
|
|
|
|
|
+ var json = new JsonElement.from_string((!)payload);
|
|
|
|
|
+ var obj = json.as<JsonObject>();
|
|
|
|
|
|
|
|
- // Check expiry again
|
|
|
|
|
- if (expires_at.compare(new DateTime.now_utc()) <= 0) {
|
|
|
|
|
- return new SessionValidationResult.failure("Session has expired");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var session_id = obj.get("session_id").as<string>();
|
|
|
|
|
+ var user_id = obj.get("user_id").as<string>();
|
|
|
|
|
+ var expires_at_str = obj.get("expires_at").as<string>();
|
|
|
|
|
+ var expires_at = new DateTime.from_iso8601(expires_at_str, new TimeZone.utc());
|
|
|
|
|
|
|
|
- // Load session from storage
|
|
|
|
|
- var session = get_session(session_id);
|
|
|
|
|
- if (session == null) {
|
|
|
|
|
- return new SessionValidationResult.failure("Session not found");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Check expiry again
|
|
|
|
|
+ if (expires_at.compare(new DateTime.now_utc()) <= 0) {
|
|
|
|
|
+ return new SessionValidationResult.failure("Session has expired");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Verify session matches token data
|
|
|
|
|
- if (session.user_id != user_id) {
|
|
|
|
|
- return new SessionValidationResult.failure("Session user mismatch");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Load session from storage
|
|
|
|
|
+ var session = yield get_session_async(session_id);
|
|
|
|
|
+ if (session == null) {
|
|
|
|
|
+ return new SessionValidationResult.failure("Session not found");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return new SessionValidationResult.success(session);
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- return new SessionValidationResult.failure("Invalid token format: %s".printf(e.message));
|
|
|
|
|
|
|
+ // Verify session matches token data
|
|
|
|
|
+ if (session.user_id != user_id) {
|
|
|
|
|
+ return new SessionValidationResult.failure("Session user mismatch");
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return new SessionValidationResult.success(session);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =========================================================================
|
|
// =========================================================================
|
|
@@ -335,27 +354,24 @@ namespace Spry.Users {
|
|
|
*
|
|
*
|
|
|
* @param session_id The session's unique identifier
|
|
* @param session_id The session's unique identifier
|
|
|
* @return The Session, or null if not found or expired
|
|
* @return The Session, or null if not found or expired
|
|
|
|
|
+ * @throws Error on storage failure
|
|
|
*/
|
|
*/
|
|
|
- public Session? get_session(string session_id) {
|
|
|
|
|
- try {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER/$session_id");
|
|
|
|
|
- var entity = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ public async Session? get_session_async(string session_id) throws Error {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER/$session_id");
|
|
|
|
|
+ var entity = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- if (entity == null || entity.entity_type != EntityType.DOCUMENT) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (entity == null || entity.entity_type != EntityType.DOCUMENT) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- var session = load_session_from_document((!)entity);
|
|
|
|
|
|
|
+ var session = yield load_session_from_document_async((!)entity);
|
|
|
|
|
|
|
|
- // Don't return expired sessions
|
|
|
|
|
- if (session != null && session.is_expired()) {
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return session;
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
|
|
+ // Don't return expired sessions
|
|
|
|
|
+ if (session != null && session.is_expired()) {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return session;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -363,21 +379,18 @@ namespace Spry.Users {
|
|
|
*
|
|
*
|
|
|
* @param user_id The user's unique identifier
|
|
* @param user_id The user's unique identifier
|
|
|
* @return A Vector of active (non-expired) sessions
|
|
* @return A Vector of active (non-expired) sessions
|
|
|
|
|
+ * @throws Error on storage failure
|
|
|
*/
|
|
*/
|
|
|
- public Vector<Session> get_sessions_for_user(string user_id) {
|
|
|
|
|
|
|
+ public async Vector<Session> get_sessions_for_user_async(string user_id) throws Error {
|
|
|
var sessions = new Vector<Session>();
|
|
var sessions = new Vector<Session>();
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- var session_ids = get_session_ids_for_user(user_id);
|
|
|
|
|
|
|
+ var session_ids = yield get_session_ids_for_user_async(user_id);
|
|
|
|
|
|
|
|
- foreach (var session_id in session_ids) {
|
|
|
|
|
- var session = get_session(session_id);
|
|
|
|
|
- if (session != null) {
|
|
|
|
|
- sessions.add(session);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ foreach (var session_id in session_ids) {
|
|
|
|
|
+ var session = yield get_session_async(session_id);
|
|
|
|
|
+ if (session != null) {
|
|
|
|
|
+ sessions.add(session);
|
|
|
}
|
|
}
|
|
|
- } catch (Error e) {
|
|
|
|
|
- // Return empty list on error
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return sessions;
|
|
return sessions;
|
|
@@ -395,59 +408,49 @@ namespace Spry.Users {
|
|
|
* - Updates the user sessions index
|
|
* - Updates the user sessions index
|
|
|
*
|
|
*
|
|
|
* @param session_id The session's unique identifier
|
|
* @param session_id The session's unique identifier
|
|
|
- * @return true on success, false on failure
|
|
|
|
|
|
|
+ * @throws Error on failure
|
|
|
*/
|
|
*/
|
|
|
- public bool delete_session(string session_id) {
|
|
|
|
|
- try {
|
|
|
|
|
- // Get session first to update user index
|
|
|
|
|
- var session = get_session(session_id);
|
|
|
|
|
- if (session == null) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var user_id = session.user_id;
|
|
|
|
|
-
|
|
|
|
|
- // Delete session document
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER/$session_id");
|
|
|
|
|
- var entity = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ public async void delete_session_async(string session_id) throws Error {
|
|
|
|
|
+ // Get session first to update user index
|
|
|
|
|
+ var session = yield get_session_async(session_id);
|
|
|
|
|
+ if (session == null) {
|
|
|
|
|
+ throw new SessionError.SESSION_NOT_FOUND("Session not found");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (entity != null) {
|
|
|
|
|
- entity.delete();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var user_id = session.user_id;
|
|
|
|
|
|
|
|
- // Update user sessions index
|
|
|
|
|
- remove_session_from_user_index(user_id, session_id);
|
|
|
|
|
|
|
+ // Delete session document
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER/$session_id");
|
|
|
|
|
+ var entity = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to delete session: %s", e.message);
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ if (entity != null) {
|
|
|
|
|
+ yield entity.delete_async();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Update user sessions index
|
|
|
|
|
+ yield remove_session_from_user_index_async(user_id, session_id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Deletes all sessions for a user.
|
|
* Deletes all sessions for a user.
|
|
|
*
|
|
*
|
|
|
* @param user_id The user's unique identifier
|
|
* @param user_id The user's unique identifier
|
|
|
|
|
+ * @throws Error on storage failure
|
|
|
*/
|
|
*/
|
|
|
- public void delete_all_sessions_for_user(string user_id) {
|
|
|
|
|
- try {
|
|
|
|
|
- var session_ids = get_session_ids_for_user(user_id);
|
|
|
|
|
|
|
+ public async void delete_all_sessions_for_user_async(string user_id) throws Error {
|
|
|
|
|
+ var session_ids = yield get_session_ids_for_user_async(user_id);
|
|
|
|
|
|
|
|
- foreach (var session_id in session_ids) {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER/$session_id");
|
|
|
|
|
- var entity = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ foreach (var session_id in session_ids) {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER/$session_id");
|
|
|
|
|
+ var entity = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- if (entity != null) {
|
|
|
|
|
- entity.delete();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (entity != null) {
|
|
|
|
|
+ yield entity.delete_async();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // Clear user sessions index
|
|
|
|
|
- clear_user_sessions_index(user_id);
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to delete all sessions for user: %s", e.message);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Clear user sessions index
|
|
|
|
|
+ yield clear_user_sessions_index_async(user_id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// =========================================================================
|
|
// =========================================================================
|
|
@@ -467,8 +470,13 @@ namespace Spry.Users {
|
|
|
* @param token The session token to set
|
|
* @param token The session token to set
|
|
|
*/
|
|
*/
|
|
|
public void set_session_cookie(HttpResult result, string token) {
|
|
public void set_session_cookie(HttpResult result, string token) {
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: set_session_cookie() called\n");
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Cookie name: %s, Token length: %d\n", _cookie_name, token.length);
|
|
|
|
|
+
|
|
|
// Build the Set-Cookie header value manually
|
|
// Build the Set-Cookie header value manually
|
|
|
var max_age = (int)(_session_duration / TimeSpan.SECOND);
|
|
var max_age = (int)(_session_duration / TimeSpan.SECOND);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Max-Age: %d seconds\n", max_age);
|
|
|
|
|
+
|
|
|
var cookie_value = @"$_cookie_name=$token; Path=/; Max-Age=$max_age; HttpOnly";
|
|
var cookie_value = @"$_cookie_name=$token; Path=/; Max-Age=$max_age; HttpOnly";
|
|
|
|
|
|
|
|
if (_cookie_secure) {
|
|
if (_cookie_secure) {
|
|
@@ -477,7 +485,11 @@ namespace Spry.Users {
|
|
|
|
|
|
|
|
cookie_value += "; SameSite=Strict";
|
|
cookie_value += "; SameSite=Strict";
|
|
|
|
|
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Cookie header value (length: %d): %s\n",
|
|
|
|
|
+ cookie_value.length, cookie_value.substring(0, int.min(100, cookie_value.length)) + "...");
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Calling result.set_header('Set-Cookie', ...)\n");
|
|
|
result.set_header("Set-Cookie", cookie_value);
|
|
result.set_header("Set-Cookie", cookie_value);
|
|
|
|
|
+ stdout.printf("SESSION DEBUG: Cookie header set successfully\n");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -516,35 +528,34 @@ namespace Spry.Users {
|
|
|
*
|
|
*
|
|
|
* This method iterates through all sessions and removes those
|
|
* This method iterates through all sessions and removes those
|
|
|
* that have passed their expiry time.
|
|
* that have passed their expiry time.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @throws Error on storage failure
|
|
|
*/
|
|
*/
|
|
|
- public void cleanup_expired_sessions() {
|
|
|
|
|
- try {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER");
|
|
|
|
|
- var container = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ public async void cleanup_expired_sessions_async() throws Error {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_CONTAINER");
|
|
|
|
|
+ var container = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- if (container == null) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var expired_session_ids = new Vector<string>();
|
|
|
|
|
|
|
+ if (container == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- foreach (var child in container.get_children()) {
|
|
|
|
|
- if (child.entity_type != EntityType.DOCUMENT) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var expired_session_ids = new Vector<string>();
|
|
|
|
|
|
|
|
- var session = load_session_from_document(child);
|
|
|
|
|
- if (session != null && session.is_expired()) {
|
|
|
|
|
- expired_session_ids.add(session.id);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var children = yield container.get_children_async();
|
|
|
|
|
+ foreach (var child in children) {
|
|
|
|
|
+ if (child.entity_type != EntityType.DOCUMENT) {
|
|
|
|
|
+ continue;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Delete expired sessions
|
|
|
|
|
- foreach (var session_id in expired_session_ids) {
|
|
|
|
|
- delete_session(session_id);
|
|
|
|
|
|
|
+ var session = yield load_session_from_document_async(child);
|
|
|
|
|
+ if (session != null && session.is_expired()) {
|
|
|
|
|
+ expired_session_ids.add(session.id);
|
|
|
}
|
|
}
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to cleanup expired sessions: %s", e.message);
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Delete expired sessions
|
|
|
|
|
+ foreach (var session_id in expired_session_ids) {
|
|
|
|
|
+ yield delete_session_async(session_id);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -564,8 +575,9 @@ namespace Spry.Users {
|
|
|
* @param http_context The HttpContext containing the request to authenticate
|
|
* @param http_context The HttpContext containing the request to authenticate
|
|
|
* @param user_service The UserService to load users from
|
|
* @param user_service The UserService to load users from
|
|
|
* @return An AuthResult with authentication status and user/session info
|
|
* @return An AuthResult with authentication status and user/session info
|
|
|
|
|
+ * @throws Error on storage failure
|
|
|
*/
|
|
*/
|
|
|
- public AuthResult authenticate_request(HttpContext http_context, UserService user_service) {
|
|
|
|
|
|
|
+ public async AuthResult authenticate_request_async(HttpContext http_context, UserService user_service) throws Error {
|
|
|
// Get session cookie
|
|
// Get session cookie
|
|
|
var token = get_session_cookie(http_context);
|
|
var token = get_session_cookie(http_context);
|
|
|
|
|
|
|
@@ -574,7 +586,7 @@ namespace Spry.Users {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Validate token
|
|
// Validate token
|
|
|
- var validation = validate_session_token((!)token);
|
|
|
|
|
|
|
+ var validation = yield validate_session_token_async((!)token);
|
|
|
|
|
|
|
|
if (!validation.is_valid) {
|
|
if (!validation.is_valid) {
|
|
|
return new AuthResult.failure(validation.error_message ?? "Invalid session");
|
|
return new AuthResult.failure(validation.error_message ?? "Invalid session");
|
|
@@ -586,7 +598,7 @@ namespace Spry.Users {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Load user
|
|
// Load user
|
|
|
- var user = user_service.get_user(session.user_id);
|
|
|
|
|
|
|
+ var user = yield user_service.get_user_async(session.user_id);
|
|
|
if (user == null) {
|
|
if (user == null) {
|
|
|
return new AuthResult.failure("User not found");
|
|
return new AuthResult.failure("User not found");
|
|
|
}
|
|
}
|
|
@@ -598,71 +610,66 @@ namespace Spry.Users {
|
|
|
// Private Helper Methods
|
|
// Private Helper Methods
|
|
|
// =========================================================================
|
|
// =========================================================================
|
|
|
|
|
|
|
|
- private Entity get_or_create_sessions_container() throws EngineError {
|
|
|
|
|
- var root = _engine.get_root();
|
|
|
|
|
|
|
+ private async Entity get_sessions_container_async() throws Error {
|
|
|
|
|
+ var root = yield _engine.get_root_async();
|
|
|
|
|
|
|
|
// Get or create /spry
|
|
// Get or create /spry
|
|
|
- Entity? spry = root.get_child("spry");
|
|
|
|
|
|
|
+ Entity? spry = yield root.get_child_async("spry");
|
|
|
if (spry == null) {
|
|
if (spry == null) {
|
|
|
- spry = root.create_container("spry");
|
|
|
|
|
|
|
+ spry = yield root.create_container_async("spry");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Get or create /spry/users
|
|
// Get or create /spry/users
|
|
|
- Entity? users_base = spry.get_child("users");
|
|
|
|
|
|
|
+ Entity? users_base = yield spry.get_child_async("users");
|
|
|
if (users_base == null) {
|
|
if (users_base == null) {
|
|
|
- users_base = spry.create_container("users");
|
|
|
|
|
|
|
+ users_base = yield spry.create_container_async("users");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Get or create /spry/users/sessions
|
|
// Get or create /spry/users/sessions
|
|
|
- Entity? sessions_container = users_base.get_child(SESSIONS_CONTAINER);
|
|
|
|
|
|
|
+ Entity? sessions_container = yield users_base.get_child_async(SESSIONS_CONTAINER);
|
|
|
if (sessions_container == null) {
|
|
if (sessions_container == null) {
|
|
|
- sessions_container = users_base.create_container(SESSIONS_CONTAINER);
|
|
|
|
|
|
|
+ sessions_container = yield users_base.create_container_async(SESSIONS_CONTAINER);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (!)sessions_container;
|
|
return (!)sessions_container;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private Entity? get_sessions_by_user_container() {
|
|
|
|
|
- try {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER");
|
|
|
|
|
- var container = _engine.get_entity_or_null(path);
|
|
|
|
|
-
|
|
|
|
|
- if (container == null) {
|
|
|
|
|
- // Create it if it doesn't exist
|
|
|
|
|
- var root = _engine.get_root();
|
|
|
|
|
- Entity? spry = root.get_child("spry");
|
|
|
|
|
- if (spry == null) {
|
|
|
|
|
- spry = root.create_container("spry");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private async Entity? get_sessions_by_user_container_async() throws Error {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER");
|
|
|
|
|
+ var container = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- Entity? users_base = spry.get_child("users");
|
|
|
|
|
- if (users_base == null) {
|
|
|
|
|
- users_base = spry.create_container("users");
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (container == null) {
|
|
|
|
|
+ // Create it if it doesn't exist
|
|
|
|
|
+ var root = yield _engine.get_root_async();
|
|
|
|
|
+ Entity? spry = yield root.get_child_async("spry");
|
|
|
|
|
+ if (spry == null) {
|
|
|
|
|
+ spry = yield root.create_container_async("spry");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- container = users_base.create_container(SESSIONS_BY_USER_CONTAINER);
|
|
|
|
|
|
|
+ Entity? users_base = yield spry.get_child_async("users");
|
|
|
|
|
+ if (users_base == null) {
|
|
|
|
|
+ users_base = yield spry.create_container_async("users");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return container;
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to get sessions_by_user container: %s", e.message);
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ container = yield users_base.create_container_async(SESSIONS_BY_USER_CONTAINER);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return container;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void store_session_in_document(Entity doc, Session session) throws EngineError {
|
|
|
|
|
|
|
+ private async void store_session_in_document_async(Entity doc, Session session) throws Error {
|
|
|
var props = session.to_json();
|
|
var props = session.to_json();
|
|
|
|
|
|
|
|
// Store each JSON member as a property
|
|
// Store each JSON member as a property
|
|
|
foreach (var member_name in props.get_members()) {
|
|
foreach (var member_name in props.get_members()) {
|
|
|
var element = json_to_element(props.get_member(member_name));
|
|
var element = json_to_element(props.get_member(member_name));
|
|
|
- doc.set_entity_property(member_name, element);
|
|
|
|
|
|
|
+ yield doc.set_entity_property_async(member_name, element);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private Session? load_session_from_document(Entity doc) {
|
|
|
|
|
|
|
+ private async Session? load_session_from_document_async(Entity doc) {
|
|
|
try {
|
|
try {
|
|
|
- var props = doc.properties;
|
|
|
|
|
|
|
+ var props = yield doc.get_properties_async();
|
|
|
var json_obj = properties_to_json(props);
|
|
var json_obj = properties_to_json(props);
|
|
|
return Session.from_json(json_obj);
|
|
return Session.from_json(json_obj);
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
@@ -714,15 +721,15 @@ namespace Spry.Users {
|
|
|
|
|
|
|
|
private Json.Node element_to_json(Element element) {
|
|
private Json.Node element_to_json(Element element) {
|
|
|
if (element is NullElement) {
|
|
if (element is NullElement) {
|
|
|
- return new Json.Node.alloc();
|
|
|
|
|
|
|
+ return new Json.Node(Json.NodeType.NULL);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Try to get as string
|
|
// Try to get as string
|
|
|
if (element.assignable_to<string>()) {
|
|
if (element.assignable_to<string>()) {
|
|
|
try {
|
|
try {
|
|
|
var str_value = element.as<string>();
|
|
var str_value = element.as<string>();
|
|
|
- var node = new Json.Node.alloc();
|
|
|
|
|
- node.set_string(str_value);
|
|
|
|
|
|
|
+ var node = new Json.Node(Json.NodeType.VALUE);
|
|
|
|
|
+ node.set_string(str_value ?? "");
|
|
|
return node;
|
|
return node;
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
|
// Fall through
|
|
// Fall through
|
|
@@ -733,7 +740,7 @@ namespace Spry.Users {
|
|
|
if (element.assignable_to<bool>()) {
|
|
if (element.assignable_to<bool>()) {
|
|
|
try {
|
|
try {
|
|
|
var bool_value = element.as<bool>();
|
|
var bool_value = element.as<bool>();
|
|
|
- var node = new Json.Node.alloc();
|
|
|
|
|
|
|
+ var node = new Json.Node(Json.NodeType.VALUE);
|
|
|
node.set_boolean(bool_value);
|
|
node.set_boolean(bool_value);
|
|
|
return node;
|
|
return node;
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
@@ -746,7 +753,7 @@ namespace Spry.Users {
|
|
|
try {
|
|
try {
|
|
|
var int_value = element.as<int64?>();
|
|
var int_value = element.as<int64?>();
|
|
|
if (int_value != null) {
|
|
if (int_value != null) {
|
|
|
- var node = new Json.Node.alloc();
|
|
|
|
|
|
|
+ var node = new Json.Node(Json.NodeType.VALUE);
|
|
|
node.set_int((!)int_value);
|
|
node.set_int((!)int_value);
|
|
|
return node;
|
|
return node;
|
|
|
}
|
|
}
|
|
@@ -760,7 +767,7 @@ namespace Spry.Users {
|
|
|
try {
|
|
try {
|
|
|
var double_value = element.as<double?>();
|
|
var double_value = element.as<double?>();
|
|
|
if (double_value != null) {
|
|
if (double_value != null) {
|
|
|
- var node = new Json.Node.alloc();
|
|
|
|
|
|
|
+ var node = new Json.Node(Json.NodeType.VALUE);
|
|
|
node.set_double((!)double_value);
|
|
node.set_double((!)double_value);
|
|
|
return node;
|
|
return node;
|
|
|
}
|
|
}
|
|
@@ -777,7 +784,7 @@ namespace Spry.Users {
|
|
|
foreach (var item in series) {
|
|
foreach (var item in series) {
|
|
|
arr.add_element(element_to_json(item));
|
|
arr.add_element(element_to_json(item));
|
|
|
}
|
|
}
|
|
|
- var node = new Json.Node.alloc();
|
|
|
|
|
|
|
+ var node = new Json.Node(Json.NodeType.ARRAY);
|
|
|
node.set_array(arr);
|
|
node.set_array(arr);
|
|
|
return node;
|
|
return node;
|
|
|
} catch (Error e) {
|
|
} catch (Error e) {
|
|
@@ -787,26 +794,26 @@ namespace Spry.Users {
|
|
|
|
|
|
|
|
// Try as Properties for objects
|
|
// Try as Properties for objects
|
|
|
if (element is Properties) {
|
|
if (element is Properties) {
|
|
|
- var node = new Json.Node.alloc();
|
|
|
|
|
|
|
+ var node = new Json.Node(Json.NodeType.OBJECT);
|
|
|
node.set_object(properties_to_json((Properties)element));
|
|
node.set_object(properties_to_json((Properties)element));
|
|
|
return node;
|
|
return node;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Default: return null
|
|
// Default: return null
|
|
|
- return new Json.Node.alloc();
|
|
|
|
|
|
|
+ return new Json.Node(Json.NodeType.NULL);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void add_session_to_user_index(string user_id, string session_id) throws EngineError {
|
|
|
|
|
- var container = get_sessions_by_user_container();
|
|
|
|
|
|
|
+ private async void add_session_to_user_index_async(string user_id, string session_id) throws Error {
|
|
|
|
|
+ var container = yield get_sessions_by_user_container_async();
|
|
|
if (container == null) return;
|
|
if (container == null) return;
|
|
|
|
|
|
|
|
// Get or create the user's session list document
|
|
// Get or create the user's session list document
|
|
|
var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
- var existing = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ var existing = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
Vector<string> session_ids;
|
|
Vector<string> session_ids;
|
|
|
if (existing != null) {
|
|
if (existing != null) {
|
|
|
- session_ids = load_session_ids_from_document((!)existing);
|
|
|
|
|
|
|
+ session_ids = yield load_session_ids_from_document_async((!)existing);
|
|
|
} else {
|
|
} else {
|
|
|
session_ids = new Vector<string>();
|
|
session_ids = new Vector<string>();
|
|
|
}
|
|
}
|
|
@@ -825,75 +832,65 @@ namespace Spry.Users {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Store updated list
|
|
// Store updated list
|
|
|
- var doc = existing;
|
|
|
|
|
- if (doc == null) {
|
|
|
|
|
- doc = container.create_document(user_id, "UserSessionsIndex");
|
|
|
|
|
|
|
+ Entity doc;
|
|
|
|
|
+ if (existing != null) {
|
|
|
|
|
+ doc = existing;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ doc = yield container.create_document_async(user_id, "UserSessionsIndex");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- store_session_ids_in_document(doc, session_ids);
|
|
|
|
|
|
|
+ yield store_session_ids_in_document_async(doc, session_ids);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void remove_session_from_user_index(string user_id, string session_id) {
|
|
|
|
|
- try {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
|
|
- var existing = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ private async void remove_session_from_user_index_async(string user_id, string session_id) throws Error {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
|
|
+ var existing = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- if (existing == null) return;
|
|
|
|
|
|
|
+ if (existing == null) return;
|
|
|
|
|
|
|
|
- var session_ids = load_session_ids_from_document((!)existing);
|
|
|
|
|
|
|
+ var session_ids = yield load_session_ids_from_document_async((!)existing);
|
|
|
|
|
|
|
|
- // Remove the session ID
|
|
|
|
|
- var new_ids = new Vector<string>();
|
|
|
|
|
- foreach (var id in session_ids) {
|
|
|
|
|
- if (id != session_id) {
|
|
|
|
|
- new_ids.add(id);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Remove the session ID
|
|
|
|
|
+ var new_ids = new Vector<string>();
|
|
|
|
|
+ foreach (var id in session_ids) {
|
|
|
|
|
+ if (id != session_id) {
|
|
|
|
|
+ new_ids.add(id);
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (new_ids.length == 0) {
|
|
|
|
|
- // Delete the index document if no sessions left
|
|
|
|
|
- existing.delete();
|
|
|
|
|
- } else {
|
|
|
|
|
- store_session_ids_in_document((!)existing, new_ids);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to remove session from user index: %s", e.message);
|
|
|
|
|
|
|
+ if (new_ids.length == 0) {
|
|
|
|
|
+ // Delete the index document if no sessions left
|
|
|
|
|
+ yield existing.delete_async();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ yield store_session_ids_in_document_async((!)existing, new_ids);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void clear_user_sessions_index(string user_id) {
|
|
|
|
|
- try {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
|
|
- var existing = _engine.get_entity_or_null(path);
|
|
|
|
|
|
|
+ private async void clear_user_sessions_index_async(string user_id) throws Error {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
|
|
+ var existing = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- if (existing != null) {
|
|
|
|
|
- existing.delete();
|
|
|
|
|
- }
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
- warning("Failed to clear user sessions index: %s", e.message);
|
|
|
|
|
|
|
+ if (existing != null) {
|
|
|
|
|
+ yield existing.delete_async();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private Vector<string> get_session_ids_for_user(string user_id) {
|
|
|
|
|
- try {
|
|
|
|
|
- var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
|
|
- var existing = _engine.get_entity_or_null(path);
|
|
|
|
|
-
|
|
|
|
|
- if (existing == null) {
|
|
|
|
|
- return new Vector<string>();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ private async Vector<string> get_session_ids_for_user_async(string user_id) throws Error {
|
|
|
|
|
+ var path = new EntityPath(@"$BASE_PATH/$SESSIONS_BY_USER_CONTAINER/$user_id");
|
|
|
|
|
+ var existing = yield _engine.get_entity_or_null_async(path);
|
|
|
|
|
|
|
|
- return load_session_ids_from_document((!)existing);
|
|
|
|
|
- } catch (Error e) {
|
|
|
|
|
|
|
+ if (existing == null) {
|
|
|
return new Vector<string>();
|
|
return new Vector<string>();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return yield load_session_ids_from_document_async((!)existing);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private Vector<string> load_session_ids_from_document(Entity doc) {
|
|
|
|
|
|
|
+ private async Vector<string> load_session_ids_from_document_async(Entity doc) {
|
|
|
var session_ids = new Vector<string>();
|
|
var session_ids = new Vector<string>();
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- var props = doc.properties;
|
|
|
|
|
|
|
+ var props = yield doc.get_properties_async();
|
|
|
if (props.has("session_ids")) {
|
|
if (props.has("session_ids")) {
|
|
|
var element = props.get("session_ids");
|
|
var element = props.get("session_ids");
|
|
|
if (element != null && element.assignable_to<Series<Element>>()) {
|
|
if (element != null && element.assignable_to<Series<Element>>()) {
|
|
@@ -912,12 +909,12 @@ namespace Spry.Users {
|
|
|
return session_ids;
|
|
return session_ids;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void store_session_ids_in_document(Entity doc, Vector<string> session_ids) throws EngineError {
|
|
|
|
|
|
|
+ private async void store_session_ids_in_document_async(Entity doc, Vector<string> session_ids) throws Error {
|
|
|
var series = new Series<Element>();
|
|
var series = new Series<Element>();
|
|
|
foreach (var id in session_ids) {
|
|
foreach (var id in session_ids) {
|
|
|
series.add(new NativeElement<string>(id));
|
|
series.add(new NativeElement<string>(id));
|
|
|
}
|
|
}
|
|
|
- doc.set_entity_property("session_ids", new NativeElement<Series<Element>>(series));
|
|
|
|
|
|
|
+ yield doc.set_entity_property_async("session_ids", new NativeElement<Series<Element>>(series));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private string generate_uuid() {
|
|
private string generate_uuid() {
|