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
mod auth;
mod certificate;
mod database;
mod item;
mod memory;
use anyhow::{Result, bail};
use auth::Auth;
use database::Database;
use gtk::glib::DateTime;
use item::Item;
use memory::Memory;
use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction;
/// Authorization wrapper for Gemini protocol
///
/// https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates
pub struct Identity {
pub auth: Auth,
pub database: Database,
pub memory: Memory,
} // @TODO remove pub access
impl Identity {
// Constructors
/// Create new `Self`
pub fn build(
database_pool: &Pool<SqliteConnectionManager>,
profile_identity_id: i64,
) -> Result<Self> {
// Init components
let auth = Auth::build(database_pool)?;
let database = Database::build(database_pool, profile_identity_id);
let memory = Memory::new();
// Init `Self`
let this = Self {
auth,
database,
memory,
};
// Build initial index
Self::index(&this)?;
Ok(this)
}
// Actions
/// Add new record to database, update memory index
/// * return new `profile_identity_id` on success
pub fn add(&self, pem: &str) -> Result<i64> {
let profile_identity_id = self.database.add(pem)?;
self.index()?;
Ok(profile_identity_id)
}
/// Delete record from database including children dependencies, update memory index
pub fn delete(&self, profile_identity_id: i64) -> Result<()> {
self.auth.remove_ref(profile_identity_id)?;
self.database.delete(profile_identity_id)?;
self.index()?;
Ok(())
}
/// Generate new certificate and insert record to DB, update memory index
/// * return new `profile_identity_id` on success
pub fn make(&self, time: Option<(DateTime, DateTime)>, name: &str) -> Result<i64> {
// Generate new certificate
match certificate::generate(
match time {
Some(value) => value,
None => (
DateTime::now_local()?,
DateTime::from_local(9999, 12, 31, 23, 59, 59.9)?, // max @TODO
),
},
name,
) {
Ok(pem) => self.add(&pem),
Err(e) => bail!("Could not create certificate: {e}"),
}
}
/// Create new `Memory` index from `Database` for `Self`
pub fn index(&self) -> Result<()> {
// Clear previous records
self.memory.clear()?;
for record in self.database.records()? {
self.memory.add(record.id, record.pem)?;
}
Ok(())
}
/// Get `Identity` match `request`
/// * [Client certificates specification](https://geminiprotocol.net/docs/protocol-specification.gmi#client-certificates)
/// * this function work with memory cache (not database)
pub fn get(&self, request: &str) -> Option<Item> {
if let Some(auth) = self.auth.get(request) {
match self.memory.get(auth.profile_identity_id) {
Ok(pem) => {
return Some(Item {
// scope: auth.scope,
pem,
});
}
Err(e) => todo!("{e}"),
}
}
None
}
}
// Tools
pub fn migrate(tx: &Transaction) -> Result<()> {
// Migrate self components
database::init(tx)?;
// Delegate migration to childs
auth::migrate(tx)?;
// Success
Ok(())
}