identity.rs
1 mod auth; 2 mod certificate; 3 mod database; 4 mod item; 5 mod memory; 6 7 use anyhow::{Result, bail}; 8 use auth::Auth; 9 use database::Database; 10 use gtk::glib::DateTime; 11 use item::Item; 12 use memory::Memory; 13 use r2d2::Pool; 14 use r2d2_sqlite::SqliteConnectionManager; 15 use sqlite::Transaction; 16 17 /// Authorization wrapper for Gemini protocol 18 /// 19 /// https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates 20 pub struct Identity { 21 pub auth: Auth, 22 pub database: Database, 23 pub memory: Memory, 24 } // @TODO remove pub access 25 26 impl Identity { 27 // Constructors 28 29 /// Create new `Self` 30 pub fn build( 31 database_pool: &Pool<SqliteConnectionManager>, 32 profile_identity_id: i64, 33 ) -> Result<Self> { 34 // Init components 35 let auth = Auth::build(database_pool)?; 36 let database = Database::build(database_pool, profile_identity_id); 37 let memory = Memory::new(); 38 39 // Init `Self` 40 let this = Self { 41 auth, 42 database, 43 memory, 44 }; 45 46 // Build initial index 47 Self::index(&this)?; 48 49 Ok(this) 50 } 51 52 // Actions 53 54 /// Add new record to database, update memory index 55 /// * return new `profile_identity_id` on success 56 pub fn add(&self, pem: &str) -> Result<i64> { 57 let profile_identity_id = self.database.add(pem)?; 58 self.index()?; 59 Ok(profile_identity_id) 60 } 61 62 /// Delete record from database including children dependencies, update memory index 63 pub fn delete(&self, profile_identity_id: i64) -> Result<()> { 64 self.auth.remove_ref(profile_identity_id)?; 65 self.database.delete(profile_identity_id)?; 66 self.index()?; 67 Ok(()) 68 } 69 70 /// Generate new certificate and insert record to DB, update memory index 71 /// * return new `profile_identity_id` on success 72 pub fn make(&self, time: Option<(DateTime, DateTime)>, name: &str) -> Result<i64> { 73 // Generate new certificate 74 match certificate::generate( 75 match time { 76 Some(value) => value, 77 None => ( 78 DateTime::now_local()?, 79 DateTime::from_local(9999, 12, 31, 23, 59, 59.9)?, // max @TODO 80 ), 81 }, 82 name, 83 ) { 84 Ok(pem) => self.add(&pem), 85 Err(e) => bail!("Could not create certificate: {e}"), 86 } 87 } 88 89 /// Create new `Memory` index from `Database` for `Self` 90 pub fn index(&self) -> Result<()> { 91 // Clear previous records 92 self.memory.clear()?; 93 for record in self.database.records()? { 94 self.memory.add(record.id, record.pem)?; 95 } 96 Ok(()) 97 } 98 99 /// Get `Identity` match `request` 100 /// * [Client certificates specification](https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates) 101 /// * this function work with memory cache (not database) 102 pub fn get(&self, request: &str) -> Option<Item> { 103 if let Some(auth) = self.auth.get(request) { 104 match self.memory.get(auth.profile_identity_id) { 105 Ok(pem) => { 106 return Some(Item { 107 // scope: auth.scope, 108 pem, 109 }); 110 } 111 Err(e) => todo!("{e}"), 112 } 113 } 114 None 115 } 116 } 117 118 // Tools 119 120 pub fn migrate(tx: &Transaction) -> Result<()> { 121 // Migrate self components 122 database::init(tx)?; 123 124 // Delegate migration to childs 125 auth::migrate(tx)?; 126 127 // Success 128 Ok(()) 129 }