graphlite 0.0.1

GraphLite - A lightweight ISO GQL Graph Database
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
// Copyright (c) 2024-2025 DeepGraph Inc.
// SPDX-License-Identifier: Apache-2.0
//
//! Unified session models for database session management
//!
//! This module provides a consolidated session management model that combines
//! authentication, authorization, and database session state management.

use crate::session::transaction_state::SessionTransactionState;
use crate::storage::{GraphCache, StorageManager, Value};
use crate::txn::TransactionManager;
use std::collections::HashMap;
use std::sync::Arc;

/// Stub for session permission cache
#[derive(Clone, Default)]
pub struct SessionPermissionCache {}

impl SessionPermissionCache {
    pub fn new() -> Self {
        Self {}
    }

    /// Check if user can access a specific graph (stub)
    pub fn can_access_graph(&self, _graph_name: &str, _action: &str) -> bool {
        true // Stub: allow all access
    }

    /// Check if user can access a specific schema (stub)
    pub fn can_access_schema(&self, _schema_name: &str, _action: &str) -> bool {
        true // Stub: allow all access
    }

    /// Check if user can perform a system operation (stub)
    pub fn can_perform_operation(&self, _operation: &str) -> bool {
        true // Stub: allow all operations
    }
}

/// Unified user session that combines authentication and database session management
#[derive(Clone)]
pub struct UserSession {
    // === Identity Information ===
    /// Unique session identifier
    pub session_id: String,
    /// User identifier (unique across system)
    pub user_id: String,
    /// User display name
    pub username: String,
    /// User roles for authorization
    pub roles: Vec<String>,

    // === Database Session Context ===
    /// Current graph being used (equivalent to current database/schema)
    pub current_graph: Option<String>,
    /// Current schema context
    pub current_schema: Option<String>,
    /// Current timezone setting
    pub current_timezone: Option<String>,

    // === Session State ===
    /// Session parameters (SET commands, user variables)
    pub parameters: HashMap<String, Value>,
    /// Authorization permission cache
    pub permissions: SessionPermissionCache,
    /// Transaction state for this session (shared reference)
    pub transaction_state: Arc<SessionTransactionState>,

    // === Session Lifecycle ===
    /// When the session was created
    pub created_at: chrono::DateTime<chrono::Utc>,
    /// Last activity timestamp
    pub last_activity: chrono::DateTime<chrono::Utc>,
    /// Whether the session is currently active
    pub active: bool,
}

impl UserSession {
    /// Create a new session from authentication information
    pub fn new(
        username: String,
        roles: Vec<String>,
        permissions: SessionPermissionCache,
        transaction_manager: Arc<TransactionManager>,
    ) -> Self {
        let now = chrono::Utc::now();
        let session_id = uuid::Uuid::new_v4().to_string();
        let transaction_state = Arc::new(SessionTransactionState::new(transaction_manager));

        Self {
            session_id,
            user_id: username.clone(), // Using username as user_id for now
            username,
            roles,
            current_graph: None,
            current_schema: None,
            current_timezone: None,
            parameters: HashMap::new(),
            permissions,
            transaction_state,
            created_at: now,
            last_activity: now,
            active: true,
        }
    }

    // === Graph Context Management ===

    /// Set the current graph for this session
    pub fn set_current_graph(&mut self, graph_path: Option<String>) {
        self.current_graph = graph_path;
        self.update_activity();
    }

    /// Get the current graph for this session
    pub fn get_current_graph(&self) -> Option<&String> {
        self.current_graph.as_ref()
    }

    /// Clear the current graph
    pub fn clear_current_graph(&mut self) {
        self.current_graph = None;
        self.update_activity();
    }

    /// Set the current schema for this session
    pub fn set_current_schema(&mut self, schema: Option<String>) {
        self.current_schema = schema;
        self.update_activity();
    }

    /// Get the current schema for this session
    pub fn get_current_schema(&self) -> Option<&String> {
        self.current_schema.as_ref()
    }

