api-tokens

This commit is contained in:
2025-11-01 21:42:52 +01:00
parent f6ceff5444
commit 00f5d3adea
16 changed files with 999 additions and 556 deletions
+72 -5
View File
@@ -4,14 +4,18 @@ use diesel::pg::{Pg, PgValue};
use diesel::prelude::*;
use diesel::serialize::{IsNull, Output, ToSql};
use diesel::{deserialize, serialize, AsExpression, FromSqlRow};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt;
use std::io::Write;
use std::str;
use uuid::Uuid;
use utoipa::ToSchema;
use crate::schema::sql_types::{
MagicTokenKind as MagicTokenKindSql, TenantStatus as TenantStatusSql,
ApiTokenCapability as ApiTokenCapabilitySql, MagicTokenKind as MagicTokenKindSql,
TenantStatus as TenantStatusSql,
};
use crate::schema::*;
@@ -52,6 +56,16 @@ pub enum MagicTokenKind {
DemoLogin,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow, Serialize, Deserialize, ToSchema,
)]
#[diesel(sql_type = ApiTokenCapabilitySql)]
#[serde(rename_all = "snake_case")]
pub enum ApiTokenCapability {
Api,
Webdav,
}
impl MagicTokenKind {
pub fn as_str(&self) -> &'static str {
match self {
@@ -65,12 +79,31 @@ impl MagicTokenKind {
}
}
impl ApiTokenCapability {
pub fn as_str(&self) -> &'static str {
match self {
ApiTokenCapability::Api => "api",
ApiTokenCapability::Webdav => "webdav",
}
}
pub fn variants() -> &'static [&'static str] {
&["api", "webdav"]
}
}
impl fmt::Display for MagicTokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl fmt::Display for ApiTokenCapability {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl ToSql<MagicTokenKindSql, Pg> for MagicTokenKind {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
out.write_all(self.as_str().as_bytes())?;
@@ -78,6 +111,13 @@ impl ToSql<MagicTokenKindSql, Pg> for MagicTokenKind {
}
}
impl ToSql<ApiTokenCapabilitySql, Pg> for ApiTokenCapability {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
out.write_all(self.as_str().as_bytes())?;
Ok(IsNull::No)
}
}
impl FromSql<MagicTokenKindSql, Pg> for MagicTokenKind {
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
match std::str::from_utf8(bytes.as_bytes())? {
@@ -91,6 +131,19 @@ impl FromSql<MagicTokenKindSql, Pg> for MagicTokenKind {
}
}
impl FromSql<ApiTokenCapabilitySql, Pg> for ApiTokenCapability {
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
match std::str::from_utf8(bytes.as_bytes())? {
"api" => Ok(ApiTokenCapability::Api),
"webdav" => Ok(ApiTokenCapability::Webdav),
other => Err(Box::new(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid api_token_capability '{other}'"),
))),
}
}
}
impl str::FromStr for MagicTokenKind {
type Err = &'static str;
@@ -103,6 +156,18 @@ impl str::FromStr for MagicTokenKind {
}
}
impl str::FromStr for ApiTokenCapability {
type Err = &'static str;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"api" => Ok(ApiTokenCapability::Api),
"webdav" => Ok(ApiTokenCapability::Webdav),
_ => Err("unsupported api token capability"),
}
}
}
impl TenantStatus {
pub fn as_str(&self) -> &'static str {
match self {
@@ -243,10 +308,10 @@ pub struct NewWebauthnChallenge {
}
#[derive(Debug, Clone, Queryable, Identifiable, Associations)]
#[diesel(table_name = webdav_tokens)]
#[diesel(table_name = api_tokens)]
#[diesel(belongs_to(User))]
#[diesel(belongs_to(Tenant))]
pub struct WebdavToken {
pub struct ApiToken {
pub id: Uuid,
pub user_id: Uuid,
pub tenant_id: Uuid,
@@ -257,11 +322,12 @@ pub struct WebdavToken {
pub last_used_at: Option<NaiveDateTime>,
pub expires_at: Option<NaiveDateTime>,
pub revoked_at: Option<NaiveDateTime>,
pub capabilities: Vec<ApiTokenCapability>,
}
#[derive(Debug, Insertable)]
#[diesel(table_name = webdav_tokens)]
pub struct NewWebdavToken {
#[diesel(table_name = api_tokens)]
pub struct NewApiToken {
pub id: Uuid,
pub user_id: Uuid,
pub tenant_id: Uuid,
@@ -269,6 +335,7 @@ pub struct NewWebdavToken {
pub token_hash: String,
pub label: Option<String>,
pub expires_at: Option<NaiveDateTime>,
pub capabilities: Vec<ApiTokenCapability>,
}
#[derive(Debug, Clone, Queryable, Identifiable)]