/ src / profile / identity.rs
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  }