    /// Set the current timezone for this session
    pub fn set_current_timezone(&mut self, timezone: Option<String>) {
        self.current_timezone = timezone;
        self.update_activity();
    }

    /// Get the current timezone for this session
    pub fn get_current_timezone(&self) -> Option<&String> {
        self.current_timezone.as_ref()
    }

    // === Session Parameter Management ===

    /// Set a session parameter
    pub fn set_parameter(&mut self, key: String, value: Value) {
        self.parameters.insert(key, value);
        self.update_activity();
    }

    /// Get a session parameter
    pub fn get_parameter(&self, key: &str) -> Option<&Value> {
        self.parameters.get(key)
    }

    /// Remove a session parameter
    pub fn remove_parameter(&mut self, key: &str) -> Option<Value> {
        self.update_activity();
        self.parameters.remove(key)
    }

    /// Clear all session parameters
    pub fn clear_parameters(&mut self) {
        self.parameters.clear();
        self.update_activity();
    }

    // === Schema Enforcement Configuration ===

    /// Set the schema enforcement mode for this session
    /// Valid values: "strict", "advisory", "disabled"
    pub fn set_schema_enforcement_mode(&mut self, mode: &str) {
        self.set_parameter(
            "schema_enforcement_mode".to_string(),
            Value::String(mode.to_string()),
        );
    }

    /// Get the current schema enforcement mode
    /// Returns "advisory" as default if not set
    pub fn get_schema_enforcement_mode(&self) -> &str {
        self.get_parameter("schema_enforcement_mode")
            .and_then(|v| match v {
                Value::String(s) => Some(s.as_str()),
                _ => None,
            })
            .unwrap_or("advisory")
    }

    /// Set whether to validate on write operations
    pub fn set_validate_on_write(&mut self, enabled: bool) {
        self.set_parameter(
            "schema_validate_on_write".to_string(),
            Value::Boolean(enabled),
        );
    }

    /// Get whether to validate on write operations (default: true)
    pub fn get_validate_on_write(&self) -> bool {
        self.get_parameter("schema_validate_on_write")
            .and_then(|v| match v {
                Value::Boolean(b) => Some(*b),
                _ => None,
            })
            .unwrap_or(true)
    }

    /// Set whether to allow unknown properties not defined in schema
    pub fn set_allow_unknown_properties(&mut self, allow: bool) {
        self.set_parameter(
            "schema_allow_unknown_properties".to_string(),
            Value::Boolean(allow),
        );
    }

    /// Get whether to allow unknown properties (default: false in strict mode, true otherwise)
    pub fn get_allow_unknown_properties(&self) -> bool {
        self.get_parameter("schema_allow_unknown_properties")
            .and_then(|v| match v {
                Value::Boolean(b) => Some(*b),
                _ => None,
            })
            .unwrap_or_else(|| self.get_schema_enforcement_mode() != "strict")
    }

    // === Session Lifecycle Management ===

    /// Update the last activity timestamp
    pub fn update_activity(&mut self) {
        self.last_activity = chrono::Utc::now();
    }

    /// Check if the session is expired (1 hour timeout)
    pub fn is_expired(&self) -> bool {
        let timeout = chrono::Duration::hours(1);
        chrono::Utc::now() - self.last_activity > timeout
    }

    /// Mark session as inactive
    pub fn deactivate(&mut self) {
        self.active = false;
        self.update_activity();
    }

    /// Reset session to defaults (clears graph context and parameters)
    pub fn reset(&mut self, reset_target: Option<SessionResetTarget>) {
        match reset_target {
            Some(SessionResetTarget::Graph) => {
                self.current_graph = None;
            }
            Some(SessionResetTarget::Schema) => {
                self.current_schema = None;
            }
            Some(SessionResetTarget::TimeZone) => {
                self.current_timezone = None;
            }
            Some(SessionResetTarget::AllParameters) => {
                self.parameters.clear();
            }
            Some(SessionResetTarget::AllCharacteristics) => {
                self.current_graph = None;
                self.current_schema = None;
                self.current_timezone = None;
            }
            Some(SessionResetTarget::Parameter(param_name)) => {
                self.parameters.remove(&param_name);
            }
            None => {
                // Reset everything except identity and permissions
                self.current_graph = None;
                self.current_schema = None;
                self.current_timezone = None;
                self.parameters.clear();
            }
        }
        self.update_activity();
    }

