mirror of
https://github.com/chatmail/core.git
synced 2026-04-19 06:26:30 +03:00
Groups promotion to other devices and QR code tokens synchronisation are not synchronised processes, so there are reasons why a QR code token may arrive earlier than the first group message: - We are going to upload sync messages via IMAP while group messages are sent by SMTP. - If sync messages go to the mvbox, they can be fetched earlier than group messages from Inbox.
117 lines
3.1 KiB
Rust
117 lines
3.1 KiB
Rust
//! # Token module.
|
|
//!
|
|
//! Functions to read/write token from/to the database. A token is any string associated with a key.
|
|
//!
|
|
//! Tokens are used in SecureJoin verification protocols.
|
|
|
|
use anyhow::Result;
|
|
use deltachat_derive::{FromSql, ToSql};
|
|
|
|
use crate::context::Context;
|
|
use crate::tools::{create_id, time};
|
|
|
|
/// Token namespace
|
|
#[derive(
|
|
Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
|
)]
|
|
#[repr(u32)]
|
|
pub enum Namespace {
|
|
#[default]
|
|
Unknown = 0,
|
|
Auth = 110,
|
|
InviteNumber = 100,
|
|
}
|
|
|
|
/// Saves a token to the database.
|
|
pub async fn save(
|
|
context: &Context,
|
|
namespace: Namespace,
|
|
foreign_key: Option<&str>,
|
|
token: &str,
|
|
) -> Result<()> {
|
|
context
|
|
.sql
|
|
.execute(
|
|
"INSERT INTO tokens (namespc, foreign_key, token, timestamp) VALUES (?, ?, ?, ?)",
|
|
(namespace, foreign_key.unwrap_or(""), token, time()),
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Looks up most recently created token for a namespace / foreign key combination.
|
|
///
|
|
/// As there may be more than one such valid token,
|
|
/// (eg. when a qr code token is withdrawn, recreated and revived later),
|
|
/// use lookup() for qr-code creation only;
|
|
/// do not use lookup() to check for token validity.
|
|
///
|
|
/// To check if a given token is valid, use exists().
|
|
pub async fn lookup(
|
|
context: &Context,
|
|
namespace: Namespace,
|
|
foreign_key: Option<&str>,
|
|
) -> Result<Option<String>> {
|
|
context
|
|
.sql
|
|
.query_get_value(
|
|
"SELECT token FROM tokens WHERE namespc=? AND foreign_key=? ORDER BY timestamp DESC LIMIT 1",
|
|
(namespace, foreign_key.unwrap_or("")),
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn lookup_or_new(
|
|
context: &Context,
|
|
namespace: Namespace,
|
|
foreign_key: Option<&str>,
|
|
) -> Result<String> {
|
|
if let Some(token) = lookup(context, namespace, foreign_key).await? {
|
|
return Ok(token);
|
|
}
|
|
|
|
let token = create_id();
|
|
save(context, namespace, foreign_key, &token).await?;
|
|
Ok(token)
|
|
}
|
|
|
|
pub async fn exists(context: &Context, namespace: Namespace, token: &str) -> Result<bool> {
|
|
let exists = context
|
|
.sql
|
|
.exists(
|
|
"SELECT COUNT(*) FROM tokens WHERE namespc=? AND token=?;",
|
|
(namespace, token),
|
|
)
|
|
.await?;
|
|
Ok(exists)
|
|
}
|
|
|
|
/// Looks up foreign key by auth token.
|
|
///
|
|
/// Returns None if auth token is not valid.
|
|
/// Returns an empty string if the token corresponds to "setup contact" rather than group join.
|
|
pub async fn auth_foreign_key(context: &Context, token: &str) -> Result<Option<String>> {
|
|
context
|
|
.sql
|
|
.query_row_optional(
|
|
"SELECT foreign_key FROM tokens WHERE namespc=? AND token=?",
|
|
(Namespace::Auth, token),
|
|
|row| {
|
|
let foreign_key: String = row.get(0)?;
|
|
Ok(foreign_key)
|
|
},
|
|
)
|
|
.await
|
|
}
|
|
|
|
pub async fn delete(context: &Context, namespace: Namespace, token: &str) -> Result<()> {
|
|
context
|
|
.sql
|
|
.execute(
|
|
"DELETE FROM tokens WHERE namespc=? AND token=?;",
|
|
(namespace, token),
|
|
)
|
|
.await?;
|
|
Ok(())
|
|
}
|