    // === Authorization ===

    /// Check if user has a specific role
    pub fn has_role(&self, role_name: &str) -> bool {
        self.roles.contains(&role_name.to_string())
    }

    /// Check if user can access a specific graph
    pub fn can_access_graph(&self, graph_name: &str, action: &str) -> bool {
        self.permissions.can_access_graph(graph_name, action)
    }

    /// Check if user can access a specific schema
    pub fn can_access_schema(&self, schema_name: &str, action: &str) -> bool {
        self.permissions.can_access_schema(schema_name, action)
    }

    /// Check if user can perform a system operation
    pub fn can_perform_operation(&self, operation: &str) -> bool {
        self.permissions.can_perform_operation(operation)
    }
}

/// Session reset targets (from exec::context)
#[derive(Debug, Clone)]
pub enum SessionResetTarget {
    Schema,
    Graph,
    TimeZone,
    AllParameters,
    AllCharacteristics,
    Parameter(String),
}

/// Simplified session for metadata tracking integration
///
/// This is a streamlined session model specifically designed for metadata
/// tracking and testing purposes. It focuses on graph management and user
/// context without the full complexity of UserSession.
#[derive(Debug, Clone)]
pub struct Session {
    /// Username for metadata tracking
    pub username: String,

    /// Storage manager reference
    pub storage: Arc<StorageManager>,

    /// Current graph name
    #[allow(dead_code)]
    // FALSE POSITIVE - Used in set_current_graph() (line 346), get_current_graph() (line 365), get_current_graph_mut() (line 356). Compiler cannot detect field usage across module boundaries when accessed through public API methods.
    pub current_graph: Option<String>,

    /// Graph caches by name
    #[allow(dead_code)]
    // FALSE POSITIVE - Used in set_current_graph() (line 350), get_current_graph() (line 366), get_current_graph_mut() (line 357). Compiler cannot detect field usage across module boundaries when accessed through public API methods.
    pub graphs: HashMap<String, GraphCache>,
}

impl Session {
    /// Create a new session with user and storage
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn new_with_user(username: &str, storage: Arc<StorageManager>) -> Self {
        Self {
            username: username.to_string(),
            storage,
            current_graph: None,
            graphs: HashMap::new(),
        }
    }

    /// Set the current graph for this session
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn set_current_graph(&mut self, name: &str) {
        self.current_graph = Some(name.to_string());

        // Ensure the graph exists in our cache
        if !self.graphs.contains_key(name) {
            self.graphs.insert(name.to_string(), GraphCache::new());
        }
    }

    /// Get a mutable reference to the current graph
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn get_current_graph_mut(&mut self) -> Option<&mut GraphCache> {
        if let Some(graph_name) = &self.current_graph {
            self.graphs.get_mut(graph_name)
        } else {
            None
        }
    }

    /// Get a reference to the current graph
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn get_current_graph(&self) -> Option<&GraphCache> {
        if let Some(graph_name) = &self.current_graph {
            self.graphs.get(graph_name)
        } else {
            None
        }
    }

    /// Get a mutable reference to a specific graph
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn get_graph_mut(&mut self, name: &str) -> Option<&mut GraphCache> {
        self.graphs.get_mut(name)
    }

    /// Get a reference to a specific graph
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn get_graph(&self, name: &str) -> Option<&GraphCache> {
        self.graphs.get(name)
    }

    /// Create or get a graph by name
    #[allow(dead_code)] // ROADMAP v0.2.0 - Session management for multi-user support (see ROADMAP.md §2)
    pub fn get_or_create_graph(&mut self, name: &str) -> &mut GraphCache {
        self.graphs
            .entry(name.to_string())
            .or_insert_with(GraphCache::new)
    }
}