mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-08-21 16:56:08 +00:00
fix: move code to more correct crates
This commit is contained in:
Generated
+142
-19
@@ -6092,9 +6092,57 @@ dependencies = [
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-api"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"backon",
|
||||
"base32",
|
||||
"base64 0.22.1",
|
||||
"bcrypt",
|
||||
"bs58",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"cid",
|
||||
"ed25519-dalek",
|
||||
"futures",
|
||||
"hex",
|
||||
"http 1.4.0",
|
||||
"infer",
|
||||
"ipld-core",
|
||||
"jacquard-common",
|
||||
"jacquard-repo",
|
||||
"k256",
|
||||
"multibase",
|
||||
"multihash",
|
||||
"rand 0.8.5",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_ipld_dagcbor",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tranquil-config",
|
||||
"tranquil-db",
|
||||
"tranquil-db-traits",
|
||||
"tranquil-lexicon",
|
||||
"tranquil-pds",
|
||||
"tranquil-scopes",
|
||||
"tranquil-types",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"webauthn-rs",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-auth"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base32",
|
||||
@@ -6117,7 +6165,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-cache"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -6131,7 +6179,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-comms"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -6146,7 +6194,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-config"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"confique",
|
||||
"serde",
|
||||
@@ -6154,7 +6202,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-crypto"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64 0.22.1",
|
||||
@@ -6170,7 +6218,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-db"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -6187,7 +6235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-db-traits"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -6203,7 +6251,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-infra"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -6214,7 +6262,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-lexicon"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"hickory-resolver",
|
||||
@@ -6232,7 +6280,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-oauth"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -6253,9 +6301,42 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-oauth-server"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"cid",
|
||||
"hmac",
|
||||
"http 1.4.0",
|
||||
"jacquard-common",
|
||||
"jacquard-repo",
|
||||
"k256",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tranquil-api",
|
||||
"tranquil-config",
|
||||
"tranquil-crypto",
|
||||
"tranquil-db-traits",
|
||||
"tranquil-pds",
|
||||
"tranquil-types",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
"webauthn-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-pds"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
@@ -6272,9 +6353,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"ciborium",
|
||||
"cid",
|
||||
"clap",
|
||||
"ctor",
|
||||
"dotenvy",
|
||||
"ed25519-dalek",
|
||||
"futures",
|
||||
"futures-util",
|
||||
@@ -6319,7 +6398,7 @@ dependencies = [
|
||||
"tower-http",
|
||||
"tower-layer",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tranquil-api",
|
||||
"tranquil-auth",
|
||||
"tranquil-cache",
|
||||
"tranquil-comms",
|
||||
@@ -6329,10 +6408,12 @@ dependencies = [
|
||||
"tranquil-db-traits",
|
||||
"tranquil-lexicon",
|
||||
"tranquil-oauth",
|
||||
"tranquil-oauth-server",
|
||||
"tranquil-repo",
|
||||
"tranquil-ripple",
|
||||
"tranquil-scopes",
|
||||
"tranquil-storage",
|
||||
"tranquil-sync",
|
||||
"tranquil-types",
|
||||
"urlencoding",
|
||||
"uuid",
|
||||
@@ -6343,7 +6424,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-repo"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cid",
|
||||
@@ -6355,7 +6436,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-ripple"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"backon",
|
||||
@@ -6380,7 +6461,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-scopes"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"futures",
|
||||
@@ -6394,9 +6475,29 @@ dependencies = [
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-server"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"clap",
|
||||
"dotenvy",
|
||||
"ed25519-dalek",
|
||||
"hex",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tranquil-api",
|
||||
"tranquil-config",
|
||||
"tranquil-oauth-server",
|
||||
"tranquil-pds",
|
||||
"tranquil-sync",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-storage"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"aws-config",
|
||||
@@ -6411,9 +6512,31 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-sync"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"cid",
|
||||
"futures",
|
||||
"ipld-core",
|
||||
"jacquard-repo",
|
||||
"serde",
|
||||
"serde_ipld_dagcbor",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tranquil-config",
|
||||
"tranquil-db-traits",
|
||||
"tranquil-pds",
|
||||
"tranquil-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tranquil-types"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"cid",
|
||||
|
||||
+10
-1
@@ -16,11 +16,15 @@ members = [
|
||||
"crates/tranquil-db-traits",
|
||||
"crates/tranquil-db",
|
||||
"crates/tranquil-pds",
|
||||
"crates/tranquil-server",
|
||||
"crates/tranquil-sync",
|
||||
"crates/tranquil-oauth-server",
|
||||
"crates/tranquil-api",
|
||||
"crates/tranquil-lexicon",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
edition = "2024"
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
@@ -40,6 +44,11 @@ tranquil-db-traits = { path = "crates/tranquil-db-traits" }
|
||||
tranquil-db = { path = "crates/tranquil-db" }
|
||||
tranquil-ripple = { path = "crates/tranquil-ripple" }
|
||||
tranquil-lexicon = { path = "crates/tranquil-lexicon" }
|
||||
tranquil-pds = { path = "crates/tranquil-pds" }
|
||||
tranquil-server = { path = "crates/tranquil-server" }
|
||||
tranquil-sync = { path = "crates/tranquil-sync" }
|
||||
tranquil-oauth-server = { path = "crates/tranquil-oauth-server" }
|
||||
tranquil-api = { path = "crates/tranquil-api" }
|
||||
|
||||
unicode-segmentation = "1"
|
||||
|
||||
|
||||
+7
-3
@@ -25,15 +25,19 @@ COPY crates/tranquil-ripple ./crates/tranquil-ripple
|
||||
COPY crates/tranquil-storage ./crates/tranquil-storage
|
||||
COPY crates/tranquil-cache ./crates/tranquil-cache
|
||||
COPY crates/tranquil-pds ./crates/tranquil-pds
|
||||
COPY crates/tranquil-sync ./crates/tranquil-sync
|
||||
COPY crates/tranquil-api ./crates/tranquil-api
|
||||
COPY crates/tranquil-oauth-server ./crates/tranquil-oauth-server
|
||||
COPY crates/tranquil-server ./crates/tranquil-server
|
||||
COPY migrations ./crates/tranquil-pds/migrations
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/app/target \
|
||||
if [ "$SLIM" = "true" ]; then \
|
||||
SQLX_OFFLINE=true cargo build --release -p tranquil-pds --no-default-features; \
|
||||
SQLX_OFFLINE=true cargo build --release -p tranquil-server --no-default-features; \
|
||||
else \
|
||||
SQLX_OFFLINE=true cargo build --release -p tranquil-pds; \
|
||||
SQLX_OFFLINE=true cargo build --release -p tranquil-server; \
|
||||
fi && \
|
||||
cp target/release/tranquil-pds /tmp/tranquil-pds
|
||||
cp target/release/tranquil-server /tmp/tranquil-pds
|
||||
|
||||
FROM alpine:3.23 AS signal-cli
|
||||
RUN apk add --no-cache curl tar
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
[package]
|
||||
name = "tranquil-api"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tranquil-pds = { workspace = true }
|
||||
tranquil-types = { workspace = true }
|
||||
tranquil-config = { workspace = true }
|
||||
tranquil-db = { workspace = true }
|
||||
tranquil-db-traits = { workspace = true }
|
||||
tranquil-lexicon = { workspace = true, features = ["resolve"] }
|
||||
tranquil-scopes = { workspace = true }
|
||||
|
||||
anyhow = { workspace = true }
|
||||
axum = { workspace = true }
|
||||
backon = { workspace = true }
|
||||
base32 = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
bs58 = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
cid = { workspace = true }
|
||||
ed25519-dalek = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
http = { workspace = true }
|
||||
infer = { workspace = true }
|
||||
ipld-core = { workspace = true }
|
||||
jacquard-common = { workspace = true }
|
||||
jacquard-repo = { workspace = true }
|
||||
k256 = { workspace = true }
|
||||
multibase = { workspace = true }
|
||||
multihash = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde_ipld_dagcbor = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
subtle = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
webauthn-rs = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::{Auth, NotTakendown, Permissive};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Auth, NotTakendown, Permissive};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
+7
-7
@@ -1,8 +1,8 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -36,7 +36,7 @@ pub async fn delete_account(
|
||||
.await
|
||||
.log_db_err("deleting account")?;
|
||||
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
did,
|
||||
tranquil_db_traits::AccountStatus::Deleted,
|
||||
@@ -50,7 +50,7 @@ pub async fn delete_account(
|
||||
}
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
Ok(EmptyResponse::ok().into_response())
|
||||
}
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle};
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, RawQuery, State},
|
||||
@@ -196,7 +196,7 @@ pub async fn get_account_infos(
|
||||
_auth: Auth<Admin>,
|
||||
RawQuery(raw_query): RawQuery,
|
||||
) -> Result<Response, ApiError> {
|
||||
let dids: Vec<String> = crate::util::parse_repeated_query_param(raw_query.as_deref(), "dids")
|
||||
let dids: Vec<String> = tranquil_pds::util::parse_repeated_query_param(raw_query.as_deref(), "dids")
|
||||
.into_iter()
|
||||
.filter(|d| !d.is_empty())
|
||||
.collect();
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle};
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
+9
-9
@@ -1,8 +1,8 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle, PlainPassword};
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle, PlainPassword};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -101,14 +101,14 @@ pub async fn update_account_handle(
|
||||
if let Some(old) = old_handle {
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&old))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&old))
|
||||
.await;
|
||||
}
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
if let Err(e) = crate::api::repo::record::sequence_identity_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
|
||||
&state,
|
||||
did,
|
||||
Some(&handle_for_check),
|
||||
@@ -121,7 +121,7 @@ pub async fn update_account_handle(
|
||||
);
|
||||
}
|
||||
if let Err(e) =
|
||||
crate::api::identity::did::update_plc_handle(&state, did, &handle_for_check).await
|
||||
crate::identity::did::update_plc_handle(&state, did, &handle_for_check).await
|
||||
{
|
||||
warn!("Failed to update PLC handle for admin handle update: {}", e);
|
||||
}
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{Json, extract::State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, warn};
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::{Admin, Auth};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{CidLink, Did};
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Admin, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{CidLink, Did};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
@@ -215,7 +215,7 @@ pub async fn update_subject_status(
|
||||
tranquil_db_traits::AccountStatus::Active
|
||||
};
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_account_event(&state, &did, status).await
|
||||
tranquil_pds::repo_ops::sequence_account_event(&state, &did, status).await
|
||||
{
|
||||
warn!("Failed to sequence account event for takedown: {}", e);
|
||||
}
|
||||
@@ -227,7 +227,7 @@ pub async fn update_subject_status(
|
||||
tranquil_db_traits::AccountStatus::Active
|
||||
};
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_account_event(&state, &did, status).await
|
||||
tranquil_pds::repo_ops::sequence_account_event(&state, &did, status).await
|
||||
{
|
||||
warn!("Failed to sequence account event for deactivation: {}", e);
|
||||
}
|
||||
@@ -235,7 +235,7 @@ pub async fn update_subject_status(
|
||||
if let Ok(Some(handle)) = state.user_repo.get_handle_by_did(&did).await {
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
}
|
||||
return Ok((
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
use crate::auth::{AccountRequirement, extract_auth_token_from_header, validate_token_with_dpop};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::auth::{AccountRequirement, extract_auth_token_from_header, validate_token_with_dpop};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -33,13 +33,13 @@ pub async fn get_age_assurance_state() -> Response {
|
||||
}
|
||||
|
||||
async fn get_account_created_at(state: &AppState, headers: &HeaderMap) -> Option<String> {
|
||||
let auth_header = crate::util::get_header_str(headers, http::header::AUTHORIZATION);
|
||||
let auth_header = tranquil_pds::util::get_header_str(headers, http::header::AUTHORIZATION);
|
||||
tracing::debug!(?auth_header, "age assurance: extracting token");
|
||||
|
||||
let extracted = extract_auth_token_from_header(auth_header)?;
|
||||
tracing::debug!("age assurance: got token, validating");
|
||||
|
||||
let dpop_proof = crate::util::get_header_str(headers, crate::util::HEADER_DPOP);
|
||||
let dpop_proof = tranquil_pds::util::get_header_str(headers, tranquil_pds::util::HEADER_DPOP);
|
||||
let http_uri = "/";
|
||||
|
||||
let auth_user = match validate_token_with_dpop(
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::api::{EmptyResponse, EnabledResponse};
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::scheduled::generate_full_backup;
|
||||
use crate::state::AppState;
|
||||
use crate::storage::{BackupStorage, backup_retention_count};
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::api::{EmptyResponse, EnabledResponse};
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::scheduled::generate_full_backup;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::storage::{BackupStorage, backup_retention_count};
|
||||
use anyhow::Context;
|
||||
use axum::{
|
||||
Json,
|
||||
@@ -39,7 +39,7 @@ pub struct ListBackupsOutput {
|
||||
pub async fn list_backups(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let (user_id, backup_enabled) = match state.backup_repo.get_user_backup_status(&auth.did).await
|
||||
{
|
||||
Ok(Some(status)) => status,
|
||||
@@ -91,7 +91,7 @@ pub async fn get_backup(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
Query(query): Query<GetBackupQuery>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let backup_id = match uuid::Uuid::parse_str(&query.id) {
|
||||
Ok(id) => id,
|
||||
Err(_) => {
|
||||
@@ -157,7 +157,7 @@ pub struct CreateBackupOutput {
|
||||
pub async fn create_backup(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let backup_storage = match state.backup_storage.as_ref() {
|
||||
Some(storage) => storage,
|
||||
None => {
|
||||
@@ -213,7 +213,7 @@ pub async fn create_backup(
|
||||
}
|
||||
};
|
||||
|
||||
let block_count = crate::scheduled::count_car_blocks(&car_bytes);
|
||||
let block_count = tranquil_pds::scheduled::count_car_blocks(&car_bytes);
|
||||
let size_bytes = i64::try_from(car_bytes.len()).unwrap_or(i64::MAX);
|
||||
|
||||
let storage_key = match backup_storage
|
||||
@@ -327,7 +327,7 @@ pub async fn delete_backup(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
Query(query): Query<DeleteBackupQuery>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let backup_id = match uuid::Uuid::parse_str(&query.id) {
|
||||
Ok(id) => id,
|
||||
Err(_) => {
|
||||
@@ -384,7 +384,7 @@ pub async fn set_backup_enabled(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
Json(input): Json<SetBackupEnabledInput>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let deactivated_at = match state
|
||||
.backup_repo
|
||||
.get_user_deactivated_status(&auth.did)
|
||||
@@ -423,7 +423,7 @@ pub async fn set_backup_enabled(
|
||||
pub async fn export_blobs(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let user_id = match state.backup_repo.get_user_id_by_did(&auth.did).await {
|
||||
Ok(Some(id)) => id,
|
||||
Ok(None) => {
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::api::repo::record::utils::create_signed_commit;
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::delegation::{
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::repo_ops::create_signed_commit;
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::delegation::{
|
||||
DelegationActionType, SCOPE_PRESETS, ValidatedDelegationScope, verify_can_add_controllers,
|
||||
verify_can_be_controller, verify_can_control_accounts,
|
||||
};
|
||||
use crate::rate_limit::{AccountCreationLimit, RateLimited};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle};
|
||||
use tranquil_pds::rate_limit::{AccountCreationLimit, RateLimited};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
@@ -449,7 +449,7 @@ pub async fn create_delegated_account(
|
||||
.unwrap_or(&input.handle),
|
||||
None => &input.handle,
|
||||
};
|
||||
match crate::api::validation::validate_short_handle(handle_to_validate) {
|
||||
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
|
||||
Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])),
|
||||
Err(e) => {
|
||||
return Ok(ApiError::InvalidRequest(e.to_string()).into_response());
|
||||
@@ -465,7 +465,7 @@ pub async fn create_delegated_account(
|
||||
.map(|e| e.trim().to_string())
|
||||
.filter(|e| !e.is_empty());
|
||||
if let Some(ref email) = email
|
||||
&& !crate::api::validation::is_valid_email(email)
|
||||
&& !tranquil_pds::api::validation::is_valid_email(email)
|
||||
{
|
||||
return Ok(ApiError::InvalidEmail.into_response());
|
||||
}
|
||||
@@ -502,9 +502,9 @@ pub async fn create_delegated_account(
|
||||
.secrets
|
||||
.plc_rotation_key
|
||||
.clone()
|
||||
.unwrap_or_else(|| crate::plc::signing_key_to_did_key(&signing_key));
|
||||
.unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&signing_key));
|
||||
|
||||
let genesis_result = match crate::plc::create_genesis_operation(
|
||||
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
|
||||
&signing_key,
|
||||
&rotation_key,
|
||||
&handle,
|
||||
@@ -520,7 +520,7 @@ pub async fn create_delegated_account(
|
||||
}
|
||||
};
|
||||
|
||||
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
if let Err(e) = plc_client
|
||||
.send_operation(&genesis_result.did, &genesis_result.signed_operation)
|
||||
.await
|
||||
@@ -540,7 +540,7 @@ pub async fn create_delegated_account(
|
||||
let handle: Handle = handle.parse().map_err(|_| ApiError::InvalidHandle(None))?;
|
||||
info!(did = %did, handle = %handle, controller = %can_control.did(), "Created DID for delegated account");
|
||||
|
||||
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
|
||||
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => {
|
||||
error!("Error encrypting signing key: {:?}", e);
|
||||
@@ -581,7 +581,7 @@ pub async fn create_delegated_account(
|
||||
controller_did: can_control.did().clone(),
|
||||
controller_scopes: input.controller_scopes.as_str().to_string(),
|
||||
encrypted_key_bytes,
|
||||
encryption_version: crate::config::ENCRYPTION_VERSION,
|
||||
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
|
||||
commit_cid: commit_cid.to_string(),
|
||||
repo_rev: rev.as_ref().to_string(),
|
||||
genesis_block_cids,
|
||||
@@ -616,11 +616,11 @@ pub async fn create_delegated_account(
|
||||
}
|
||||
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle)).await
|
||||
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle)).await
|
||||
{
|
||||
warn!("Failed to sequence identity event for {}: {}", did, e);
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
&did,
|
||||
tranquil_db_traits::AccountStatus::Active,
|
||||
@@ -634,11 +634,11 @@ pub async fn create_delegated_account(
|
||||
"$type": "app.bsky.actor.profile",
|
||||
"displayName": handle
|
||||
});
|
||||
if let Err(e) = crate::api::repo::record::create_record_internal(
|
||||
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
|
||||
&state,
|
||||
&did,
|
||||
&crate::types::PROFILE_COLLECTION,
|
||||
&crate::types::PROFILE_RKEY,
|
||||
&tranquil_pds::types::PROFILE_COLLECTION,
|
||||
&tranquil_pds::types::PROFILE_RKEY,
|
||||
&profile_record,
|
||||
)
|
||||
.await
|
||||
+3
-3
@@ -10,9 +10,9 @@ use serde_json::json;
|
||||
use tracing::{debug, info, warn};
|
||||
use tranquil_types::Handle;
|
||||
|
||||
use crate::comms::comms_repo;
|
||||
use crate::state::AppState;
|
||||
use crate::util::discord_public_key;
|
||||
use tranquil_pds::comms::comms_repo;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::util::discord_public_key;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Interaction {
|
||||
+38
-38
@@ -1,12 +1,12 @@
|
||||
use super::did::verify_did_web;
|
||||
use crate::api::error::ApiError;
|
||||
use crate::api::repo::record::utils::create_signed_commit;
|
||||
use crate::auth::{ServiceTokenVerifier, extract_auth_token_from_header, is_service_token};
|
||||
use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key};
|
||||
use crate::rate_limit::{AccountCreationLimit, RateLimited};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle, PlainPassword};
|
||||
use crate::validation::validate_password;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::repo_ops::create_signed_commit;
|
||||
use tranquil_pds::auth::{ServiceTokenVerifier, extract_auth_token_from_header, is_service_token};
|
||||
use tranquil_pds::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key};
|
||||
use tranquil_pds::rate_limit::{AccountCreationLimit, RateLimited};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle, PlainPassword};
|
||||
use tranquil_pds::validation::validate_password;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -73,7 +73,7 @@ pub async fn create_account(
|
||||
}
|
||||
|
||||
let migration_auth = if let Some(extracted) = extract_auth_token_from_header(
|
||||
crate::util::get_header_str(&headers, http::header::AUTHORIZATION),
|
||||
tranquil_pds::util::get_header_str(&headers, http::header::AUTHORIZATION),
|
||||
) {
|
||||
let token = extracted.token;
|
||||
if is_service_token(&token) {
|
||||
@@ -155,14 +155,14 @@ pub async fn create_account(
|
||||
.unwrap_or(&input.handle),
|
||||
None => &input.handle,
|
||||
};
|
||||
match crate::api::validation::validate_short_handle(handle_to_validate) {
|
||||
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
|
||||
Ok(h) => h,
|
||||
Err(e) => {
|
||||
return ApiError::from(e).into_response();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match crate::api::validation::validate_full_domain_handle(&input.handle) {
|
||||
match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) {
|
||||
Ok(h) => h,
|
||||
Err(e) => return ApiError::from(e).into_response(),
|
||||
}
|
||||
@@ -173,7 +173,7 @@ pub async fn create_account(
|
||||
.map(|e| e.trim().to_string())
|
||||
.filter(|e| !e.is_empty());
|
||||
if let Some(ref email) = email
|
||||
&& !crate::api::validation::is_valid_email(email)
|
||||
&& !tranquil_pds::api::validation::is_valid_email(email)
|
||||
{
|
||||
return ApiError::InvalidEmail.into_response();
|
||||
}
|
||||
@@ -191,7 +191,7 @@ pub async fn create_account(
|
||||
tranquil_db_traits::CommsChannel::Discord => match &input.discord_username {
|
||||
Some(username) if !username.trim().is_empty() => {
|
||||
let clean = username.trim().to_lowercase();
|
||||
if !crate::api::validation::is_valid_discord_username(&clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_discord_username(&clean) {
|
||||
return ApiError::InvalidRequest(
|
||||
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(),
|
||||
).into_response();
|
||||
@@ -203,7 +203,7 @@ pub async fn create_account(
|
||||
tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username {
|
||||
Some(username) if !username.trim().is_empty() => {
|
||||
let clean = username.trim().trim_start_matches('@');
|
||||
if !crate::api::validation::is_valid_telegram_username(clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_telegram_username(clean) {
|
||||
return ApiError::InvalidRequest(
|
||||
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(),
|
||||
).into_response();
|
||||
@@ -257,7 +257,7 @@ pub async fn create_account(
|
||||
let did_type = input.did_type.as_deref().unwrap_or("plc");
|
||||
let did = match did_type {
|
||||
"web" => {
|
||||
if !crate::api::server::meta::is_self_hosted_did_web_enabled() {
|
||||
if !tranquil_pds::util::is_self_hosted_did_web_enabled() {
|
||||
return ApiError::SelfHostedDidWebDisabled.into_response();
|
||||
}
|
||||
let encoded_handle = handle.replace(':', "%3A");
|
||||
@@ -408,7 +408,7 @@ pub async fn create_account(
|
||||
.await
|
||||
{
|
||||
Ok(Some(key_info)) => {
|
||||
match crate::config::decrypt_key(
|
||||
match tranquil_pds::config::decrypt_key(
|
||||
&key_info.key_bytes,
|
||||
key_info.encryption_version,
|
||||
) {
|
||||
@@ -428,14 +428,14 @@ pub async fn create_account(
|
||||
}
|
||||
};
|
||||
let access_meta =
|
||||
match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
|
||||
match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("Error creating access token: {:?}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let refresh_meta = match crate::auth::create_refresh_token_with_metadata(
|
||||
let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(
|
||||
&did,
|
||||
&secret_key_bytes,
|
||||
) {
|
||||
@@ -463,12 +463,12 @@ pub async fn create_account(
|
||||
}
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
let verification_required = if let Some(ref user_email) = email {
|
||||
let token = crate::auth::verification_token::generate_migration_token(
|
||||
let token = tranquil_pds::auth::verification_token::generate_migration_token(
|
||||
&did_typed, user_email,
|
||||
);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&token);
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification(
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&token);
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
reactivated.user_id,
|
||||
@@ -590,7 +590,7 @@ pub async fn create_account(
|
||||
None
|
||||
};
|
||||
|
||||
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
|
||||
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
|
||||
Ok(enc) => enc,
|
||||
Err(e) => {
|
||||
error!("Error encrypting user key: {:?}", e);
|
||||
@@ -666,7 +666,7 @@ pub async fn create_account(
|
||||
.map(|s| s.to_lowercase()),
|
||||
deactivated_at,
|
||||
encrypted_key_bytes,
|
||||
encryption_version: crate::config::ENCRYPTION_VERSION,
|
||||
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
|
||||
reserved_key_id,
|
||||
commit_cid: commit_cid_str.clone(),
|
||||
repo_rev: rev_str.clone(),
|
||||
@@ -697,7 +697,7 @@ pub async fn create_account(
|
||||
};
|
||||
let user_id = create_result.user_id;
|
||||
if !is_migration && !is_did_web_byod {
|
||||
if let Err(e) = crate::api::repo::record::sequence_identity_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
|
||||
&state,
|
||||
&did_for_commit,
|
||||
Some(&handle_typed),
|
||||
@@ -706,7 +706,7 @@ pub async fn create_account(
|
||||
{
|
||||
warn!("Failed to sequence identity event for {}: {}", did, e);
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
&did_for_commit,
|
||||
tranquil_db_traits::AccountStatus::Active,
|
||||
@@ -715,7 +715,7 @@ pub async fn create_account(
|
||||
{
|
||||
warn!("Failed to sequence account event for {}: {}", did, e);
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_genesis_commit(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_genesis_commit(
|
||||
&state,
|
||||
&did_for_commit,
|
||||
&commit_cid,
|
||||
@@ -726,7 +726,7 @@ pub async fn create_account(
|
||||
{
|
||||
warn!("Failed to sequence commit event for {}: {}", did, e);
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_sync_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_sync_event(
|
||||
&state,
|
||||
&did_for_commit,
|
||||
&commit_cid_str,
|
||||
@@ -740,11 +740,11 @@ pub async fn create_account(
|
||||
"$type": "app.bsky.actor.profile",
|
||||
"displayName": input.handle
|
||||
});
|
||||
if let Err(e) = crate::api::repo::record::create_record_internal(
|
||||
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
|
||||
&state,
|
||||
&did_for_commit,
|
||||
&crate::types::PROFILE_COLLECTION,
|
||||
&crate::types::PROFILE_RKEY,
|
||||
&tranquil_pds::types::PROFILE_COLLECTION,
|
||||
&tranquil_pds::types::PROFILE_RKEY,
|
||||
&profile_record,
|
||||
)
|
||||
.await
|
||||
@@ -755,14 +755,14 @@ pub async fn create_account(
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if !is_migration {
|
||||
if let Some(ref recipient) = verification_recipient {
|
||||
let verification_token = crate::auth::verification_token::generate_signup_token(
|
||||
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
|
||||
&did_for_commit,
|
||||
verification_channel,
|
||||
recipient,
|
||||
);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&verification_token);
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
@@ -781,9 +781,9 @@ pub async fn create_account(
|
||||
}
|
||||
} else if let Some(ref user_email) = email {
|
||||
let token =
|
||||
crate::auth::verification_token::generate_migration_token(&did_for_commit, user_email);
|
||||
let formatted_token = crate::auth::verification_token::format_token_for_display(&token);
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification(
|
||||
tranquil_pds::auth::verification_token::generate_migration_token(&did_for_commit, user_email);
|
||||
let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token);
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
@@ -797,7 +797,7 @@ pub async fn create_account(
|
||||
}
|
||||
}
|
||||
|
||||
let access_meta = match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes)
|
||||
let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes)
|
||||
{
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
@@ -806,7 +806,7 @@ pub async fn create_account(
|
||||
}
|
||||
};
|
||||
let refresh_meta =
|
||||
match crate::auth::create_refresh_token_with_metadata(&did, &secret_key_bytes) {
|
||||
match tranquil_pds::auth::create_refresh_token_with_metadata(&did, &secret_key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("createAccount: Error creating refresh token: {:?}", e);
|
||||
+37
-37
@@ -1,12 +1,12 @@
|
||||
use crate::api::{ApiError, DidResponse, EmptyResponse};
|
||||
use crate::auth::{Auth, NotTakendown};
|
||||
use crate::plc::signing_key_to_did_key;
|
||||
use crate::rate_limit::{
|
||||
use tranquil_pds::api::{ApiError, DidResponse, EmptyResponse};
|
||||
use tranquil_pds::auth::{Auth, NotTakendown};
|
||||
use tranquil_pds::plc::signing_key_to_did_key;
|
||||
use tranquil_pds::rate_limit::{
|
||||
HandleUpdateDailyLimit, HandleUpdateLimit, check_user_rate_limit_with_message,
|
||||
};
|
||||
use crate::state::AppState;
|
||||
use crate::types::Handle;
|
||||
use crate::util::get_header_str;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::Handle;
|
||||
use tranquil_pds::util::get_header_str;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, Query, State},
|
||||
@@ -42,7 +42,7 @@ pub async fn resolve_handle(
|
||||
if handle_str.is_empty() {
|
||||
return ApiError::InvalidRequest("handle is required".into()).into_response();
|
||||
}
|
||||
let cache_key = crate::cache_keys::handle_key(handle_str);
|
||||
let cache_key = tranquil_pds::cache_keys::handle_key(handle_str);
|
||||
if let Some(did) = state.cache.get(&cache_key).await {
|
||||
return DidResponse::response(did).into_response();
|
||||
}
|
||||
@@ -61,7 +61,7 @@ pub async fn resolve_handle(
|
||||
.await;
|
||||
DidResponse::response(row.did).into_response()
|
||||
}
|
||||
Ok(None) => match crate::handle::resolve_handle(handle.as_str()).await {
|
||||
Ok(None) => match tranquil_pds::handle::resolve_handle(handle.as_str()).await {
|
||||
Ok(did) => {
|
||||
let _ = state
|
||||
.cache
|
||||
@@ -148,7 +148,7 @@ pub async fn well_known_did(State(state): State<AppState>, headers: HeaderMap) -
|
||||
"id": did,
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": format!("https://{}", hostname)
|
||||
}]
|
||||
}))
|
||||
@@ -158,7 +158,7 @@ pub async fn well_known_did(State(state): State<AppState>, headers: HeaderMap) -
|
||||
async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) -> Response {
|
||||
let encoded_handle = handle.replace(':', "%3A");
|
||||
let expected_did = format!("did:web:{}", encoded_handle);
|
||||
let expected_did_typed: crate::types::Did = match expected_did.parse() {
|
||||
let expected_did_typed: tranquil_pds::types::Did = match expected_did.parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
|
||||
};
|
||||
@@ -216,7 +216,7 @@ async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) ->
|
||||
})).collect::<Vec<_>>(),
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
@@ -229,7 +229,7 @@ async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) ->
|
||||
Err(_) => return ApiError::InternalError(None).into_response(),
|
||||
};
|
||||
let key_bytes: Vec<u8> =
|
||||
match crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
|
||||
match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
|
||||
Ok(k) => k,
|
||||
Err(_) => {
|
||||
return ApiError::InternalError(None).into_response();
|
||||
@@ -269,7 +269,7 @@ async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) ->
|
||||
}],
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
@@ -351,7 +351,7 @@ pub async fn user_did_doc(State(state): State<AppState>, Path(handle): Path<Stri
|
||||
})).collect::<Vec<_>>(),
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
@@ -364,7 +364,7 @@ pub async fn user_did_doc(State(state): State<AppState>, Path(handle): Path<Stri
|
||||
Err(_) => return ApiError::InternalError(None).into_response(),
|
||||
};
|
||||
let key_bytes: Vec<u8> =
|
||||
match crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
|
||||
match tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version) {
|
||||
Ok(k) => k,
|
||||
Err(_) => {
|
||||
return ApiError::InternalError(None).into_response();
|
||||
@@ -404,7 +404,7 @@ pub async fn user_did_doc(State(state): State<AppState>, Path(handle): Path<Stri
|
||||
}],
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
}))
|
||||
@@ -480,7 +480,7 @@ pub async fn verify_did_web(
|
||||
let path = parts[3..].join("/");
|
||||
format!("{}://{}/{}/did.json", scheme, domain, path)
|
||||
};
|
||||
let client = crate::api::proxy_client::did_resolution_client();
|
||||
let client = tranquil_pds::api::proxy_client::did_resolution_client();
|
||||
let resp = client
|
||||
.get(&url)
|
||||
.send()
|
||||
@@ -503,7 +503,7 @@ pub async fn verify_did_web(
|
||||
))?;
|
||||
let pds_endpoint = format!("https://{}", hostname);
|
||||
let has_valid_service = services.iter().any(|s| {
|
||||
s["type"] == crate::plc::ServiceType::Pds.as_str() && s["serviceEndpoint"] == pds_endpoint
|
||||
s["type"] == tranquil_pds::plc::ServiceType::Pds.as_str() && s["serviceEndpoint"] == pds_endpoint
|
||||
});
|
||||
if !has_valid_service {
|
||||
return Err(DidWebVerifyError::PdsNotListed(pds_endpoint));
|
||||
@@ -600,7 +600,7 @@ pub async fn get_recommended_did_credentials(
|
||||
verification_methods: VerificationMethods { atproto: did_key },
|
||||
services: Services {
|
||||
atproto_pds: AtprotoPds {
|
||||
service_type: crate::plc::ServiceType::Pds.as_str().to_string(),
|
||||
service_type: tranquil_pds::plc::ServiceType::Pds.as_str().to_string(),
|
||||
endpoint: pds_endpoint,
|
||||
},
|
||||
},
|
||||
@@ -619,10 +619,10 @@ pub async fn update_handle(
|
||||
auth: Auth<NotTakendown>,
|
||||
Json(input): Json<UpdateHandleInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_identity_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::IdentityAttr::Handle,
|
||||
tranquil_pds::oauth::scopes::IdentityAttr::Handle,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -672,7 +672,7 @@ pub async fn update_handle(
|
||||
"Handle segment cannot start or end with hyphen".into(),
|
||||
)));
|
||||
}
|
||||
if crate::moderation::has_explicit_slur(&new_handle) {
|
||||
if tranquil_pds::moderation::has_explicit_slur(&new_handle) {
|
||||
return Err(ApiError::InvalidHandle(Some(
|
||||
"Inappropriate language in handle".into(),
|
||||
)));
|
||||
@@ -704,7 +704,7 @@ pub async fn update_handle(
|
||||
Err(_) => return Err(ApiError::InvalidHandle(None)),
|
||||
};
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed))
|
||||
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed))
|
||||
.await
|
||||
{
|
||||
warn!("Failed to sequence identity event for handle update: {}", e);
|
||||
@@ -730,19 +730,19 @@ pub async fn update_handle(
|
||||
Err(_) => return Err(ApiError::InvalidHandle(None)),
|
||||
};
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed))
|
||||
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed))
|
||||
.await
|
||||
{
|
||||
warn!("Failed to sequence identity event for handle update: {}", e);
|
||||
}
|
||||
return Ok(EmptyResponse::ok().into_response());
|
||||
}
|
||||
match crate::handle::verify_handle_ownership(&new_handle, &did).await {
|
||||
match tranquil_pds::handle::verify_handle_ownership(&new_handle, &did).await {
|
||||
Ok(()) => {}
|
||||
Err(crate::handle::HandleResolutionError::NotFound) => {
|
||||
Err(tranquil_pds::handle::HandleResolutionError::NotFound) => {
|
||||
return Err(ApiError::HandleNotAvailable(None));
|
||||
}
|
||||
Err(crate::handle::HandleResolutionError::DidMismatch { expected, actual }) => {
|
||||
Err(tranquil_pds::handle::HandleResolutionError::DidMismatch { expected, actual }) => {
|
||||
return Err(ApiError::HandleNotAvailable(Some(format!(
|
||||
"Handle points to different DID. Expected {}, got {}",
|
||||
expected, actual
|
||||
@@ -781,15 +781,15 @@ pub async fn update_handle(
|
||||
if !current_handle.is_empty() {
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(¤t_handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(¤t_handle))
|
||||
.await;
|
||||
}
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_identity_event(&state, &did, Some(&handle_typed)).await
|
||||
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle_typed)).await
|
||||
{
|
||||
warn!("Failed to sequence identity event for handle update: {}", e);
|
||||
}
|
||||
@@ -801,7 +801,7 @@ pub async fn update_handle(
|
||||
|
||||
pub async fn update_plc_handle(
|
||||
state: &AppState,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
new_handle: &Handle,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
if !did.as_str().starts_with("did:plc:") {
|
||||
@@ -811,20 +811,20 @@ pub async fn update_plc_handle(
|
||||
Some(r) => r,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let key_bytes = crate::config::decrypt_key(&user_row.key_bytes, user_row.encryption_version)?;
|
||||
let key_bytes = tranquil_pds::config::decrypt_key(&user_row.key_bytes, user_row.encryption_version)?;
|
||||
let signing_key = k256::ecdsa::SigningKey::from_slice(&key_bytes)?;
|
||||
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
let last_op = plc_client.get_last_op(did).await?;
|
||||
let new_also_known_as = vec![format!("at://{}", new_handle)];
|
||||
let update_op =
|
||||
crate::plc::create_update_op(&last_op, None, None, Some(new_also_known_as), None)?;
|
||||
let signed_op = crate::plc::sign_operation(&update_op, &signing_key)?;
|
||||
tranquil_pds::plc::create_update_op(&last_op, None, None, Some(new_also_known_as), None)?;
|
||||
let signed_op = tranquil_pds::plc::sign_operation(&update_op, &signing_key)?;
|
||||
plc_client.send_operation(did, &signed_op).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn well_known_atproto_did(State(state): State<AppState>, headers: HeaderMap) -> Response {
|
||||
let host = match crate::util::get_header_str(&headers, http::header::HOST) {
|
||||
let host = match tranquil_pds::util::get_header_str(&headers, http::header::HOST) {
|
||||
Some(h) => h,
|
||||
None => return (StatusCode::BAD_REQUEST, "Missing host header").into_response(),
|
||||
};
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
use crate::rate_limit::{HandleVerificationLimit, RateLimited};
|
||||
use crate::types::{Did, Handle};
|
||||
use tranquil_pds::rate_limit::{HandleVerificationLimit, RateLimited};
|
||||
use tranquil_pds::types::{Did, Handle};
|
||||
use axum::{
|
||||
Json,
|
||||
response::{IntoResponse, Response},
|
||||
@@ -29,7 +29,7 @@ pub async fn verify_handle_ownership(
|
||||
let handle_str = input.handle.as_str();
|
||||
let did_str = input.did.as_str();
|
||||
|
||||
let dns_mismatch = match crate::handle::resolve_handle_dns(handle_str).await {
|
||||
let dns_mismatch = match tranquil_pds::handle::resolve_handle_dns(handle_str).await {
|
||||
Ok(did) if did == did_str => {
|
||||
return Json(VerifyHandleOwnershipOutput {
|
||||
verified: true,
|
||||
@@ -45,7 +45,7 @@ pub async fn verify_handle_ownership(
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
match crate::handle::resolve_handle_http(handle_str).await {
|
||||
match tranquil_pds::handle::resolve_handle_http(handle_str).await {
|
||||
Ok(did) if did == did_str => Json(VerifyHandleOwnershipOutput {
|
||||
verified: true,
|
||||
method: Some("http".to_string()),
|
||||
+8
-8
@@ -1,7 +1,7 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Auth, Permissive};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Auth, Permissive};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
extract::State,
|
||||
response::{IntoResponse, Response},
|
||||
@@ -10,17 +10,17 @@ use chrono::{Duration, Utc};
|
||||
use tracing::{info, warn};
|
||||
|
||||
fn generate_plc_token() -> String {
|
||||
crate::util::generate_token_code()
|
||||
tranquil_pds::util::generate_token_code()
|
||||
}
|
||||
|
||||
pub async fn request_plc_operation_signature(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Permissive>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_identity_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::IdentityAttr::Wildcard,
|
||||
tranquil_pds::oauth::scopes::IdentityAttr::Wildcard,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ pub async fn request_plc_operation_signature(
|
||||
.log_db_err("creating PLC token")?;
|
||||
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_plc_operation(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_plc_operation(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
+9
-9
@@ -1,9 +1,9 @@
|
||||
use crate::api::ApiError;
|
||||
use crate::api::error::DbResultExt;
|
||||
use crate::auth::{Auth, Permissive};
|
||||
use crate::circuit_breaker::with_circuit_breaker;
|
||||
use crate::plc::{PlcClient, PlcError, PlcService, ServiceType, create_update_op, sign_operation};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::ApiError;
|
||||
use tranquil_pds::api::error::DbResultExt;
|
||||
use tranquil_pds::auth::{Auth, Permissive};
|
||||
use tranquil_pds::circuit_breaker::with_circuit_breaker;
|
||||
use tranquil_pds::plc::{PlcClient, PlcError, PlcService, ServiceType, create_update_op, sign_operation};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -44,10 +44,10 @@ pub async fn sign_plc_operation(
|
||||
auth: Auth<Permissive>,
|
||||
Json(input): Json<SignPlcOperationInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_identity_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::IdentityAttr::Wildcard,
|
||||
tranquil_pds::oauth::scopes::IdentityAttr::Wildcard,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -86,7 +86,7 @@ pub async fn sign_plc_operation(
|
||||
.log_db_err("fetching user key")?
|
||||
.ok_or_else(|| ApiError::InternalError(Some("User signing key not found".into())))?;
|
||||
|
||||
let key_bytes = crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
|
||||
let key_bytes = tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
|
||||
.map_err(|e| {
|
||||
error!("Failed to decrypt user key: {}", e);
|
||||
ApiError::InternalError(None)
|
||||
+13
-13
@@ -1,9 +1,9 @@
|
||||
use crate::api::error::DbResultExt;
|
||||
use crate::api::{ApiError, EmptyResponse};
|
||||
use crate::auth::{Auth, Permissive};
|
||||
use crate::circuit_breaker::with_circuit_breaker;
|
||||
use crate::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::DbResultExt;
|
||||
use tranquil_pds::api::{ApiError, EmptyResponse};
|
||||
use tranquil_pds::auth::{Auth, Permissive};
|
||||
use tranquil_pds::circuit_breaker::with_circuit_breaker;
|
||||
use tranquil_pds::plc::{PlcClient, signing_key_to_did_key, validate_plc_operation};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -24,10 +24,10 @@ pub async fn submit_plc_operation(
|
||||
auth: Auth<Permissive>,
|
||||
Json(input): Json<SubmitPlcOperationInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_identity_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_identity_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::IdentityAttr::Wildcard,
|
||||
tranquil_pds::oauth::scopes::IdentityAttr::Wildcard,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -57,7 +57,7 @@ pub async fn submit_plc_operation(
|
||||
.log_db_err("fetching user key")?
|
||||
.ok_or_else(|| ApiError::InternalError(Some("User signing key not found".into())))?;
|
||||
|
||||
let key_bytes = crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
|
||||
let key_bytes = tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
|
||||
.map_err(|e| {
|
||||
error!("Failed to decrypt user key: {}", e);
|
||||
ApiError::InternalError(None)
|
||||
@@ -89,7 +89,7 @@ pub async fn submit_plc_operation(
|
||||
{
|
||||
let service_type = pds.get("type").and_then(|v| v.as_str());
|
||||
let endpoint = pds.get("endpoint").and_then(|v| v.as_str());
|
||||
if service_type != Some(crate::plc::ServiceType::Pds.as_str()) {
|
||||
if service_type != Some(tranquil_pds::plc::ServiceType::Pds.as_str()) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Incorrect type on atproto_pds service".into(),
|
||||
));
|
||||
@@ -147,15 +147,15 @@ pub async fn submit_plc_operation(
|
||||
}
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&user.handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&user.handle))
|
||||
.await;
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::plc_doc_key(did))
|
||||
.delete(&tranquil_pds::cache_keys::plc_doc_key(did))
|
||||
.await;
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::plc_data_key(did))
|
||||
.delete(&tranquil_pds::cache_keys::plc_data_key(did))
|
||||
.await;
|
||||
if state.did_resolver.refresh_did(did).await.is_none() {
|
||||
warn!(did = %did, "Failed to refresh DID cache after PLC update");
|
||||
@@ -0,0 +1,273 @@
|
||||
pub mod actor;
|
||||
pub mod admin;
|
||||
pub mod age_assurance;
|
||||
pub mod backup;
|
||||
pub mod delegation;
|
||||
pub mod discord_webhook;
|
||||
pub mod identity;
|
||||
pub mod moderation;
|
||||
pub mod notification_prefs;
|
||||
pub mod repo;
|
||||
pub mod server;
|
||||
pub mod telegram_webhook;
|
||||
pub mod temp;
|
||||
pub mod verification;
|
||||
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
pub fn api_routes() -> axum::Router<AppState> {
|
||||
use axum::routing::{get, post};
|
||||
|
||||
axum::Router::new()
|
||||
.route("/_health", get(server::health))
|
||||
.route(
|
||||
"/com.atproto.server.describeServer",
|
||||
get(server::describe_server),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createAccount",
|
||||
post(identity::create_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createSession",
|
||||
post(server::create_session),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.getSession",
|
||||
get(server::get_session),
|
||||
)
|
||||
.route("/_account.listSessions", get(server::list_sessions))
|
||||
.route("/_account.revokeSession", post(server::revoke_session))
|
||||
.route(
|
||||
"/_account.revokeAllSessions",
|
||||
post(server::revoke_all_sessions),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deleteSession",
|
||||
post(server::delete_session),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.refreshSession",
|
||||
post(server::refresh_session),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.confirmSignup",
|
||||
post(server::confirm_signup),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.resendVerification",
|
||||
post(server::resend_verification),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.getServiceAuth",
|
||||
get(server::get_service_auth),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.resolveHandle",
|
||||
get(identity::resolve_handle),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.createRecord",
|
||||
post(repo::create_record),
|
||||
)
|
||||
.route("/com.atproto.repo.putRecord", post(repo::put_record))
|
||||
.route("/com.atproto.repo.getRecord", get(repo::get_record))
|
||||
.route(
|
||||
"/com.atproto.repo.deleteRecord",
|
||||
post(repo::delete_record),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.listRecords",
|
||||
get(repo::list_records),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.describeRepo",
|
||||
get(repo::describe_repo),
|
||||
)
|
||||
.route("/com.atproto.repo.uploadBlob", post(repo::upload_blob))
|
||||
.route(
|
||||
"/com.atproto.repo.applyWrites",
|
||||
post(repo::apply_writes),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.checkAccountStatus",
|
||||
get(server::check_account_status),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.getRecommendedDidCredentials",
|
||||
get(identity::get_recommended_did_credentials),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.listMissingBlobs",
|
||||
get(repo::list_missing_blobs),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.moderation.createReport",
|
||||
post(moderation::create_report),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.getAccountInfo",
|
||||
get(admin::get_account_info),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.getAccountInfos",
|
||||
get(admin::get_account_infos),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.searchAccounts",
|
||||
get(admin::search_accounts),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.activateAccount",
|
||||
post(server::activate_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deactivateAccount",
|
||||
post(server::deactivate_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.requestAccountDelete",
|
||||
post(server::request_account_delete),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deleteAccount",
|
||||
post(server::delete_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.requestPasswordReset",
|
||||
post(server::request_password_reset),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.resetPassword",
|
||||
post(server::reset_password),
|
||||
)
|
||||
.route("/_account.changePassword", post(server::change_password))
|
||||
.route("/_account.removePassword", post(server::remove_password))
|
||||
.route("/_account.setPassword", post(server::set_password))
|
||||
.route("/_account.getPasswordStatus", get(server::get_password_status))
|
||||
.route("/_account.getReauthStatus", get(server::get_reauth_status))
|
||||
.route("/_account.reauthPassword", post(server::reauth_password))
|
||||
.route("/_account.reauthTotp", post(server::reauth_totp))
|
||||
.route("/_account.reauthPasskeyStart", post(server::reauth_passkey_start))
|
||||
.route("/_account.reauthPasskeyFinish", post(server::reauth_passkey_finish))
|
||||
.route("/_account.getLegacyLoginPreference", get(server::get_legacy_login_preference))
|
||||
.route("/_account.updateLegacyLoginPreference", post(server::update_legacy_login_preference))
|
||||
.route("/_account.updateLocale", post(server::update_locale))
|
||||
.route("/_account.listTrustedDevices", get(server::list_trusted_devices))
|
||||
.route("/_account.revokeTrustedDevice", post(server::revoke_trusted_device))
|
||||
.route("/_account.updateTrustedDevice", post(server::update_trusted_device))
|
||||
.route("/_account.createPasskeyAccount", post(server::create_passkey_account))
|
||||
.route("/_account.startPasskeyRegistrationForSetup", post(server::start_passkey_registration_for_setup))
|
||||
.route("/_account.completePasskeySetup", post(server::complete_passkey_setup))
|
||||
.route("/_account.requestPasskeyRecovery", post(server::request_passkey_recovery))
|
||||
.route("/_account.recoverPasskeyAccount", post(server::recover_passkey_account))
|
||||
.route("/_account.updateDidDocument", post(server::update_did_document))
|
||||
.route("/_account.getDidDocument", get(server::get_did_document))
|
||||
.route("/com.atproto.server.requestEmailUpdate", post(server::request_email_update))
|
||||
.route("/_checkEmailVerified", post(server::check_email_verified))
|
||||
.route("/_checkChannelVerified", post(server::check_channel_verified))
|
||||
.route("/com.atproto.server.confirmEmail", post(server::confirm_email))
|
||||
.route("/com.atproto.server.updateEmail", post(server::update_email))
|
||||
.route("/_account.authorizeEmailUpdate", get(server::authorize_email_update))
|
||||
.route("/_account.checkEmailUpdateStatus", get(server::check_email_update_status))
|
||||
.route("/_account.checkEmailInUse", post(server::check_email_in_use))
|
||||
.route("/_account.checkCommsChannelInUse", post(server::check_comms_channel_in_use))
|
||||
.route("/com.atproto.server.reserveSigningKey", post(server::reserve_signing_key))
|
||||
.route("/com.atproto.server.verifyMigrationEmail", post(server::verify_migration_email))
|
||||
.route("/com.atproto.server.resendMigrationVerification", post(server::resend_migration_verification))
|
||||
.route("/com.atproto.identity.updateHandle", post(identity::update_handle))
|
||||
.route("/com.atproto.identity.requestPlcOperationSignature", post(identity::request_plc_operation_signature))
|
||||
.route("/com.atproto.identity.signPlcOperation", post(identity::sign_plc_operation))
|
||||
.route("/com.atproto.identity.submitPlcOperation", post(identity::submit_plc_operation))
|
||||
.route("/_identity.verifyHandleOwnership", post(identity::verify_handle_ownership))
|
||||
.route("/com.atproto.repo.importRepo", post(repo::import_repo))
|
||||
.route("/com.atproto.admin.deleteAccount", post(admin::delete_account))
|
||||
.route("/com.atproto.admin.updateAccountEmail", post(admin::update_account_email))
|
||||
.route("/com.atproto.admin.updateAccountHandle", post(admin::update_account_handle))
|
||||
.route("/com.atproto.admin.updateAccountPassword", post(admin::update_account_password))
|
||||
.route("/com.atproto.server.listAppPasswords", get(server::list_app_passwords))
|
||||
.route("/com.atproto.server.createAppPassword", post(server::create_app_password))
|
||||
.route("/com.atproto.server.revokeAppPassword", post(server::revoke_app_password))
|
||||
.route("/com.atproto.server.createInviteCode", post(server::create_invite_code))
|
||||
.route("/com.atproto.server.createInviteCodes", post(server::create_invite_codes))
|
||||
.route("/com.atproto.server.getAccountInviteCodes", get(server::get_account_invite_codes))
|
||||
.route("/com.atproto.server.createTotpSecret", post(server::create_totp_secret))
|
||||
.route("/com.atproto.server.enableTotp", post(server::enable_totp))
|
||||
.route("/com.atproto.server.disableTotp", post(server::disable_totp))
|
||||
.route("/com.atproto.server.getTotpStatus", get(server::get_totp_status))
|
||||
.route("/com.atproto.server.regenerateBackupCodes", post(server::regenerate_backup_codes))
|
||||
.route("/com.atproto.server.startPasskeyRegistration", post(server::start_passkey_registration))
|
||||
.route("/com.atproto.server.finishPasskeyRegistration", post(server::finish_passkey_registration))
|
||||
.route("/com.atproto.server.listPasskeys", get(server::list_passkeys))
|
||||
.route("/com.atproto.server.deletePasskey", post(server::delete_passkey))
|
||||
.route("/com.atproto.server.updatePasskey", post(server::update_passkey))
|
||||
.route("/com.atproto.admin.getInviteCodes", get(admin::get_invite_codes))
|
||||
.route("/_admin.getServerStats", get(admin::get_server_stats))
|
||||
.route("/_server.getConfig", get(admin::get_server_config))
|
||||
.route("/_admin.updateServerConfig", post(admin::update_server_config))
|
||||
.route("/com.atproto.admin.disableAccountInvites", post(admin::disable_account_invites))
|
||||
.route("/com.atproto.admin.enableAccountInvites", post(admin::enable_account_invites))
|
||||
.route("/com.atproto.admin.disableInviteCodes", post(admin::disable_invite_codes))
|
||||
.route("/com.atproto.admin.getSubjectStatus", get(admin::get_subject_status))
|
||||
.route("/com.atproto.admin.updateSubjectStatus", post(admin::update_subject_status))
|
||||
.route("/com.atproto.admin.sendEmail", post(admin::send_email))
|
||||
.route("/app.bsky.actor.getPreferences", get(actor::get_preferences))
|
||||
.route("/app.bsky.actor.putPreferences", post(actor::put_preferences))
|
||||
.route("/com.atproto.temp.checkSignupQueue", get(temp::check_signup_queue))
|
||||
.route("/com.atproto.temp.dereferenceScope", post(temp::dereference_scope))
|
||||
.route("/_account.getNotificationPrefs", get(notification_prefs::get_notification_prefs))
|
||||
.route("/_account.updateNotificationPrefs", post(notification_prefs::update_notification_prefs))
|
||||
.route("/_account.getNotificationHistory", get(notification_prefs::get_notification_history))
|
||||
.route("/_account.confirmChannelVerification", post(verification::confirm_channel_verification))
|
||||
.route("/_account.verifyToken", post(server::verify_token))
|
||||
.route("/_delegation.listControllers", get(delegation::list_controllers))
|
||||
.route("/_delegation.addController", post(delegation::add_controller))
|
||||
.route("/_delegation.removeController", post(delegation::remove_controller))
|
||||
.route("/_delegation.updateControllerScopes", post(delegation::update_controller_scopes))
|
||||
.route("/_delegation.listControlledAccounts", get(delegation::list_controlled_accounts))
|
||||
.route("/_delegation.getAuditLog", get(delegation::get_audit_log))
|
||||
.route("/_delegation.getScopePresets", get(delegation::get_scope_presets))
|
||||
.route("/_delegation.createDelegatedAccount", post(delegation::create_delegated_account))
|
||||
.route("/_backup.listBackups", get(backup::list_backups))
|
||||
.route("/_backup.getBackup", get(backup::get_backup))
|
||||
.route("/_backup.createBackup", post(backup::create_backup))
|
||||
.route("/_backup.deleteBackup", post(backup::delete_backup))
|
||||
.route("/_backup.setEnabled", post(backup::set_backup_enabled))
|
||||
.route("/_backup.exportBlobs", get(backup::export_blobs))
|
||||
.route("/app.bsky.ageassurance.getState", get(age_assurance::get_state))
|
||||
.route("/app.bsky.unspecced.getAgeAssuranceState", get(age_assurance::get_age_assurance_state))
|
||||
}
|
||||
|
||||
pub fn well_known_api_routes() -> axum::Router<AppState> {
|
||||
use axum::routing::get;
|
||||
|
||||
axum::Router::new()
|
||||
.route("/did.json", get(identity::well_known_did))
|
||||
.route("/atproto-did", get(identity::well_known_atproto_did))
|
||||
}
|
||||
|
||||
pub fn webhook_routes() -> axum::Router<AppState> {
|
||||
use axum::{extract::DefaultBodyLimit, routing::post};
|
||||
|
||||
axum::Router::new()
|
||||
.route(
|
||||
"/webhook/telegram",
|
||||
post(telegram_webhook::handle_telegram_webhook)
|
||||
.layer(DefaultBodyLimit::max(64 * 1024)),
|
||||
)
|
||||
.route(
|
||||
"/webhook/discord",
|
||||
post(discord_webhook::handle_discord_webhook)
|
||||
.layer(DefaultBodyLimit::max(64 * 1024)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn misc_routes() -> axum::Router<AppState> {
|
||||
use axum::routing::get;
|
||||
|
||||
axum::Router::new()
|
||||
.route("/health", get(server::health))
|
||||
.route("/robots.txt", get(server::robots_txt))
|
||||
.route("/favicon.ico", get(server::get_logo))
|
||||
.route("/u/{handle}/did.json", get(identity::user_did_doc))
|
||||
}
|
||||
+8
-8
@@ -1,7 +1,7 @@
|
||||
use crate::api::ApiError;
|
||||
use crate::api::proxy_client::{is_ssrf_safe, proxy_client};
|
||||
use crate::auth::{AnyUser, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::ApiError;
|
||||
use tranquil_pds::api::proxy_client::{is_ssrf_safe, proxy_client};
|
||||
use tranquil_pds::auth::{AnyUser, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -94,7 +94,7 @@ pub async fn create_report(
|
||||
|
||||
async fn proxy_to_report_service(
|
||||
state: &AppState,
|
||||
auth_user: &crate::auth::AuthenticatedUser,
|
||||
auth_user: &tranquil_pds::auth::AuthenticatedUser,
|
||||
service_url: &str,
|
||||
service_did: &str,
|
||||
input: &CreateReportInput,
|
||||
@@ -109,7 +109,7 @@ async fn proxy_to_report_service(
|
||||
Some(kb) => kb.clone(),
|
||||
None => match state.user_repo.get_with_key_by_did(&auth_user.did).await {
|
||||
Ok(Some(user_with_key)) => {
|
||||
match crate::config::decrypt_key(
|
||||
match tranquil_pds::config::decrypt_key(
|
||||
&user_with_key.key_bytes,
|
||||
user_with_key.encryption_version,
|
||||
) {
|
||||
@@ -135,7 +135,7 @@ async fn proxy_to_report_service(
|
||||
},
|
||||
};
|
||||
|
||||
let service_token = match crate::auth::create_service_token(
|
||||
let service_token = match tranquil_pds::auth::create_service_token(
|
||||
&auth_user.did,
|
||||
service_did,
|
||||
"com.atproto.moderation.createReport",
|
||||
@@ -211,7 +211,7 @@ async fn proxy_to_report_service(
|
||||
|
||||
async fn create_report_locally(
|
||||
state: &AppState,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
is_takendown: bool,
|
||||
input: CreateReportInput,
|
||||
) -> Response {
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -142,14 +142,14 @@ pub async fn request_channel_verification(
|
||||
handle: Option<&str>,
|
||||
) -> Result<String, ApiError> {
|
||||
let token =
|
||||
crate::auth::verification_token::generate_channel_update_token(did, channel, identifier);
|
||||
let formatted_token = crate::auth::verification_token::format_token_for_display(&token);
|
||||
tranquil_pds::auth::verification_token::generate_channel_update_token(did, channel, identifier);
|
||||
let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token);
|
||||
|
||||
match channel {
|
||||
CommsChannel::Email => {
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
let handle_str = handle.unwrap_or("user");
|
||||
crate::comms::comms_repo::enqueue_email_update(
|
||||
tranquil_pds::comms::comms_repo::enqueue_email_update(
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
identifier,
|
||||
@@ -183,12 +183,12 @@ pub async fn request_channel_verification(
|
||||
.as_ref()
|
||||
.and_then(|p| p.preferred_locale.as_deref())
|
||||
.unwrap_or("en");
|
||||
let strings = crate::comms::get_strings(locale);
|
||||
let body = crate::comms::format_message(
|
||||
let strings = tranquil_pds::comms::get_strings(locale);
|
||||
let body = tranquil_pds::comms::format_message(
|
||||
strings.channel_verification_body,
|
||||
&[("code", &formatted_token), ("verify_link", &verify_link)],
|
||||
);
|
||||
let subject = crate::comms::format_message(
|
||||
let subject = tranquil_pds::comms::format_message(
|
||||
strings.channel_verification_subject,
|
||||
&[("hostname", hostname)],
|
||||
);
|
||||
@@ -277,7 +277,7 @@ pub async fn update_notification_prefs(
|
||||
return Err(ApiError::InvalidRequest("Email cannot be empty".into()));
|
||||
}
|
||||
|
||||
if !crate::api::validation::is_valid_email(&email_clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_email(&email_clean) {
|
||||
return Err(ApiError::InvalidEmail);
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ pub async fn update_notification_prefs(
|
||||
.await
|
||||
.map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?;
|
||||
info!(did = %auth.did, "Cleared Discord");
|
||||
} else if !crate::api::validation::is_valid_discord_username(&discord_clean) {
|
||||
} else if !tranquil_pds::api::validation::is_valid_discord_username(&discord_clean) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)"
|
||||
.into(),
|
||||
@@ -340,7 +340,7 @@ pub async fn update_notification_prefs(
|
||||
.await
|
||||
.map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?;
|
||||
info!(did = %auth.did, "Cleared Telegram username");
|
||||
} else if !crate::api::validation::is_valid_telegram_username(telegram_clean) {
|
||||
} else if !tranquil_pds::api::validation::is_valid_telegram_username(telegram_clean) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore"
|
||||
.into(),
|
||||
@@ -370,7 +370,7 @@ pub async fn update_notification_prefs(
|
||||
.await
|
||||
.map_err(|e| ApiError::InternalError(Some(format!("Database error: {}", e))))?;
|
||||
info!(did = %auth.did, "Cleared Signal username");
|
||||
} else if !crate::comms::is_valid_signal_username(&signal_clean) {
|
||||
} else if !tranquil_pds::comms::is_valid_signal_username(&signal_clean) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Invalid Signal username. Must be 3-32 characters followed by .XX (e.g. username.01)"
|
||||
.into(),
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Auth, AuthAny, NotTakendown, Permissive, VerifyScope};
|
||||
use crate::delegation::DelegationActionType;
|
||||
use crate::state::AppState;
|
||||
use crate::types::{CidLink, Did};
|
||||
use crate::util::get_header_str;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Auth, AuthAny, NotTakendown, Permissive, VerifyScope};
|
||||
use tranquil_pds::delegation::DelegationActionType;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{CidLink, Did};
|
||||
use tranquil_pds::util::get_header_str;
|
||||
use axum::body::Body;
|
||||
use axum::{
|
||||
Json,
|
||||
+16
-16
@@ -1,11 +1,11 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::api::repo::record::create_signed_commit;
|
||||
use crate::auth::{Auth, NotTakendown};
|
||||
use crate::state::AppState;
|
||||
use crate::sync::import::{ImportError, apply_import, parse_car};
|
||||
use crate::sync::verify::CarVerifier;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::repo_ops::create_signed_commit;
|
||||
use tranquil_pds::auth::{Auth, NotTakendown};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::sync::import::{ImportError, apply_import, parse_car};
|
||||
use tranquil_pds::sync::verify::CarVerifier;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{
|
||||
body::Bytes,
|
||||
extract::State,
|
||||
@@ -121,7 +121,7 @@ pub async fn import_repo(
|
||||
verified.rev, verified.data_cid
|
||||
);
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::DidMismatch {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::DidMismatch {
|
||||
commit_did,
|
||||
expected_did,
|
||||
}) => {
|
||||
@@ -130,7 +130,7 @@ pub async fn import_repo(
|
||||
commit_did, expected_did
|
||||
)));
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::MstValidationFailed(msg)) => {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::MstValidationFailed(msg)) => {
|
||||
return Err(ApiError::InvalidRequest(format!(
|
||||
"MST validation failed: {}",
|
||||
msg
|
||||
@@ -154,7 +154,7 @@ pub async fn import_repo(
|
||||
verified.rev, verified.data_cid
|
||||
);
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::DidMismatch {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::DidMismatch {
|
||||
commit_did,
|
||||
expected_did,
|
||||
}) => {
|
||||
@@ -163,24 +163,24 @@ pub async fn import_repo(
|
||||
commit_did, expected_did
|
||||
)));
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::InvalidSignature) => {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::InvalidSignature) => {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"CAR file commit signature verification failed".into(),
|
||||
));
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::DidResolutionFailed(msg)) => {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::DidResolutionFailed(msg)) => {
|
||||
warn!("DID resolution failed during import verification: {}", msg);
|
||||
return Err(ApiError::InvalidRequest(format!(
|
||||
"Failed to verify DID: {}",
|
||||
msg
|
||||
)));
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::NoSigningKey) => {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::NoSigningKey) => {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"DID document does not contain a signing key".into(),
|
||||
));
|
||||
}
|
||||
Err(crate::sync::verify::VerifyError::MstValidationFailed(msg)) => {
|
||||
Err(tranquil_pds::sync::verify::VerifyError::MstValidationFailed(msg)) => {
|
||||
return Err(ApiError::InvalidRequest(format!(
|
||||
"MST validation failed: {}",
|
||||
msg
|
||||
@@ -264,7 +264,7 @@ pub async fn import_repo(
|
||||
ApiError::InternalError(Some("Signing key not found".into()))
|
||||
})?;
|
||||
let key_bytes =
|
||||
crate::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
|
||||
tranquil_pds::config::decrypt_key(&key_row.key_bytes, key_row.encryption_version)
|
||||
.map_err(|e| {
|
||||
error!("Failed to decrypt signing key: {}", e);
|
||||
ApiError::InternalError(None)
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::state::AppState;
|
||||
use crate::types::AtIdentifier;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::AtIdentifier;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
@@ -20,7 +20,7 @@ pub async fn describe_repo(
|
||||
) -> Response {
|
||||
let hostname_for_handles = tranquil_config::get().server.hostname_without_port();
|
||||
let user_row = if input.repo.is_did() {
|
||||
let did: crate::types::Did = match input.repo.as_str().parse() {
|
||||
let did: tranquil_pds::types::Did = match input.repo.as_str().parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
|
||||
};
|
||||
@@ -36,7 +36,7 @@ pub async fn describe_repo(
|
||||
} else {
|
||||
repo_str.to_string()
|
||||
};
|
||||
let handle: crate::types::Handle = match handle_str.parse() {
|
||||
let handle: tranquil_pds::types::Handle = match handle_str.parse() {
|
||||
Ok(h) => h,
|
||||
Err(_) => {
|
||||
return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
|
||||
+11
-11
@@ -1,17 +1,17 @@
|
||||
use super::validation::validate_record_with_status;
|
||||
use super::validation_mode::{ValidationMode, deserialize_validation_mode};
|
||||
use crate::api::error::ApiError;
|
||||
use crate::api::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids};
|
||||
use crate::auth::{
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use crate::repo::record::utils::{CommitParams, RecordOp, commit_and_log, extract_blob_cids};
|
||||
use tranquil_pds::auth::{
|
||||
Active, Auth, WriteOpKind, require_not_migrated, require_verified_or_delegated,
|
||||
verify_batch_write_scopes,
|
||||
};
|
||||
use crate::cid_types::CommitCid;
|
||||
use crate::delegation::DelegationActionType;
|
||||
use crate::repo::tracking::TrackingBlockStore;
|
||||
use crate::state::AppState;
|
||||
use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
|
||||
use crate::validation::ValidationStatus;
|
||||
use tranquil_pds::cid_types::CommitCid;
|
||||
use tranquil_pds::delegation::DelegationActionType;
|
||||
use tranquil_pds::repo::tracking::TrackingBlockStore;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
|
||||
use tranquil_pds::validation::ValidationStatus;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -74,7 +74,7 @@ async fn process_single_write(
|
||||
};
|
||||
all_blob_cids.extend(extract_blob_cids(value));
|
||||
let rkey = rkey.clone().unwrap_or_else(Rkey::generate);
|
||||
let record_ipld = crate::util::json_to_ipld(value);
|
||||
let record_ipld = tranquil_pds::util::json_to_ipld(value);
|
||||
let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| {
|
||||
ApiError::InvalidRecord("Failed to serialize record".into()).into_response()
|
||||
})?;
|
||||
@@ -126,7 +126,7 @@ async fn process_single_write(
|
||||
}
|
||||
};
|
||||
all_blob_cids.extend(extract_blob_cids(value));
|
||||
let record_ipld = crate::util::json_to_ipld(value);
|
||||
let record_ipld = tranquil_pds::util::json_to_ipld(value);
|
||||
let record_bytes = serde_ipld_dagcbor::to_vec(&record_ipld).map_err(|_| {
|
||||
ApiError::InvalidRecord("Failed to serialize record".into()).into_response()
|
||||
})?;
|
||||
+11
-11
@@ -1,14 +1,14 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::api::repo::record::utils::{
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use crate::repo::record::utils::{
|
||||
CommitError, CommitParams, RecordOp, commit_and_log, get_current_root_cid,
|
||||
};
|
||||
use crate::api::repo::record::write::{CommitInfo, prepare_repo_write};
|
||||
use crate::auth::{Active, Auth, VerifyScope};
|
||||
use crate::cid_types::CommitCid;
|
||||
use crate::delegation::DelegationActionType;
|
||||
use crate::repo::tracking::TrackingBlockStore;
|
||||
use crate::state::AppState;
|
||||
use crate::types::{AtIdentifier, AtUri, Nsid, Rkey};
|
||||
use crate::repo::record::write::{CommitInfo, prepare_repo_write};
|
||||
use tranquil_pds::auth::{Active, Auth, VerifyScope};
|
||||
use tranquil_pds::cid_types::CommitCid;
|
||||
use tranquil_pds::delegation::DelegationActionType;
|
||||
use tranquil_pds::repo::tracking::TrackingBlockStore;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{AtIdentifier, AtUri, Nsid, Rkey};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -45,7 +45,7 @@ pub async fn delete_record(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
Json(input): Json<DeleteRecordInput>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let scope_proof = match auth.verify_repo_delete(&input.collection) {
|
||||
Ok(proof) => proof,
|
||||
Err(e) => return Ok(e.into_response()),
|
||||
@@ -229,7 +229,7 @@ pub async fn delete_record(
|
||||
.into_response())
|
||||
}
|
||||
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::types::Did;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn delete_record_internal(
|
||||
+8
-8
@@ -1,7 +1,7 @@
|
||||
use super::pagination::{PaginationDirection, deserialize_pagination_direction};
|
||||
use crate::api::error::ApiError;
|
||||
use crate::state::AppState;
|
||||
use crate::types::{AtIdentifier, Nsid, Rkey};
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{AtIdentifier, Nsid, Rkey};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
@@ -61,7 +61,7 @@ pub async fn get_record(
|
||||
) -> Response {
|
||||
let hostname_for_handles = tranquil_config::get().server.hostname_without_port();
|
||||
let user_id_opt = if input.repo.is_did() {
|
||||
let did: crate::types::Did = match input.repo.as_str().parse() {
|
||||
let did: tranquil_pds::types::Did = match input.repo.as_str().parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
|
||||
};
|
||||
@@ -73,7 +73,7 @@ pub async fn get_record(
|
||||
} else {
|
||||
repo_str.to_string()
|
||||
};
|
||||
let handle: crate::types::Handle = match handle_str.parse() {
|
||||
let handle: tranquil_pds::types::Handle = match handle_str.parse() {
|
||||
Ok(h) => h,
|
||||
Err(_) => {
|
||||
return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
|
||||
@@ -160,7 +160,7 @@ pub async fn list_records(
|
||||
) -> Response {
|
||||
let hostname_for_handles = tranquil_config::get().server.hostname_without_port();
|
||||
let user_id_opt = if input.repo.is_did() {
|
||||
let did: crate::types::Did = match input.repo.as_str().parse() {
|
||||
let did: tranquil_pds::types::Did = match input.repo.as_str().parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => return ApiError::InvalidRequest("Invalid DID format".into()).into_response(),
|
||||
};
|
||||
@@ -172,7 +172,7 @@ pub async fn list_records(
|
||||
} else {
|
||||
repo_str.to_string()
|
||||
};
|
||||
let handle: crate::types::Handle = match handle_str.parse() {
|
||||
let handle: tranquil_pds::types::Handle = match handle_str.parse() {
|
||||
Ok(h) => h,
|
||||
Err(_) => {
|
||||
return ApiError::InvalidRequest("Invalid handle format".into()).into_response();
|
||||
@@ -198,7 +198,7 @@ pub async fn list_records(
|
||||
let cursor_rkey = input
|
||||
.cursor
|
||||
.as_ref()
|
||||
.and_then(|c| c.parse::<crate::types::Rkey>().ok());
|
||||
.and_then(|c| c.parse::<tranquil_pds::types::Rkey>().ok());
|
||||
let rows = match state
|
||||
.repo_repo
|
||||
.list_records(
|
||||
@@ -0,0 +1 @@
|
||||
pub use tranquil_pds::repo_ops::*;
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::types::{Nsid, Rkey};
|
||||
use crate::validation::{RecordValidator, ValidationError, ValidationStatus};
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::types::{Nsid, Rkey};
|
||||
use tranquil_pds::validation::{RecordValidator, ValidationError, ValidationStatus};
|
||||
use axum::response::Response;
|
||||
|
||||
pub async fn validate_record_with_status(
|
||||
+13
-13
@@ -1,20 +1,20 @@
|
||||
use super::validation::validate_record_with_status;
|
||||
use super::validation_mode::{ValidationMode, deserialize_validation_mode};
|
||||
use crate::api::error::ApiError;
|
||||
use crate::api::repo::record::utils::{
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use crate::repo::record::utils::{
|
||||
CommitParams, RecordOp, commit_and_log, extract_backlinks, extract_blob_cids,
|
||||
get_current_root_cid,
|
||||
};
|
||||
use crate::auth::{
|
||||
use tranquil_pds::auth::{
|
||||
Active, Auth, AuthSource, RepoScopeAction, ScopeVerified, VerifyScope, require_not_migrated,
|
||||
require_verified_or_delegated,
|
||||
};
|
||||
use crate::cid_types::CommitCid;
|
||||
use crate::delegation::DelegationActionType;
|
||||
use crate::repo::tracking::TrackingBlockStore;
|
||||
use crate::state::AppState;
|
||||
use crate::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
|
||||
use crate::validation::ValidationStatus;
|
||||
use tranquil_pds::cid_types::CommitCid;
|
||||
use tranquil_pds::delegation::DelegationActionType;
|
||||
use tranquil_pds::repo::tracking::TrackingBlockStore;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{AtIdentifier, AtUri, Did, Nsid, Rkey};
|
||||
use tranquil_pds::validation::ValidationStatus;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -104,7 +104,7 @@ pub async fn create_record(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
Json(input): Json<CreateRecordInput>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let scope_proof = match auth.verify_repo_create(&input.collection) {
|
||||
Ok(proof) => proof,
|
||||
Err(e) => return Ok(e.into_response()),
|
||||
@@ -232,7 +232,7 @@ pub async fn create_record(
|
||||
}
|
||||
}
|
||||
|
||||
let record_ipld = crate::util::json_to_ipld(&input.record);
|
||||
let record_ipld = tranquil_pds::util::json_to_ipld(&input.record);
|
||||
let mut record_bytes = Vec::new();
|
||||
if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() {
|
||||
return Ok(ApiError::InvalidRecord("Failed to serialize record".into()).into_response());
|
||||
@@ -408,7 +408,7 @@ pub async fn put_record(
|
||||
State(state): State<AppState>,
|
||||
auth: Auth<Active>,
|
||||
Json(input): Json<PutRecordInput>,
|
||||
) -> Result<Response, crate::api::error::ApiError> {
|
||||
) -> Result<Response, tranquil_pds::api::error::ApiError> {
|
||||
let upsert_proof = match auth.verify_repo_upsert(&input.collection) {
|
||||
Ok(proof) => proof,
|
||||
Err(e) => return Ok(e.into_response()),
|
||||
@@ -476,7 +476,7 @@ pub async fn put_record(
|
||||
}
|
||||
}
|
||||
let existing_cid = mst.get(&key).await.ok().flatten();
|
||||
let record_ipld = crate::util::json_to_ipld(&input.record);
|
||||
let record_ipld = tranquil_pds::util::json_to_ipld(&input.record);
|
||||
let mut record_bytes = Vec::new();
|
||||
if serde_ipld_dagcbor::to_writer(&mut record_bytes, &record_ipld).is_err() {
|
||||
return Ok(ApiError::InvalidRecord("Failed to serialize record".into()).into_response());
|
||||
+31
-31
@@ -1,10 +1,10 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Auth, NotTakendown, Permissive, require_legacy_session_mfa};
|
||||
use crate::cache::Cache;
|
||||
use crate::plc::PlcClient;
|
||||
use crate::state::AppState;
|
||||
use crate::types::PlainPassword;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Auth, NotTakendown, Permissive, require_legacy_session_mfa};
|
||||
use tranquil_pds::cache::Cache;
|
||||
use tranquil_pds::plc::PlcClient;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::PlainPassword;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -117,7 +117,7 @@ pub async fn check_account_status(
|
||||
async fn is_valid_did_for_service(
|
||||
user_repo: &dyn tranquil_db_traits::UserRepository,
|
||||
cache: Arc<dyn Cache>,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> bool {
|
||||
assert_valid_did_document_for_service(user_repo, cache, did, false)
|
||||
.await
|
||||
@@ -127,7 +127,7 @@ async fn is_valid_did_for_service(
|
||||
async fn assert_valid_did_document_for_service(
|
||||
user_repo: &dyn tranquil_db_traits::UserRepository,
|
||||
cache: Arc<dyn Cache>,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
with_retry: bool,
|
||||
) -> Result<(), ApiError> {
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
@@ -226,7 +226,7 @@ async fn assert_valid_did_document_for_service(
|
||||
|
||||
if let Some(key_info) = user_key {
|
||||
let key_bytes =
|
||||
crate::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version)
|
||||
tranquil_pds::config::decrypt_key(&key_info.key_bytes, key_info.encryption_version)
|
||||
.map_err(|e| {
|
||||
error!("Failed to decrypt user key: {}", e);
|
||||
ApiError::InternalError(None)
|
||||
@@ -235,7 +235,7 @@ async fn assert_valid_did_document_for_service(
|
||||
error!("Failed to create signing key: {:?}", e);
|
||||
ApiError::InternalError(None)
|
||||
})?;
|
||||
let expected_did_key = crate::plc::signing_key_to_did_key(&signing_key);
|
||||
let expected_did_key = tranquil_pds::plc::signing_key_to_did_key(&signing_key);
|
||||
|
||||
if doc_signing_key != Some(&expected_did_key) {
|
||||
warn!(
|
||||
@@ -248,7 +248,7 @@ async fn assert_valid_did_document_for_service(
|
||||
}
|
||||
}
|
||||
} else if let Some(host_and_path) = did.as_str().strip_prefix("did:web:") {
|
||||
let client = crate::api::proxy_client::did_resolution_client();
|
||||
let client = tranquil_pds::api::proxy_client::did_resolution_client();
|
||||
let decoded = host_and_path.replace("%3A", ":");
|
||||
let parts: Vec<&str> = decoded.split(':').collect();
|
||||
let (host, path_parts) = if parts.len() > 1 && parts[1].chars().all(|c| c.is_ascii_digit())
|
||||
@@ -284,7 +284,7 @@ async fn assert_valid_did_document_for_service(
|
||||
arr.iter().find(|svc| {
|
||||
svc.get("id").and_then(|id| id.as_str()) == Some("#atproto_pds")
|
||||
|| svc.get("type").and_then(|t| t.as_str())
|
||||
== Some(crate::plc::ServiceType::Pds.as_str())
|
||||
== Some(tranquil_pds::plc::ServiceType::Pds.as_str())
|
||||
})
|
||||
})
|
||||
.and_then(|svc| svc.get("serviceEndpoint"))
|
||||
@@ -314,11 +314,11 @@ pub async fn activate_account(
|
||||
auth.did
|
||||
);
|
||||
|
||||
if let Err(e) = crate::auth::scope_check::check_account_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::AccountAttr::Repo,
|
||||
crate::oauth::scopes::AccountAction::Manage,
|
||||
tranquil_pds::oauth::scopes::AccountAttr::Repo,
|
||||
tranquil_pds::oauth::scopes::AccountAction::Manage,
|
||||
) {
|
||||
info!("[MIGRATION] activateAccount: Scope check failed");
|
||||
return Ok(e);
|
||||
@@ -365,15 +365,15 @@ pub async fn activate_account(
|
||||
did
|
||||
);
|
||||
if let Some(ref h) = handle {
|
||||
let _ = state.cache.delete(&crate::cache_keys::handle_key(h)).await;
|
||||
let _ = state.cache.delete(&tranquil_pds::cache_keys::handle_key(h)).await;
|
||||
}
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::plc_doc_key(&did))
|
||||
.delete(&tranquil_pds::cache_keys::plc_doc_key(&did))
|
||||
.await;
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::plc_data_key(&did))
|
||||
.delete(&tranquil_pds::cache_keys::plc_data_key(&did))
|
||||
.await;
|
||||
if state.did_resolver.refresh_did(did.as_str()).await.is_none() {
|
||||
warn!(
|
||||
@@ -385,7 +385,7 @@ pub async fn activate_account(
|
||||
"[MIGRATION] activateAccount: Sequencing account event (active=true) for did={}",
|
||||
did
|
||||
);
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
&did,
|
||||
tranquil_db_traits::AccountStatus::Active,
|
||||
@@ -404,7 +404,7 @@ pub async fn activate_account(
|
||||
did, handle
|
||||
);
|
||||
let handle_typed = handle.clone();
|
||||
if let Err(e) = crate::api::repo::record::sequence_identity_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
|
||||
&state,
|
||||
&did,
|
||||
handle_typed.as_ref(),
|
||||
@@ -438,7 +438,7 @@ pub async fn activate_account(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Err(e) = crate::api::repo::record::sequence_sync_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_sync_event(
|
||||
&state,
|
||||
&did,
|
||||
root_cid_link.as_str(),
|
||||
@@ -483,11 +483,11 @@ pub async fn deactivate_account(
|
||||
auth: Auth<Permissive>,
|
||||
Json(input): Json<DeactivateAccountInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_account_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::AccountAttr::Repo,
|
||||
crate::oauth::scopes::AccountAction::Manage,
|
||||
tranquil_pds::oauth::scopes::AccountAttr::Repo,
|
||||
tranquil_pds::oauth::scopes::AccountAction::Manage,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -507,9 +507,9 @@ pub async fn deactivate_account(
|
||||
match result {
|
||||
Ok(true) => {
|
||||
if let Some(ref h) = handle {
|
||||
let _ = state.cache.delete(&crate::cache_keys::handle_key(h)).await;
|
||||
let _ = state.cache.delete(&tranquil_pds::cache_keys::handle_key(h)).await;
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
&did,
|
||||
tranquil_db_traits::AccountStatus::Deactivated,
|
||||
@@ -552,7 +552,7 @@ pub async fn request_account_delete(
|
||||
.await
|
||||
.log_db_err("creating deletion token")?;
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_account_deletion(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_account_deletion(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
@@ -569,7 +569,7 @@ pub async fn request_account_delete(
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct DeleteAccountInput {
|
||||
pub did: crate::types::Did,
|
||||
pub did: tranquil_pds::types::Did,
|
||||
pub password: PlainPassword,
|
||||
pub token: String,
|
||||
}
|
||||
@@ -642,7 +642,7 @@ pub async fn delete_account(
|
||||
error!("DB error deleting account: {:?}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
let account_seq = crate::api::repo::record::sequence_account_event(
|
||||
let account_seq = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
did,
|
||||
tranquil_db_traits::AccountStatus::Deleted,
|
||||
@@ -666,7 +666,7 @@ pub async fn delete_account(
|
||||
}
|
||||
let _ = state
|
||||
.cache
|
||||
.delete(&crate::cache_keys::handle_key(&handle))
|
||||
.delete(&tranquil_pds::cache_keys::handle_key(&handle))
|
||||
.await;
|
||||
info!("Account {} deleted successfully", did);
|
||||
EmptyResponse::ok().into_response()
|
||||
+7
-7
@@ -1,9 +1,9 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Auth, NotTakendown, Permissive, generate_app_password};
|
||||
use crate::delegation::{DelegationActionType, intersect_scopes};
|
||||
use crate::rate_limit::{AppPasswordLimit, RateLimited};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Auth, NotTakendown, Permissive, generate_app_password};
|
||||
use tranquil_pds::delegation::{DelegationActionType, intersect_scopes};
|
||||
use tranquil_pds::rate_limit::{AppPasswordLimit, RateLimited};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -233,7 +233,7 @@ pub async fn revoke_app_password(
|
||||
.log_db_err("revoking sessions for app password")?;
|
||||
|
||||
futures::future::join_all(sessions_to_invalidate.iter().map(|jti| {
|
||||
let cache_key = crate::cache_keys::session_key(&auth.did, jti);
|
||||
let cache_key = tranquil_pds::cache_keys::session_key(&auth.did, jti);
|
||||
let cache = state.cache.clone();
|
||||
async move {
|
||||
let _ = cache.delete(&cache_key).await;
|
||||
+43
-43
@@ -1,8 +1,8 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::api::{EmptyResponse, TokenRequiredResponse, VerifiedResponse};
|
||||
use crate::auth::{Auth, NotTakendown};
|
||||
use crate::rate_limit::{EmailUpdateLimit, RateLimited, VerificationCheckLimit};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::api::{EmptyResponse, TokenRequiredResponse, VerifiedResponse};
|
||||
use tranquil_pds::auth::{Auth, NotTakendown};
|
||||
use tranquil_pds::rate_limit::{EmailUpdateLimit, RateLimited, VerificationCheckLimit};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -20,7 +20,7 @@ use tranquil_db_traits::CommsChannel;
|
||||
const EMAIL_UPDATE_TTL: Duration = Duration::from_secs(30 * 60);
|
||||
|
||||
fn email_update_cache_key(did: &str) -> String {
|
||||
crate::cache_keys::email_update_key(did)
|
||||
tranquil_pds::cache_keys::email_update_key(did)
|
||||
}
|
||||
|
||||
fn hash_token(token: &str) -> String {
|
||||
@@ -49,11 +49,11 @@ pub async fn request_email_update(
|
||||
auth: Auth<NotTakendown>,
|
||||
input: Option<Json<RequestEmailUpdateInput>>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_account_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::AccountAttr::Email,
|
||||
crate::oauth::scopes::AccountAction::Manage,
|
||||
tranquil_pds::oauth::scopes::AccountAttr::Email,
|
||||
tranquil_pds::oauth::scopes::AccountAction::Manage,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -74,10 +74,10 @@ pub async fn request_email_update(
|
||||
let token_required = user.email_verified;
|
||||
|
||||
if token_required {
|
||||
let token = crate::auth::email_token::create_email_token(
|
||||
let token = tranquil_pds::auth::email_token::create_email_token(
|
||||
state.cache.as_ref(),
|
||||
auth.did.as_str(),
|
||||
crate::auth::email_token::EmailTokenPurpose::UpdateEmail,
|
||||
tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@@ -89,7 +89,7 @@ pub async fn request_email_update(
|
||||
&& let Some(ref new_email) = inp.new_email
|
||||
{
|
||||
let new_email = new_email.trim().to_lowercase();
|
||||
if !new_email.is_empty() && crate::api::validation::is_valid_email(&new_email) {
|
||||
if !new_email.is_empty() && tranquil_pds::api::validation::is_valid_email(&new_email) {
|
||||
let pending = PendingEmailUpdate {
|
||||
new_email,
|
||||
token_hash: hash_token(&token),
|
||||
@@ -105,7 +105,7 @@ pub async fn request_email_update(
|
||||
}
|
||||
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_short_token_email(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_short_token_email(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user.id,
|
||||
@@ -135,11 +135,11 @@ pub async fn confirm_email(
|
||||
auth: Auth<NotTakendown>,
|
||||
Json(input): Json<ConfirmEmailInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_account_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::AccountAttr::Email,
|
||||
crate::oauth::scopes::AccountAction::Manage,
|
||||
tranquil_pds::oauth::scopes::AccountAttr::Email,
|
||||
tranquil_pds::oauth::scopes::AccountAction::Manage,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -167,9 +167,9 @@ pub async fn confirm_email(
|
||||
}
|
||||
|
||||
let confirmation_code =
|
||||
crate::auth::verification_token::normalize_token_input(input.token.trim());
|
||||
tranquil_pds::auth::verification_token::normalize_token_input(input.token.trim());
|
||||
|
||||
let verified = crate::auth::verification_token::verify_signup_token(
|
||||
let verified = tranquil_pds::auth::verification_token::verify_signup_token(
|
||||
&confirmation_code,
|
||||
CommsChannel::Email,
|
||||
&provided_email,
|
||||
@@ -181,7 +181,7 @@ pub async fn confirm_email(
|
||||
return Err(ApiError::InvalidToken(None));
|
||||
}
|
||||
}
|
||||
Err(crate::auth::verification_token::VerifyError::Expired) => {
|
||||
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
|
||||
return Err(ApiError::ExpiredToken(None));
|
||||
}
|
||||
Err(_) => {
|
||||
@@ -213,11 +213,11 @@ pub async fn update_email(
|
||||
auth: Auth<NotTakendown>,
|
||||
Json(input): Json<UpdateEmailInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_account_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::AccountAttr::Email,
|
||||
crate::oauth::scopes::AccountAction::Manage,
|
||||
tranquil_pds::oauth::scopes::AccountAttr::Email,
|
||||
tranquil_pds::oauth::scopes::AccountAction::Manage,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
@@ -235,7 +235,7 @@ pub async fn update_email(
|
||||
let email_verified = user.email_verified;
|
||||
let new_email = input.email.trim().to_lowercase();
|
||||
|
||||
if !crate::api::validation::is_valid_email(&new_email) {
|
||||
if !tranquil_pds::api::validation::is_valid_email(&new_email) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"This email address is not supported, please use a different email.".into(),
|
||||
));
|
||||
@@ -255,15 +255,15 @@ pub async fn update_email(
|
||||
.filter(|t| !t.is_empty())
|
||||
.ok_or(ApiError::TokenRequired)?;
|
||||
|
||||
crate::auth::email_token::validate_email_token(
|
||||
tranquil_pds::auth::email_token::validate_email_token(
|
||||
state.cache.as_ref(),
|
||||
did.as_str(),
|
||||
crate::auth::email_token::EmailTokenPurpose::UpdateEmail,
|
||||
tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail,
|
||||
token,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
crate::auth::email_token::TokenError::ExpiredToken => {
|
||||
tranquil_pds::auth::email_token::TokenError::ExpiredToken => {
|
||||
ApiError::ExpiredToken(None)
|
||||
}
|
||||
_ => ApiError::InvalidToken(None),
|
||||
@@ -303,24 +303,24 @@ pub async fn update_email(
|
||||
.filter(|t| !t.is_empty())
|
||||
.ok_or(ApiError::TokenRequired)?;
|
||||
|
||||
let short_token_result = crate::auth::email_token::validate_email_token(
|
||||
let short_token_result = tranquil_pds::auth::email_token::validate_email_token(
|
||||
state.cache.as_ref(),
|
||||
did.as_str(),
|
||||
crate::auth::email_token::EmailTokenPurpose::UpdateEmail,
|
||||
tranquil_pds::auth::email_token::EmailTokenPurpose::UpdateEmail,
|
||||
token,
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Err(e) = short_token_result {
|
||||
let confirmation_token =
|
||||
crate::auth::verification_token::normalize_token_input(token.trim());
|
||||
tranquil_pds::auth::verification_token::normalize_token_input(token.trim());
|
||||
|
||||
let current_email_lower = current_email
|
||||
.as_ref()
|
||||
.map(|e| e.to_lowercase())
|
||||
.unwrap_or_default();
|
||||
|
||||
let verified = crate::auth::verification_token::verify_channel_update_token(
|
||||
let verified = tranquil_pds::auth::verification_token::verify_channel_update_token(
|
||||
&confirmation_token,
|
||||
CommsChannel::Email,
|
||||
¤t_email_lower,
|
||||
@@ -332,9 +332,9 @@ pub async fn update_email(
|
||||
return Err(ApiError::InvalidToken(None));
|
||||
}
|
||||
}
|
||||
Err(crate::auth::verification_token::VerifyError::Expired) => {
|
||||
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
|
||||
return Err(match e {
|
||||
crate::auth::email_token::TokenError::ExpiredToken => {
|
||||
tranquil_pds::auth::email_token::TokenError::ExpiredToken => {
|
||||
ApiError::ExpiredToken(None)
|
||||
}
|
||||
_ => ApiError::InvalidToken(None),
|
||||
@@ -342,7 +342,7 @@ pub async fn update_email(
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(match e {
|
||||
crate::auth::email_token::TokenError::ExpiredToken => {
|
||||
tranquil_pds::auth::email_token::TokenError::ExpiredToken => {
|
||||
ApiError::ExpiredToken(None)
|
||||
}
|
||||
_ => ApiError::InvalidToken(None),
|
||||
@@ -359,15 +359,15 @@ pub async fn update_email(
|
||||
.await
|
||||
.log_db_err("updating email")?;
|
||||
|
||||
let verification_token = crate::auth::verification_token::generate_signup_token(
|
||||
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
|
||||
did,
|
||||
CommsChannel::Email,
|
||||
&new_email,
|
||||
);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&verification_token);
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
@@ -423,7 +423,7 @@ pub async fn check_email_verified(
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CheckChannelVerifiedInput {
|
||||
pub did: crate::types::Did,
|
||||
pub did: tranquil_pds::types::Did,
|
||||
pub channel: CommsChannel,
|
||||
}
|
||||
|
||||
@@ -456,11 +456,11 @@ pub async fn authorize_email_update(
|
||||
_rate_limit: RateLimited<VerificationCheckLimit>,
|
||||
axum::extract::Query(query): axum::extract::Query<AuthorizeEmailUpdateQuery>,
|
||||
) -> Response {
|
||||
let verified = crate::auth::verification_token::verify_token_signature(&query.token);
|
||||
let verified = tranquil_pds::auth::verification_token::verify_token_signature(&query.token);
|
||||
|
||||
let token_data = match verified {
|
||||
Ok(data) => data,
|
||||
Err(crate::auth::verification_token::VerifyError::Expired) => {
|
||||
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
|
||||
warn!("authorize_email_update: token expired");
|
||||
return ApiError::ExpiredToken(None).into_response();
|
||||
}
|
||||
@@ -470,7 +470,7 @@ pub async fn authorize_email_update(
|
||||
}
|
||||
};
|
||||
|
||||
if token_data.purpose != crate::auth::verification_token::VerificationPurpose::ChannelUpdate {
|
||||
if token_data.purpose != tranquil_pds::auth::verification_token::VerificationPurpose::ChannelUpdate {
|
||||
warn!(
|
||||
"authorize_email_update: wrong purpose: {:?}",
|
||||
token_data.purpose
|
||||
@@ -544,11 +544,11 @@ pub async fn check_email_update_status(
|
||||
_rate_limit: RateLimited<VerificationCheckLimit>,
|
||||
auth: Auth<NotTakendown>,
|
||||
) -> Result<Response, ApiError> {
|
||||
if let Err(e) = crate::auth::scope_check::check_account_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_account_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
crate::oauth::scopes::AccountAttr::Email,
|
||||
crate::oauth::scopes::AccountAction::Read,
|
||||
tranquil_pds::oauth::scopes::AccountAttr::Email,
|
||||
tranquil_pds::oauth::scopes::AccountAction::Read,
|
||||
) {
|
||||
return Ok(e);
|
||||
}
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
use crate::api::ApiError;
|
||||
use crate::api::error::DbResultExt;
|
||||
use crate::auth::{Admin, Auth, NotTakendown};
|
||||
use crate::state::AppState;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::ApiError;
|
||||
use tranquil_pds::api::error::DbResultExt;
|
||||
use tranquil_pds::auth::{Admin, Auth, NotTakendown};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::State,
|
||||
@@ -21,7 +21,7 @@ pub async fn get_logo(State(state): State<AppState>) -> Response {
|
||||
Some(c) if !c.is_empty() => c,
|
||||
_ => return StatusCode::NOT_FOUND.into_response(),
|
||||
};
|
||||
let cid = match crate::types::CidLink::new(&cid_str) {
|
||||
let cid = match tranquil_pds::types::CidLink::new(&cid_str) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return StatusCode::NOT_FOUND.into_response(),
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::BUILD_VERSION;
|
||||
use crate::state::AppState;
|
||||
use crate::util::{discord_app_id, discord_bot_username, telegram_bot_username};
|
||||
use tranquil_pds::BUILD_VERSION;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::util::{discord_app_id, discord_bot_username, telegram_bot_username};
|
||||
use axum::{Json, extract::State, http::StatusCode, response::IntoResponse};
|
||||
use serde_json::json;
|
||||
|
||||
+9
-9
@@ -1,7 +1,7 @@
|
||||
use crate::api::ApiError;
|
||||
use crate::api::error::DbResultExt;
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::ApiError;
|
||||
use tranquil_pds::api::error::DbResultExt;
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -145,7 +145,7 @@ pub async fn get_did_document(
|
||||
Ok((StatusCode::OK, Json(json!({ "didDocument": did_doc }))).into_response())
|
||||
}
|
||||
|
||||
async fn build_did_document(state: &AppState, did: &crate::types::Did) -> serde_json::Value {
|
||||
async fn build_did_document(state: &AppState, did: &tranquil_pds::types::Did) -> serde_json::Value {
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
|
||||
let user = match state.user_repo.get_user_for_did_doc_build(did).await {
|
||||
@@ -195,7 +195,7 @@ async fn build_did_document(state: &AppState, did: &crate::types::Did) -> serde_
|
||||
})).collect::<Vec<_>>(),
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
});
|
||||
@@ -209,8 +209,8 @@ async fn build_did_document(state: &AppState, did: &crate::types::Did) -> serde_
|
||||
.flatten();
|
||||
|
||||
let public_key_multibase = match key_info {
|
||||
Some(info) => match crate::config::decrypt_key(&info.key_bytes, info.encryption_version) {
|
||||
Ok(key_bytes) => crate::api::identity::did::get_public_key_multibase(&key_bytes)
|
||||
Some(info) => match tranquil_pds::config::decrypt_key(&info.key_bytes, info.encryption_version) {
|
||||
Ok(key_bytes) => crate::identity::did::get_public_key_multibase(&key_bytes)
|
||||
.unwrap_or_else(|_| "error".to_string()),
|
||||
Err(_) => "error".to_string(),
|
||||
},
|
||||
@@ -243,7 +243,7 @@ async fn build_did_document(state: &AppState, did: &crate::types::Did) -> serde_
|
||||
}],
|
||||
"service": [{
|
||||
"id": "#atproto_pds",
|
||||
"type": crate::plc::ServiceType::Pds.as_str(),
|
||||
"type": tranquil_pds::plc::ServiceType::Pds.as_str(),
|
||||
"serviceEndpoint": service_endpoint
|
||||
}]
|
||||
})
|
||||
+34
-34
@@ -1,6 +1,6 @@
|
||||
use crate::api::SuccessResponse;
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::NormalizedLoginIdentifier;
|
||||
use tranquil_pds::api::SuccessResponse;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::NormalizedLoginIdentifier;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -19,12 +19,12 @@ use tracing::{debug, error, info, warn};
|
||||
use tranquil_db_traits::WebauthnChallengeType;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::api::repo::record::utils::create_signed_commit;
|
||||
use crate::auth::{ServiceTokenVerifier, generate_app_password, is_service_token};
|
||||
use crate::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle, PlainPassword};
|
||||
use crate::validation::validate_password;
|
||||
use tranquil_pds::repo_ops::create_signed_commit;
|
||||
use tranquil_pds::auth::{ServiceTokenVerifier, generate_app_password, is_service_token};
|
||||
use tranquil_pds::rate_limit::{AccountCreationLimit, PasswordResetLimit, RateLimited};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle, PlainPassword};
|
||||
use tranquil_pds::validation::validate_password;
|
||||
|
||||
fn generate_setup_token() -> String {
|
||||
let mut rng = rand::thread_rng();
|
||||
@@ -72,8 +72,8 @@ pub async fn create_passkey_account(
|
||||
headers: HeaderMap,
|
||||
Json(input): Json<CreatePasskeyAccountInput>,
|
||||
) -> Response {
|
||||
let byod_auth = if let Some(extracted) = crate::auth::extract_auth_token_from_header(
|
||||
crate::util::get_header_str(&headers, http::header::AUTHORIZATION),
|
||||
let byod_auth = if let Some(extracted) = tranquil_pds::auth::extract_auth_token_from_header(
|
||||
tranquil_pds::util::get_header_str(&headers, http::header::AUTHORIZATION),
|
||||
) {
|
||||
let token = extracted.token;
|
||||
if is_service_token(&token) {
|
||||
@@ -128,14 +128,14 @@ pub async fn create_passkey_account(
|
||||
.unwrap_or(&input.handle),
|
||||
None => &input.handle,
|
||||
};
|
||||
match crate::api::validation::validate_short_handle(handle_to_validate) {
|
||||
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
|
||||
Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])),
|
||||
Err(_) => {
|
||||
return ApiError::InvalidHandle(None).into_response();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match crate::api::validation::validate_full_domain_handle(&input.handle) {
|
||||
match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) {
|
||||
Ok(h) => h,
|
||||
Err(_) => return ApiError::InvalidHandle(None).into_response(),
|
||||
}
|
||||
@@ -147,7 +147,7 @@ pub async fn create_passkey_account(
|
||||
.map(|e| e.trim().to_string())
|
||||
.filter(|e| !e.is_empty());
|
||||
if let Some(ref email) = email
|
||||
&& !crate::api::validation::is_valid_email(email)
|
||||
&& !tranquil_pds::api::validation::is_valid_email(email)
|
||||
{
|
||||
return ApiError::InvalidEmail.into_response();
|
||||
}
|
||||
@@ -184,7 +184,7 @@ pub async fn create_passkey_account(
|
||||
tranquil_db_traits::CommsChannel::Discord => match &input.discord_username {
|
||||
Some(username) if !username.trim().is_empty() => {
|
||||
let clean = username.trim().to_lowercase();
|
||||
if !crate::api::validation::is_valid_discord_username(&clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_discord_username(&clean) {
|
||||
return ApiError::InvalidRequest(
|
||||
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(),
|
||||
).into_response();
|
||||
@@ -196,7 +196,7 @@ pub async fn create_passkey_account(
|
||||
tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username {
|
||||
Some(username) if !username.trim().is_empty() => {
|
||||
let clean = username.trim().trim_start_matches('@');
|
||||
if !crate::api::validation::is_valid_telegram_username(clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_telegram_username(clean) {
|
||||
return ApiError::InvalidRequest(
|
||||
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(),
|
||||
).into_response();
|
||||
@@ -250,7 +250,7 @@ pub async fn create_passkey_account(
|
||||
|
||||
let did = match did_type {
|
||||
"web" => {
|
||||
if !crate::api::server::meta::is_self_hosted_did_web_enabled() {
|
||||
if !tranquil_pds::util::is_self_hosted_did_web_enabled() {
|
||||
return ApiError::SelfHostedDidWebDisabled.into_response();
|
||||
}
|
||||
let encoded_handle = handle.replace(':', "%3A");
|
||||
@@ -284,7 +284,7 @@ pub async fn create_passkey_account(
|
||||
}
|
||||
info!(did = %d, "Creating external did:web passkey account (BYOD key)");
|
||||
} else {
|
||||
if let Err(e) = crate::api::identity::did::verify_did_web(
|
||||
if let Err(e) = crate::identity::did::verify_did_web(
|
||||
d,
|
||||
hostname,
|
||||
&input.handle,
|
||||
@@ -328,9 +328,9 @@ pub async fn create_passkey_account(
|
||||
.secrets
|
||||
.plc_rotation_key
|
||||
.clone()
|
||||
.unwrap_or_else(|| crate::plc::signing_key_to_did_key(&secret_key));
|
||||
.unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&secret_key));
|
||||
|
||||
let genesis_result = match crate::plc::create_genesis_operation(
|
||||
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
|
||||
&secret_key,
|
||||
&rotation_key,
|
||||
&handle,
|
||||
@@ -346,7 +346,7 @@ pub async fn create_passkey_account(
|
||||
}
|
||||
};
|
||||
|
||||
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
if let Err(e) = plc_client
|
||||
.send_operation(&genesis_result.did, &genesis_result.signed_operation)
|
||||
.await
|
||||
@@ -381,7 +381,7 @@ pub async fn create_passkey_account(
|
||||
None
|
||||
};
|
||||
|
||||
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
|
||||
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => {
|
||||
error!("Error encrypting signing key: {:?}", e);
|
||||
@@ -458,7 +458,7 @@ pub async fn create_passkey_account(
|
||||
setup_expires_at,
|
||||
deactivated_at,
|
||||
encrypted_key_bytes,
|
||||
encryption_version: crate::config::ENCRYPTION_VERSION,
|
||||
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
|
||||
reserved_key_id,
|
||||
commit_cid: commit_cid.to_string(),
|
||||
repo_rev: rev.as_ref().to_string(),
|
||||
@@ -487,7 +487,7 @@ pub async fn create_passkey_account(
|
||||
let user_id = create_result.user_id;
|
||||
|
||||
if !is_byod_did_web {
|
||||
if let Err(e) = crate::api::repo::record::sequence_identity_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_identity_event(
|
||||
&state,
|
||||
&did_typed,
|
||||
Some(&handle_typed),
|
||||
@@ -496,7 +496,7 @@ pub async fn create_passkey_account(
|
||||
{
|
||||
warn!("Failed to sequence identity event for {}: {}", did, e);
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
&did_typed,
|
||||
tranquil_db_traits::AccountStatus::Active,
|
||||
@@ -509,11 +509,11 @@ pub async fn create_passkey_account(
|
||||
"$type": "app.bsky.actor.profile",
|
||||
"displayName": handle
|
||||
});
|
||||
if let Err(e) = crate::api::repo::record::create_record_internal(
|
||||
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
|
||||
&state,
|
||||
&did_typed,
|
||||
&crate::types::PROFILE_COLLECTION,
|
||||
&crate::types::PROFILE_RKEY,
|
||||
&tranquil_pds::types::PROFILE_COLLECTION,
|
||||
&tranquil_pds::types::PROFILE_RKEY,
|
||||
&profile_record,
|
||||
)
|
||||
.await
|
||||
@@ -522,14 +522,14 @@ pub async fn create_passkey_account(
|
||||
}
|
||||
}
|
||||
|
||||
let verification_token = crate::auth::verification_token::generate_signup_token(
|
||||
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
|
||||
&did_typed,
|
||||
verification_channel,
|
||||
&verification_recipient,
|
||||
);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&verification_token);
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
@@ -546,7 +546,7 @@ pub async fn create_passkey_account(
|
||||
info!(did = %did, handle = %handle, "Passkey-only account created, awaiting setup completion");
|
||||
|
||||
let access_jwt = if byod_auth.is_some() {
|
||||
match crate::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
|
||||
match tranquil_pds::auth::create_access_token_with_metadata(&did, &secret_key_bytes) {
|
||||
Ok(token_meta) => {
|
||||
let refresh_jti = uuid::Uuid::new_v4().to_string();
|
||||
let refresh_expires = chrono::Utc::now() + chrono::Duration::hours(24);
|
||||
@@ -887,7 +887,7 @@ pub async fn request_passkey_recovery(
|
||||
urlencoding::encode(&recovery_token)
|
||||
);
|
||||
|
||||
let _ = crate::comms::comms_repo::enqueue_passkey_recovery(
|
||||
let _ = tranquil_pds::comms::comms_repo::enqueue_passkey_recovery(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user.id,
|
||||
@@ -968,7 +968,7 @@ pub async fn recover_passkey_account(
|
||||
}
|
||||
if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user.id).await {
|
||||
let actual_channel =
|
||||
crate::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
|
||||
tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
|
||||
if let Err(e) = state
|
||||
.user_repo
|
||||
.set_channel_verified(&input.did, actual_channel)
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{Active, Auth, require_legacy_session_mfa, require_reauth_window};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{Active, Auth, require_legacy_session_mfa, require_reauth_window};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -271,6 +271,6 @@ pub async fn update_passkey(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn has_passkeys_for_user(state: &AppState, did: &crate::types::Did) -> bool {
|
||||
pub async fn has_passkeys_for_user(state: &AppState, did: &tranquil_pds::types::Did) -> bool {
|
||||
state.user_repo.has_passkeys(did).await.unwrap_or(false)
|
||||
}
|
||||
+12
-12
@@ -1,13 +1,13 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::api::{EmptyResponse, HasPasswordResponse, SuccessResponse};
|
||||
use crate::auth::{
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::api::{EmptyResponse, HasPasswordResponse, SuccessResponse};
|
||||
use tranquil_pds::auth::{
|
||||
Active, Auth, NormalizedLoginIdentifier, require_legacy_session_mfa, require_reauth_window,
|
||||
require_reauth_window_if_available,
|
||||
};
|
||||
use crate::rate_limit::{PasswordResetLimit, RateLimited, ResetPasswordLimit};
|
||||
use crate::state::AppState;
|
||||
use crate::types::PlainPassword;
|
||||
use crate::validation::validate_password;
|
||||
use tranquil_pds::rate_limit::{PasswordResetLimit, RateLimited, ResetPasswordLimit};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::PlainPassword;
|
||||
use tranquil_pds::validation::validate_password;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -19,7 +19,7 @@ use serde::Deserialize;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
fn generate_reset_code() -> String {
|
||||
crate::util::generate_token_code()
|
||||
tranquil_pds::util::generate_token_code()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -78,7 +78,7 @@ pub async fn request_password_reset(
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_password_reset(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_password_reset(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id,
|
||||
@@ -170,7 +170,7 @@ pub async fn reset_password(
|
||||
}
|
||||
};
|
||||
futures::future::join_all(result.session_jtis.iter().map(|jti| {
|
||||
let cache_key = crate::cache_keys::session_key(&result.did, jti);
|
||||
let cache_key = tranquil_pds::cache_keys::session_key(&result.did, jti);
|
||||
let cache = state.cache.clone();
|
||||
async move {
|
||||
if let Err(e) = cache.delete(&cache_key).await {
|
||||
@@ -184,7 +184,7 @@ pub async fn reset_password(
|
||||
.await;
|
||||
if let Ok(Some(prefs)) = state.user_repo.get_comms_prefs(user_id).await {
|
||||
let actual_channel =
|
||||
crate::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
|
||||
tranquil_pds::comms::resolve_delivery_channel(&prefs, user.preferred_comms_channel);
|
||||
if let Err(e) = state
|
||||
.user_repo
|
||||
.set_channel_verified(&user.did, actual_channel)
|
||||
@@ -212,7 +212,7 @@ pub async fn change_password(
|
||||
auth: Auth<Active>,
|
||||
Json(input): Json<ChangePasswordInput>,
|
||||
) -> Result<Response, ApiError> {
|
||||
use crate::auth::verify_password_mfa;
|
||||
use tranquil_pds::auth::verify_password_mfa;
|
||||
|
||||
let session_mfa = match require_legacy_session_mfa(&state, &auth).await {
|
||||
Ok(proof) => proof,
|
||||
+18
-18
@@ -1,4 +1,4 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -10,10 +10,10 @@ use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, info, warn};
|
||||
use tranquil_db_traits::{SessionRepository, UserRepository, WebauthnChallengeType};
|
||||
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
|
||||
use crate::state::AppState;
|
||||
use crate::types::PlainPassword;
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::PlainPassword;
|
||||
|
||||
pub const REAUTH_WINDOW_SECONDS: i64 = 300;
|
||||
|
||||
@@ -125,7 +125,7 @@ pub async fn reauth_totp(
|
||||
.await?;
|
||||
|
||||
let valid =
|
||||
crate::api::server::totp::verify_totp_or_backup_for_user(&state, &auth.did, &input.code)
|
||||
crate::server::totp::verify_totp_or_backup_for_user(&state, &auth.did, &input.code)
|
||||
.await;
|
||||
|
||||
if !valid {
|
||||
@@ -276,11 +276,11 @@ pub async fn reauth_passkey_finish(
|
||||
|
||||
pub async fn update_last_reauth_cached(
|
||||
session_repo: &dyn SessionRepository,
|
||||
cache: &std::sync::Arc<dyn crate::cache::Cache>,
|
||||
did: &crate::types::Did,
|
||||
cache: &std::sync::Arc<dyn tranquil_pds::cache::Cache>,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> Result<DateTime<Utc>, tranquil_db_traits::DbError> {
|
||||
let now = session_repo.update_last_reauth(did).await?;
|
||||
let cache_key = crate::cache_keys::reauth_key(did);
|
||||
let cache_key = tranquil_pds::cache_keys::reauth_key(did);
|
||||
let _ = cache
|
||||
.set(
|
||||
&cache_key,
|
||||
@@ -304,7 +304,7 @@ fn is_reauth_required(last_reauth_at: Option<DateTime<Utc>>) -> bool {
|
||||
async fn get_available_reauth_methods(
|
||||
user_repo: &dyn UserRepository,
|
||||
_session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> Vec<ReauthMethod> {
|
||||
let mut methods = Vec::new();
|
||||
|
||||
@@ -334,7 +334,7 @@ async fn get_available_reauth_methods(
|
||||
|
||||
pub async fn check_reauth_required(
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> bool {
|
||||
match session_repo.get_last_reauth_at(did).await {
|
||||
Ok(last_reauth_at) => is_reauth_required(last_reauth_at),
|
||||
@@ -344,10 +344,10 @@ pub async fn check_reauth_required(
|
||||
|
||||
pub async fn check_reauth_required_cached(
|
||||
session_repo: &dyn SessionRepository,
|
||||
cache: &std::sync::Arc<dyn crate::cache::Cache>,
|
||||
did: &crate::types::Did,
|
||||
cache: &std::sync::Arc<dyn tranquil_pds::cache::Cache>,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> bool {
|
||||
let cache_key = crate::cache_keys::reauth_key(did);
|
||||
let cache_key = tranquil_pds::cache_keys::reauth_key(did);
|
||||
if let Some(timestamp_str) = cache.get(&cache_key).await
|
||||
&& let Ok(timestamp) = timestamp_str.parse::<i64>()
|
||||
{
|
||||
@@ -376,7 +376,7 @@ pub struct ReauthRequiredError {
|
||||
pub async fn reauth_required_response(
|
||||
user_repo: &dyn UserRepository,
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> Response {
|
||||
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
|
||||
(
|
||||
@@ -392,7 +392,7 @@ pub async fn reauth_required_response(
|
||||
|
||||
pub async fn check_legacy_session_mfa(
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> bool {
|
||||
match session_repo.get_session_mfa_status(did).await {
|
||||
Ok(Some(status)) => {
|
||||
@@ -416,7 +416,7 @@ pub async fn check_legacy_session_mfa(
|
||||
|
||||
pub async fn update_mfa_verified(
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> Result<(), tranquil_db_traits::DbError> {
|
||||
session_repo.update_mfa_verified(did).await
|
||||
}
|
||||
@@ -424,7 +424,7 @@ pub async fn update_mfa_verified(
|
||||
pub async fn legacy_mfa_required_response(
|
||||
user_repo: &dyn UserRepository,
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
) -> Response {
|
||||
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
|
||||
(
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::extractor::{Auth, Permissive};
|
||||
use crate::state::AppState;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::extractor::{Auth, Permissive};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
@@ -75,7 +75,7 @@ pub async fn get_service_auth(
|
||||
match state.user_repo.get_user_info_by_did(&auth.did).await {
|
||||
Ok(Some(info)) => match info.key_bytes {
|
||||
Some(key_bytes_enc) => {
|
||||
match crate::config::decrypt_key(&key_bytes_enc, info.encryption_version) {
|
||||
match tranquil_pds::config::decrypt_key(&key_bytes_enc, info.encryption_version) {
|
||||
Ok(key) => key,
|
||||
Err(e) => {
|
||||
error!(error = ?e, "Failed to decrypt user key for service auth");
|
||||
@@ -112,7 +112,7 @@ pub async fn get_service_auth(
|
||||
let lxm_for_token = lxm.map_or("*", |n| n.as_str());
|
||||
|
||||
if let Some(method) = lxm {
|
||||
if let Err(e) = crate::auth::scope_check::check_rpc_scope(
|
||||
if let Err(e) = tranquil_pds::auth::scope_check::check_rpc_scope(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
params.aud.as_str(),
|
||||
@@ -167,7 +167,7 @@ pub async fn get_service_auth(
|
||||
}
|
||||
}
|
||||
|
||||
let service_token = match crate::auth::create_service_token(
|
||||
let service_token = match tranquil_pds::auth::create_service_token(
|
||||
&auth.did,
|
||||
params.aud.as_str(),
|
||||
lxm_for_token,
|
||||
+51
-51
@@ -1,12 +1,12 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::api::{EmptyResponse, SuccessResponse};
|
||||
use crate::auth::{
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::api::{EmptyResponse, SuccessResponse};
|
||||
use tranquil_pds::auth::{
|
||||
Active, Auth, NormalizedLoginIdentifier, Permissive, require_legacy_session_mfa,
|
||||
require_reauth_window,
|
||||
};
|
||||
use crate::rate_limit::{LoginLimit, RateLimited, RefreshSessionLimit};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{AccountState, Did, Handle, PlainPassword};
|
||||
use tranquil_pds::rate_limit::{LoginLimit, RateLimited, RefreshSessionLimit};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{AccountState, Did, Handle, PlainPassword};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -93,7 +93,7 @@ pub async fn create_session(
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let key_bytes = match crate::config::decrypt_key(&row.key_bytes, row.encryption_version) {
|
||||
let key_bytes = match tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
error!("Failed to decrypt user key: {:?}", e);
|
||||
@@ -173,12 +173,12 @@ pub async fn create_session(
|
||||
let has_totp = row.totp_enabled;
|
||||
let email_2fa_enabled = row.email_2fa_enabled;
|
||||
let is_legacy_login = has_totp || email_2fa_enabled;
|
||||
let twofa_ctx = crate::auth::legacy_2fa::Legacy2faContext {
|
||||
let twofa_ctx = tranquil_pds::auth::legacy_2fa::Legacy2faContext {
|
||||
email_2fa_enabled,
|
||||
has_totp,
|
||||
allow_legacy_login: row.allow_legacy_login,
|
||||
};
|
||||
match crate::auth::legacy_2fa::process_legacy_2fa(
|
||||
match tranquil_pds::auth::legacy_2fa::process_legacy_2fa(
|
||||
state.cache.as_ref(),
|
||||
&row.did,
|
||||
&twofa_ctx,
|
||||
@@ -186,14 +186,14 @@ pub async fn create_session(
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::NotRequired) => {}
|
||||
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::Blocked) => {
|
||||
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::NotRequired) => {}
|
||||
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::Blocked) => {
|
||||
warn!("Legacy login blocked for TOTP-enabled account: {}", row.did);
|
||||
return ApiError::LegacyLoginBlocked.into_response();
|
||||
}
|
||||
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => {
|
||||
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::ChallengeSent(code)) => {
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_2fa_code(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_2fa_code(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
row.id,
|
||||
@@ -203,7 +203,7 @@ pub async fn create_session(
|
||||
.await
|
||||
{
|
||||
error!("Failed to send 2FA code: {:?}", e);
|
||||
crate::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &row.did).await;
|
||||
tranquil_pds::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &row.did).await;
|
||||
return ApiError::InternalError(Some(
|
||||
"Failed to send verification code. Please try again.".into(),
|
||||
))
|
||||
@@ -211,9 +211,9 @@ pub async fn create_session(
|
||||
}
|
||||
return ApiError::AuthFactorTokenRequired.into_response();
|
||||
}
|
||||
Ok(crate::auth::legacy_2fa::Legacy2faOutcome::Verified) => {}
|
||||
Err(crate::auth::legacy_2fa::Legacy2faFlowError::Challenge(e)) => {
|
||||
use crate::auth::legacy_2fa::ChallengeError;
|
||||
Ok(tranquil_pds::auth::legacy_2fa::Legacy2faOutcome::Verified) => {}
|
||||
Err(tranquil_pds::auth::legacy_2fa::Legacy2faFlowError::Challenge(e)) => {
|
||||
use tranquil_pds::auth::legacy_2fa::ChallengeError;
|
||||
return match e {
|
||||
ChallengeError::CacheUnavailable => {
|
||||
error!("Cache unavailable for 2FA, blocking legacy login");
|
||||
@@ -232,8 +232,8 @@ pub async fn create_session(
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(crate::auth::legacy_2fa::Legacy2faFlowError::Validation(e)) => {
|
||||
use crate::auth::legacy_2fa::ValidationError;
|
||||
Err(tranquil_pds::auth::legacy_2fa::Legacy2faFlowError::Validation(e)) => {
|
||||
use tranquil_pds::auth::legacy_2fa::ValidationError;
|
||||
warn!("Invalid 2FA code for {}: {:?}", row.did, e);
|
||||
let msg = match e {
|
||||
ValidationError::TooManyAttempts => "Too many attempts. Please request a new code.",
|
||||
@@ -248,7 +248,7 @@ pub async fn create_session(
|
||||
return ApiError::InvalidCode(Some(msg.into())).into_response();
|
||||
}
|
||||
}
|
||||
let access_meta = match crate::auth::create_access_token_with_delegation(
|
||||
let access_meta = match tranquil_pds::auth::create_access_token_with_delegation(
|
||||
&row.did,
|
||||
&key_bytes,
|
||||
app_password_scopes.as_deref(),
|
||||
@@ -261,7 +261,7 @@ pub async fn create_session(
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let refresh_meta = match crate::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
|
||||
let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("Failed to create refresh token: {:?}", e);
|
||||
@@ -297,7 +297,7 @@ pub async fn create_session(
|
||||
"Legacy login on TOTP-enabled account - sending notification"
|
||||
);
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_legacy_login(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_legacy_login(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
row.id,
|
||||
@@ -406,18 +406,18 @@ pub async fn delete_session(
|
||||
headers: axum::http::HeaderMap,
|
||||
_auth: Auth<Active>,
|
||||
) -> Result<Response, ApiError> {
|
||||
let extracted = crate::auth::extract_auth_token_from_header(crate::util::get_header_str(
|
||||
let extracted = tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str(
|
||||
&headers,
|
||||
http::header::AUTHORIZATION,
|
||||
))
|
||||
.ok_or(ApiError::AuthenticationRequired)?;
|
||||
let jti = crate::auth::get_jti_from_token(&extracted.token)
|
||||
let jti = tranquil_pds::auth::get_jti_from_token(&extracted.token)
|
||||
.map_err(|_| ApiError::AuthenticationFailed(None))?;
|
||||
let did = crate::auth::get_did_from_token(&extracted.token).ok();
|
||||
let did = tranquil_pds::auth::get_did_from_token(&extracted.token).ok();
|
||||
match state.session_repo.delete_session_by_access_jti(&jti).await {
|
||||
Ok(rows) if rows > 0 => {
|
||||
if let Some(did) = did {
|
||||
let session_cache_key = crate::cache_keys::session_key(&did, &jti);
|
||||
let session_cache_key = tranquil_pds::cache_keys::session_key(&did, &jti);
|
||||
let _ = state.cache.delete(&session_cache_key).await;
|
||||
}
|
||||
Ok(EmptyResponse::ok().into_response())
|
||||
@@ -432,7 +432,7 @@ pub async fn refresh_session(
|
||||
_rate_limit: RateLimited<RefreshSessionLimit>,
|
||||
headers: axum::http::HeaderMap,
|
||||
) -> Response {
|
||||
let extracted = match crate::auth::extract_auth_token_from_header(crate::util::get_header_str(
|
||||
let extracted = match tranquil_pds::auth::extract_auth_token_from_header(tranquil_pds::util::get_header_str(
|
||||
&headers,
|
||||
http::header::AUTHORIZATION,
|
||||
)) {
|
||||
@@ -440,7 +440,7 @@ pub async fn refresh_session(
|
||||
None => return ApiError::AuthenticationRequired.into_response(),
|
||||
};
|
||||
let refresh_token = extracted.token;
|
||||
let refresh_jti = match crate::auth::get_jti_from_token(&refresh_token) {
|
||||
let refresh_jti = match tranquil_pds::auth::get_jti_from_token(&refresh_token) {
|
||||
Ok(jti) => jti,
|
||||
Err(_) => {
|
||||
return ApiError::AuthenticationFailed(Some("Invalid token format".into()))
|
||||
@@ -473,7 +473,7 @@ pub async fn refresh_session(
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let key_bytes = match crate::config::decrypt_key(
|
||||
let key_bytes = match tranquil_pds::config::decrypt_key(
|
||||
&session_row.key_bytes,
|
||||
Some(session_row.encryption_version),
|
||||
) {
|
||||
@@ -483,11 +483,11 @@ pub async fn refresh_session(
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
if crate::auth::verify_refresh_token(&refresh_token, &key_bytes).is_err() {
|
||||
if tranquil_pds::auth::verify_refresh_token(&refresh_token, &key_bytes).is_err() {
|
||||
return ApiError::AuthenticationFailed(Some("Invalid refresh token".into()))
|
||||
.into_response();
|
||||
}
|
||||
let new_access_meta = match crate::auth::create_access_token_with_delegation(
|
||||
let new_access_meta = match tranquil_pds::auth::create_access_token_with_delegation(
|
||||
&session_row.did,
|
||||
&key_bytes,
|
||||
session_row.scope.as_deref(),
|
||||
@@ -501,7 +501,7 @@ pub async fn refresh_session(
|
||||
}
|
||||
};
|
||||
let new_refresh_meta =
|
||||
match crate::auth::create_refresh_token_with_metadata(&session_row.did, &key_bytes) {
|
||||
match tranquil_pds::auth::create_refresh_token_with_metadata(&session_row.did, &key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("Failed to create refresh token: {:?}", e);
|
||||
@@ -641,8 +641,8 @@ pub async fn confirm_signup(
|
||||
};
|
||||
|
||||
let normalized_token =
|
||||
crate::auth::verification_token::normalize_token_input(&input.verification_code);
|
||||
match crate::auth::verification_token::verify_signup_token(
|
||||
tranquil_pds::auth::verification_token::normalize_token_input(&input.verification_code);
|
||||
match tranquil_pds::auth::verification_token::verify_signup_token(
|
||||
&normalized_token,
|
||||
row.channel,
|
||||
&identifier,
|
||||
@@ -657,7 +657,7 @@ pub async fn confirm_signup(
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
Err(crate::auth::verification_token::VerifyError::Expired) => {
|
||||
Err(tranquil_pds::auth::verification_token::VerifyError::Expired) => {
|
||||
warn!("Verification code expired for user: {}", input.did);
|
||||
return ApiError::ExpiredToken(Some("Verification code has expired".into()))
|
||||
.into_response();
|
||||
@@ -668,7 +668,7 @@ pub async fn confirm_signup(
|
||||
}
|
||||
}
|
||||
|
||||
let key_bytes = match crate::config::decrypt_key(&row.key_bytes, row.encryption_version) {
|
||||
let key_bytes = match tranquil_pds::config::decrypt_key(&row.key_bytes, row.encryption_version) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
error!("Failed to decrypt user key: {:?}", e);
|
||||
@@ -676,14 +676,14 @@ pub async fn confirm_signup(
|
||||
}
|
||||
};
|
||||
|
||||
let access_meta = match crate::auth::create_access_token_with_metadata(&row.did, &key_bytes) {
|
||||
let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&row.did, &key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("Failed to create access token: {:?}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
let refresh_meta = match crate::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
|
||||
let refresh_meta = match tranquil_pds::auth::create_refresh_token_with_metadata(&row.did, &key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("Failed to create refresh token: {:?}", e);
|
||||
@@ -718,7 +718,7 @@ pub async fn confirm_signup(
|
||||
}
|
||||
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_welcome(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
row.id,
|
||||
@@ -749,7 +749,7 @@ pub struct AutoResendResult {
|
||||
}
|
||||
|
||||
pub async fn auto_resend_verification(state: &AppState, did: &Did) -> Option<AutoResendResult> {
|
||||
let debounce_key = crate::cache_keys::auto_verify_sent_key(did.as_str());
|
||||
let debounce_key = tranquil_pds::cache_keys::auto_verify_sent_key(did.as_str());
|
||||
let debounced = state.cache.get(&debounce_key).await.is_some();
|
||||
let row = match state.user_repo.get_resend_verification_by_did(did).await {
|
||||
Ok(Some(row)) => row,
|
||||
@@ -789,11 +789,11 @@ pub async fn auto_resend_verification(state: &AppState, did: &Did) -> Option<Aut
|
||||
return Some(result);
|
||||
}
|
||||
let verification_token =
|
||||
crate::auth::verification_token::generate_signup_token(did, row.channel, &recipient);
|
||||
tranquil_pds::auth::verification_token::generate_signup_token(did, row.channel, &recipient);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&verification_token);
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
row.id,
|
||||
@@ -856,12 +856,12 @@ pub async fn resend_verification(
|
||||
};
|
||||
|
||||
let verification_token =
|
||||
crate::auth::verification_token::generate_signup_token(&input.did, row.channel, &recipient);
|
||||
tranquil_pds::auth::verification_token::generate_signup_token(&input.did, row.channel, &recipient);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&verification_token);
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
|
||||
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
row.id,
|
||||
@@ -910,7 +910,7 @@ pub async fn list_sessions(
|
||||
.get("authorization")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|v| v.strip_prefix("Bearer "))
|
||||
.and_then(|token| crate::auth::get_jti_from_token(token).ok());
|
||||
.and_then(|token| tranquil_pds::auth::get_jti_from_token(token).ok());
|
||||
|
||||
let jwt_rows = state
|
||||
.session_repo
|
||||
@@ -990,7 +990,7 @@ pub async fn revoke_session(
|
||||
.delete_session_by_id(session_id)
|
||||
.await
|
||||
.log_db_err("deleting session")?;
|
||||
let cache_key = crate::cache_keys::session_key(&auth.did, &access_jti);
|
||||
let cache_key = tranquil_pds::cache_keys::session_key(&auth.did, &access_jti);
|
||||
if let Err(e) = state.cache.delete(&cache_key).await {
|
||||
warn!("Failed to invalidate session cache: {:?}", e);
|
||||
}
|
||||
@@ -1020,10 +1020,10 @@ pub async fn revoke_all_sessions(
|
||||
headers: HeaderMap,
|
||||
auth: Auth<Active>,
|
||||
) -> Result<Response, ApiError> {
|
||||
let jti = crate::auth::extract_auth_token_from_header(
|
||||
let jti = tranquil_pds::auth::extract_auth_token_from_header(
|
||||
headers.get("authorization").and_then(|v| v.to_str().ok()),
|
||||
)
|
||||
.and_then(|extracted| crate::auth::get_jti_from_token(&extracted.token).ok())
|
||||
.and_then(|extracted| tranquil_pds::auth::get_jti_from_token(&extracted.token).ok())
|
||||
.ok_or(ApiError::InvalidToken(None))?;
|
||||
|
||||
if auth.is_oauth() {
|
||||
@@ -1119,7 +1119,7 @@ pub async fn update_legacy_login_preference(
|
||||
.into_response())
|
||||
}
|
||||
|
||||
use crate::comms::VALID_LOCALES;
|
||||
use tranquil_pds::comms::VALID_LOCALES;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -25,7 +25,7 @@ fn public_key_to_did_key(signing_key: &SigningKey) -> String {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ReserveSigningKeyInput {
|
||||
pub did: Option<crate::types::Did>,
|
||||
pub did: Option<tranquil_pds::types::Did>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
+10
-10
@@ -1,14 +1,14 @@
|
||||
use crate::api::EmptyResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::auth::{
|
||||
use tranquil_pds::api::EmptyResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::auth::{
|
||||
Active, Auth, decrypt_totp_secret, encrypt_totp_secret, generate_backup_codes,
|
||||
generate_qr_png_base64, generate_totp_secret, generate_totp_uri, hash_backup_code,
|
||||
is_backup_code_format, require_legacy_session_mfa, verify_backup_code, verify_password_mfa,
|
||||
verify_totp_code, verify_totp_mfa,
|
||||
};
|
||||
use crate::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
|
||||
use crate::state::AppState;
|
||||
use crate::types::PlainPassword;
|
||||
use tranquil_pds::rate_limit::{TotpVerifyLimit, check_user_rate_limit_with_message};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::PlainPassword;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -186,7 +186,7 @@ pub async fn disable_totp(
|
||||
.await
|
||||
.log_db_err("deleting TOTP")?;
|
||||
|
||||
crate::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &auth.did).await;
|
||||
tranquil_pds::auth::legacy_2fa::clear_challenge(state.cache.as_ref(), &auth.did).await;
|
||||
|
||||
info!(did = %session_mfa.did(), "TOTP disabled (verified via {} and {})", password_mfa.method(), totp_mfa.method());
|
||||
|
||||
@@ -280,7 +280,7 @@ pub async fn regenerate_backup_codes(
|
||||
|
||||
async fn verify_backup_code_for_user(
|
||||
state: &AppState,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
code: &str,
|
||||
) -> bool {
|
||||
let code = code.trim().to_uppercase();
|
||||
@@ -308,7 +308,7 @@ async fn verify_backup_code_for_user(
|
||||
|
||||
pub async fn verify_totp_or_backup_for_user(
|
||||
state: &AppState,
|
||||
did: &crate::types::Did,
|
||||
did: &tranquil_pds::types::Did,
|
||||
code: &str,
|
||||
) -> bool {
|
||||
use tranquil_db_traits::TotpRecordState;
|
||||
@@ -340,6 +340,6 @@ pub async fn verify_totp_or_backup_for_user(
|
||||
false
|
||||
}
|
||||
|
||||
pub async fn has_totp_enabled(state: &AppState, did: &crate::types::Did) -> bool {
|
||||
pub async fn has_totp_enabled(state: &AppState, did: &tranquil_pds::types::Did) -> bool {
|
||||
state.user_repo.has_totp_enabled(did).await.unwrap_or(false)
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
use crate::api::SuccessResponse;
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::api::SuccessResponse;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -11,8 +11,8 @@ use tracing::{error, info};
|
||||
use tranquil_db_traits::OAuthRepository;
|
||||
use tranquil_types::DeviceId;
|
||||
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
const TRUST_DURATION_DAYS: i64 = 30;
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{Json, extract::State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -71,10 +71,10 @@ pub async fn resend_migration_verification(
|
||||
}
|
||||
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
let token = crate::auth::verification_token::generate_migration_token(&user.did, &email);
|
||||
let formatted_token = crate::auth::verification_token::format_token_for_display(&token);
|
||||
let token = tranquil_pds::auth::verification_token::generate_migration_token(&user.did, &email);
|
||||
let formatted_token = tranquil_pds::auth::verification_token::format_token_for_display(&token);
|
||||
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_migration_verification(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_migration_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user.id,
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
use crate::api::error::{ApiError, DbResultExt};
|
||||
use crate::comms::comms_repo;
|
||||
use crate::types::Did;
|
||||
use tranquil_pds::api::error::{ApiError, DbResultExt};
|
||||
use tranquil_pds::comms::comms_repo;
|
||||
use tranquil_pds::types::Did;
|
||||
use axum::{Json, extract::State};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::auth::verification_token::{
|
||||
use tranquil_pds::auth::verification_token::{
|
||||
VerificationPurpose, normalize_token_input, verify_token_signature,
|
||||
};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_db_traits::CommsChannel;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
@@ -46,7 +46,7 @@ pub async fn verify_token_internal(
|
||||
ApiError::from(e)
|
||||
})?;
|
||||
|
||||
let expected_hash = crate::auth::verification_token::hash_identifier(&identifier);
|
||||
let expected_hash = tranquil_pds::auth::verification_token::hash_identifier(&identifier);
|
||||
if token_data.identifier_hash != expected_hash {
|
||||
return Err(ApiError::IdentifierMismatch);
|
||||
}
|
||||
+2
-2
@@ -6,8 +6,8 @@ use axum::{
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::comms::comms_repo;
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::comms::comms_repo;
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TelegramUpdate {
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::{Active, Auth, Permissive};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::{Active, Auth, Permissive};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -57,7 +57,7 @@ pub async fn dereference_scope(
|
||||
|
||||
for part in scope_parts {
|
||||
if let Some(cid_str) = part.strip_prefix("ref:") {
|
||||
let cache_key = crate::cache_keys::scope_ref_key(cid_str);
|
||||
let cache_key = tranquil_pds::cache_keys::scope_ref_key(cid_str);
|
||||
if let Some(cached) = state.cache.get(&cache_key).await {
|
||||
for s in cached.split_whitespace() {
|
||||
if !resolved_scopes.contains(&s.to_string()) {
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
use crate::api::SuccessResponse;
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::api::SuccessResponse;
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -19,12 +19,12 @@ pub async fn confirm_channel_verification(
|
||||
State(state): State<AppState>,
|
||||
Json(input): Json<ConfirmChannelVerificationInput>,
|
||||
) -> Response {
|
||||
let token_input = crate::api::server::VerifyTokenInput {
|
||||
let token_input = crate::server::VerifyTokenInput {
|
||||
token: input.code,
|
||||
identifier: input.identifier,
|
||||
};
|
||||
|
||||
match crate::api::server::verify_token_internal(&state, token_input).await {
|
||||
match crate::server::verify_token_internal(&state, token_input).await {
|
||||
Ok(_output) => SuccessResponse::ok().into_response(),
|
||||
Err(e) => e.into_response(),
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "tranquil-oauth-server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tranquil-pds = { workspace = true }
|
||||
tranquil-api = { workspace = true }
|
||||
tranquil-types = { workspace = true }
|
||||
tranquil-config = { workspace = true }
|
||||
tranquil-crypto = { workspace = true }
|
||||
tranquil-db-traits = { workspace = true }
|
||||
|
||||
axum = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
bcrypt = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
cid = { workspace = true }
|
||||
hmac = { workspace = true }
|
||||
http = { workspace = true }
|
||||
jacquard-common = { workspace = true }
|
||||
jacquard-repo = { workspace = true }
|
||||
k256 = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde_urlencoded = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
subtle = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
webauthn-rs = { workspace = true }
|
||||
+33
-33
@@ -1,16 +1,16 @@
|
||||
use crate::auth::{BareLoginIdentifier, NormalizedLoginIdentifier};
|
||||
use crate::comms::comms_repo::enqueue_2fa_code;
|
||||
use crate::oauth::{
|
||||
use tranquil_pds::auth::{BareLoginIdentifier, NormalizedLoginIdentifier};
|
||||
use tranquil_pds::comms::comms_repo::enqueue_2fa_code;
|
||||
use tranquil_pds::oauth::{
|
||||
AuthFlow, ClientMetadataCache, Code, DeviceData, DeviceId, OAuthError, Prompt, SessionId,
|
||||
db::should_show_consent, scopes::expand_include_scopes,
|
||||
};
|
||||
use crate::rate_limit::{
|
||||
use tranquil_pds::rate_limit::{
|
||||
OAuthAuthorizeLimit, OAuthRateLimited, OAuthRegisterCompleteLimit, TotpVerifyLimit,
|
||||
check_user_rate_limit,
|
||||
};
|
||||
use crate::state::AppState;
|
||||
use crate::types::{Did, Handle, PlainPassword};
|
||||
use crate::util::extract_client_ip;
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::{Did, Handle, PlainPassword};
|
||||
use tranquil_pds::util::extract_client_ip;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Query, State},
|
||||
@@ -95,7 +95,7 @@ fn extract_device_cookie(headers: &HeaderMap) -> Option<tranquil_types::DeviceId
|
||||
cookie_str.split(';').map(|c| c.trim()).find_map(|cookie| {
|
||||
cookie
|
||||
.strip_prefix(&format!("{}=", DEVICE_COOKIE_NAME))
|
||||
.and_then(|value| crate::config::AuthConfig::get().verify_device_cookie(value))
|
||||
.and_then(|value| tranquil_pds::config::AuthConfig::get().verify_device_cookie(value))
|
||||
.map(tranquil_types::DeviceId::new)
|
||||
})
|
||||
})
|
||||
@@ -109,7 +109,7 @@ fn extract_user_agent(headers: &HeaderMap) -> Option<String> {
|
||||
}
|
||||
|
||||
fn make_device_cookie(device_id: &tranquil_types::DeviceId) -> String {
|
||||
let signed_value = crate::config::AuthConfig::get().sign_device_cookie(device_id.as_str());
|
||||
let signed_value = tranquil_pds::config::AuthConfig::get().sign_device_cookie(device_id.as_str());
|
||||
format!(
|
||||
"{}={}; Path=/oauth; HttpOnly; Secure; SameSite=Lax; Max-Age=31536000",
|
||||
DEVICE_COOKIE_NAME, signed_value
|
||||
@@ -343,7 +343,7 @@ pub async fn authorize_get_json(
|
||||
.oauth_repo
|
||||
.get_authorization_request(&request_id_json)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
|
||||
.ok_or_else(|| OAuthError::InvalidRequest("Invalid or expired request_uri".to_string()))?;
|
||||
if request_data.expires_at < Utc::now() {
|
||||
let _ = state
|
||||
@@ -626,7 +626,7 @@ pub async fn authorize_post(
|
||||
}
|
||||
let is_verified = user.channel_verification.has_any_verified();
|
||||
if !is_verified {
|
||||
let resend_info = crate::api::server::auto_resend_verification(&state, &user.did).await;
|
||||
let resend_info = tranquil_api::server::auto_resend_verification(&state, &user.did).await;
|
||||
let handle = resend_info
|
||||
.as_ref()
|
||||
.map(|r| r.handle.to_string())
|
||||
@@ -653,11 +653,11 @@ pub async fn authorize_post(
|
||||
url_encode("account_not_verified")
|
||||
));
|
||||
}
|
||||
let has_totp = crate::api::server::has_totp_enabled(&state, &user.did).await;
|
||||
let has_totp = tranquil_api::server::has_totp_enabled(&state, &user.did).await;
|
||||
if has_totp {
|
||||
let device_cookie = extract_device_cookie(&headers);
|
||||
let device_is_trusted = if let Some(ref dev_id) = device_cookie {
|
||||
crate::api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &user.did)
|
||||
tranquil_api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &user.did)
|
||||
.await
|
||||
} else {
|
||||
false
|
||||
@@ -665,7 +665,7 @@ pub async fn authorize_post(
|
||||
|
||||
if device_is_trusted {
|
||||
if let Some(ref dev_id) = device_cookie {
|
||||
let _ = crate::api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id)
|
||||
let _ = tranquil_api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id)
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
@@ -978,7 +978,7 @@ pub async fn authorize_select(
|
||||
};
|
||||
let is_verified = user.channel_verification.has_any_verified();
|
||||
if !is_verified {
|
||||
let resend_info = crate::api::server::auto_resend_verification(&state, &did).await;
|
||||
let resend_info = tranquil_api::server::auto_resend_verification(&state, &did).await;
|
||||
return (
|
||||
StatusCode::FORBIDDEN,
|
||||
Json(serde_json::json!({
|
||||
@@ -991,11 +991,11 @@ pub async fn authorize_select(
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
let has_totp = crate::api::server::has_totp_enabled(&state, &did).await;
|
||||
let has_totp = tranquil_api::server::has_totp_enabled(&state, &did).await;
|
||||
let select_early_device_typed = device_id.clone();
|
||||
if has_totp {
|
||||
let device_is_trusted =
|
||||
crate::api::server::is_device_trusted(state.oauth_repo.as_ref(), &device_id, &did)
|
||||
tranquil_api::server::is_device_trusted(state.oauth_repo.as_ref(), &device_id, &did)
|
||||
.await;
|
||||
if !device_is_trusted {
|
||||
if state
|
||||
@@ -1016,7 +1016,7 @@ pub async fn authorize_select(
|
||||
.into_response();
|
||||
}
|
||||
let _ =
|
||||
crate::api::server::extend_device_trust(state.oauth_repo.as_ref(), &device_id).await;
|
||||
tranquil_api::server::extend_device_trust(state.oauth_repo.as_ref(), &device_id).await;
|
||||
}
|
||||
if user.two_factor_enabled {
|
||||
let _ = state
|
||||
@@ -1413,7 +1413,7 @@ pub async fn consent_get(
|
||||
};
|
||||
|
||||
let effective_scope_str = if let Some(ref grant) = delegation_grant {
|
||||
crate::delegation::intersect_scopes(requested_scope_str, grant.granted_scopes.as_str())
|
||||
tranquil_pds::delegation::intersect_scopes(requested_scope_str, grant.granted_scopes.as_str())
|
||||
} else {
|
||||
requested_scope_str.to_string()
|
||||
};
|
||||
@@ -1445,7 +1445,7 @@ pub async fn consent_get(
|
||||
.iter()
|
||||
.map(|scope| {
|
||||
let (category, required, description, display_name) = if let Some(def) =
|
||||
crate::oauth::scopes::SCOPE_DEFINITIONS.get(*scope)
|
||||
tranquil_pds::oauth::scopes::SCOPE_DEFINITIONS.get(*scope)
|
||||
{
|
||||
let desc = if *scope == "atproto" && has_granular_scopes {
|
||||
"AT Protocol baseline scope (permissions determined by selected options below)"
|
||||
@@ -1510,7 +1510,7 @@ pub async fn consent_get(
|
||||
.map(|h| h.to_string());
|
||||
|
||||
let level = if let Some(ref grant) = delegation_grant {
|
||||
let preset = crate::delegation::SCOPE_PRESETS
|
||||
let preset = tranquil_pds::delegation::SCOPE_PRESETS
|
||||
.iter()
|
||||
.find(|p| p.scopes == grant.granted_scopes.as_str());
|
||||
preset
|
||||
@@ -1622,7 +1622,7 @@ pub async fn consent_post(
|
||||
};
|
||||
|
||||
let effective_scope_str = if let Some(ref grant) = delegation_grant {
|
||||
crate::delegation::intersect_scopes(original_scope_str, grant.granted_scopes.as_str())
|
||||
tranquil_pds::delegation::intersect_scopes(original_scope_str, grant.granted_scopes.as_str())
|
||||
} else {
|
||||
original_scope_str.to_string()
|
||||
};
|
||||
@@ -1955,7 +1955,7 @@ pub async fn authorize_2fa_post(
|
||||
);
|
||||
}
|
||||
};
|
||||
if !crate::api::server::has_totp_enabled(&state, &did).await {
|
||||
if !tranquil_api::server::has_totp_enabled(&state, &did).await {
|
||||
return json_error(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"invalid_request",
|
||||
@@ -1973,7 +1973,7 @@ pub async fn authorize_2fa_post(
|
||||
}
|
||||
};
|
||||
let totp_valid =
|
||||
crate::api::server::verify_totp_or_backup_for_user(&state, &did, &form.code).await;
|
||||
tranquil_api::server::verify_totp_or_backup_for_user(&state, &did, &form.code).await;
|
||||
if !totp_valid {
|
||||
return json_error(
|
||||
StatusCode::FORBIDDEN,
|
||||
@@ -2011,7 +2011,7 @@ pub async fn authorize_2fa_post(
|
||||
.oauth_repo
|
||||
.upsert_account_device(&did, &trust_device_id)
|
||||
.await;
|
||||
let _ = crate::api::server::trust_device(state.oauth_repo.as_ref(), &trust_device_id).await;
|
||||
let _ = tranquil_api::server::trust_device(state.oauth_repo.as_ref(), &trust_device_id).await;
|
||||
}
|
||||
let requested_scope_str = request_data
|
||||
.parameters
|
||||
@@ -2110,7 +2110,7 @@ pub async fn check_user_has_passkeys(
|
||||
.await;
|
||||
|
||||
let has_passkeys = match user {
|
||||
Ok(Some(u)) => crate::api::server::has_passkeys_for_user(&state, &u.did).await,
|
||||
Ok(Some(u)) => tranquil_api::server::has_passkeys_for_user(&state, &u.did).await,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -2149,8 +2149,8 @@ pub async fn check_user_security_status(
|
||||
Option<String>,
|
||||
) = match user {
|
||||
Ok(Some(u)) => {
|
||||
let passkeys = crate::api::server::has_passkeys_for_user(&state, &u.did).await;
|
||||
let totp = crate::api::server::has_totp_enabled(&state, &u.did).await;
|
||||
let passkeys = tranquil_api::server::has_passkeys_for_user(&state, &u.did).await;
|
||||
let totp = tranquil_api::server::has_totp_enabled(&state, &u.did).await;
|
||||
let has_pw = u.password_hash.is_some();
|
||||
let has_controllers = state
|
||||
.delegation_repo
|
||||
@@ -2297,7 +2297,7 @@ pub async fn passkey_start(
|
||||
let is_verified = user.channel_verification.has_any_verified();
|
||||
|
||||
if !is_verified {
|
||||
let resend_info = crate::api::server::auto_resend_verification(&state, &user.did).await;
|
||||
let resend_info = tranquil_api::server::auto_resend_verification(&state, &user.did).await;
|
||||
return (
|
||||
StatusCode::FORBIDDEN,
|
||||
Json(serde_json::json!({
|
||||
@@ -3125,14 +3125,14 @@ pub async fn authorize_passkey_finish(
|
||||
if has_totp {
|
||||
let device_cookie = extract_device_cookie(&headers);
|
||||
let device_is_trusted = if let Some(ref dev_id) = device_cookie {
|
||||
crate::api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &did).await
|
||||
tranquil_api::server::is_device_trusted(state.oauth_repo.as_ref(), dev_id, &did).await
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if device_is_trusted {
|
||||
if let Some(ref dev_id) = device_cookie {
|
||||
let _ = crate::api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id)
|
||||
let _ = tranquil_api::server::extend_device_trust(state.oauth_repo.as_ref(), dev_id)
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
@@ -3395,7 +3395,7 @@ pub async fn register_complete(
|
||||
};
|
||||
|
||||
if !is_verified {
|
||||
let resend_info = crate::api::server::auto_resend_verification(&state, &did).await;
|
||||
let resend_info = tranquil_api::server::auto_resend_verification(&state, &did).await;
|
||||
return (
|
||||
StatusCode::FORBIDDEN,
|
||||
Json(serde_json::json!({
|
||||
@@ -3503,7 +3503,7 @@ pub async fn register_complete(
|
||||
pub async fn establish_session(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
auth: crate::auth::Auth<crate::auth::Active>,
|
||||
auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>,
|
||||
) -> Response {
|
||||
let did = &auth.did;
|
||||
|
||||
+8
-8
@@ -1,9 +1,9 @@
|
||||
use crate::auth::{Active, Auth};
|
||||
use crate::delegation::DelegationActionType;
|
||||
use crate::rate_limit::{LoginLimit, OAuthRateLimited, TotpVerifyLimit};
|
||||
use crate::state::AppState;
|
||||
use crate::types::PlainPassword;
|
||||
use crate::util::extract_client_ip;
|
||||
use tranquil_pds::auth::{Active, Auth};
|
||||
use tranquil_pds::delegation::DelegationActionType;
|
||||
use tranquil_pds::rate_limit::{LoginLimit, OAuthRateLimited, TotpVerifyLimit};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_pds::types::PlainPassword;
|
||||
use tranquil_pds::util::extract_client_ip;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::State,
|
||||
@@ -220,7 +220,7 @@ pub async fn delegation_auth(
|
||||
.into_response();
|
||||
}
|
||||
|
||||
let has_totp = crate::api::server::has_totp_enabled(&state, &controller_did).await;
|
||||
let has_totp = tranquil_api::server::has_totp_enabled(&state, &controller_did).await;
|
||||
if has_totp {
|
||||
return Json(DelegationAuthResponse {
|
||||
success: true,
|
||||
@@ -377,7 +377,7 @@ pub async fn delegation_totp_verify(
|
||||
};
|
||||
|
||||
let totp_valid =
|
||||
crate::api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code)
|
||||
tranquil_api::server::verify_totp_or_backup_for_user(&state, &controller_did, &form.code)
|
||||
.await;
|
||||
if !totp_valid {
|
||||
return Json(DelegationAuthResponse {
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::oauth::jwks::{JwkSet, create_jwk_set};
|
||||
use crate::state::AppState;
|
||||
use crate::jwks::{JwkSet, create_jwk_set};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::{Json, extract::State};
|
||||
use http::{HeaderName, header};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -129,8 +129,8 @@ pub async fn oauth_authorization_server(
|
||||
}
|
||||
|
||||
pub async fn oauth_jwks(State(_state): State<AppState>) -> Json<JwkSet> {
|
||||
use crate::config::AuthConfig;
|
||||
use crate::oauth::jwks::Jwk;
|
||||
use tranquil_pds::config::AuthConfig;
|
||||
use crate::jwks::Jwk;
|
||||
let config = AuthConfig::get();
|
||||
let server_key = Jwk {
|
||||
kty: "EC".to_string(),
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
use crate::oauth::{
|
||||
use tranquil_pds::oauth::{
|
||||
AuthorizationRequestParameters, ClientAuth, ClientMetadataCache, CodeChallengeMethod,
|
||||
OAuthError, Prompt, RequestData, RequestId, ResponseMode, ResponseType,
|
||||
scopes::{ParsedScope, parse_scope},
|
||||
};
|
||||
use crate::rate_limit::{OAuthParLimit, OAuthRateLimited};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::rate_limit::{OAuthParLimit, OAuthRateLimited};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::body::Bytes;
|
||||
use axum::{Json, extract::State, http::HeaderMap};
|
||||
use chrono::{Duration, Utc};
|
||||
@@ -118,7 +118,7 @@ pub async fn pushed_authorization_request(
|
||||
.oauth_repo
|
||||
.create_authorization_request(&request_id_typed, &request_data)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
tokio::spawn({
|
||||
let oauth_repo = state.oauth_repo.clone();
|
||||
async move {
|
||||
@@ -159,7 +159,7 @@ fn determine_client_auth(request: &ParRequest) -> Result<ClientAuth, OAuthError>
|
||||
|
||||
fn validate_scope(
|
||||
requested_scope: &Option<String>,
|
||||
client_metadata: &crate::oauth::ClientMetadata,
|
||||
client_metadata: &tranquil_pds::oauth::ClientMetadata,
|
||||
) -> Result<Option<String>, OAuthError> {
|
||||
let scope_str = match requested_scope {
|
||||
Some(s) if !s.is_empty() => s,
|
||||
+11
-11
@@ -2,16 +2,16 @@ use super::helpers::{create_access_token_with_delegation, verify_pkce};
|
||||
use super::types::{
|
||||
RequestClientAuth, TokenGrant, TokenResponse, TokenType, ValidatedTokenRequest,
|
||||
};
|
||||
use crate::config::AuthConfig;
|
||||
use crate::delegation::intersect_scopes;
|
||||
use crate::oauth::{
|
||||
use tranquil_pds::config::AuthConfig;
|
||||
use tranquil_pds::delegation::intersect_scopes;
|
||||
use tranquil_pds::oauth::{
|
||||
AuthFlow, ClientAuth, ClientMetadataCache, DPoPVerifier, OAuthError, RefreshToken, TokenData,
|
||||
TokenId,
|
||||
db::{enforce_token_limit_for_user, lookup_refresh_token},
|
||||
scopes::expand_include_scopes,
|
||||
verify_client_auth,
|
||||
};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::Json;
|
||||
use axum::http::{HeaderMap, Method};
|
||||
use chrono::{Duration, Utc};
|
||||
@@ -50,7 +50,7 @@ pub async fn handle_authorization_code_grant(
|
||||
.oauth_repo
|
||||
.consume_authorization_request_by_code(&auth_code)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
|
||||
.ok_or_else(|| OAuthError::InvalidGrant("Invalid or expired code".to_string()))?;
|
||||
|
||||
let flow = AuthFlow::from_request_data(auth_request)
|
||||
@@ -107,7 +107,7 @@ pub async fn handle_authorization_code_grant(
|
||||
.oauth_repo
|
||||
.check_and_record_dpop_jti(&result.jti)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
|
||||
{
|
||||
return Err(OAuthError::InvalidDpopProof(
|
||||
"DPoP proof has already been used".to_string(),
|
||||
@@ -201,7 +201,7 @@ pub async fn handle_authorization_code_grant(
|
||||
.oauth_repo
|
||||
.create_token(&token_data)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
tracing::info!(
|
||||
did = %did,
|
||||
token_id = %token_id.0,
|
||||
@@ -321,7 +321,7 @@ pub async fn handle_refresh_token_grant(
|
||||
.oauth_repo
|
||||
.delete_token_family(original_token_id)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
return Err(OAuthError::InvalidGrant(
|
||||
"Refresh token reuse detected, token family revoked".to_string(),
|
||||
));
|
||||
@@ -332,7 +332,7 @@ pub async fn handle_refresh_token_grant(
|
||||
.oauth_repo
|
||||
.delete_token_family(db_id)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
return Err(OAuthError::InvalidGrant(
|
||||
"Refresh token has expired".to_string(),
|
||||
));
|
||||
@@ -354,7 +354,7 @@ pub async fn handle_refresh_token_grant(
|
||||
.oauth_repo
|
||||
.check_and_record_dpop_jti(&result.jti)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
|
||||
{
|
||||
return Err(OAuthError::InvalidDpopProof(
|
||||
"DPoP proof has already been used".to_string(),
|
||||
@@ -387,7 +387,7 @@ pub async fn handle_refresh_token_grant(
|
||||
.oauth_repo
|
||||
.rotate_token(db_id, &new_refresh_typed, new_expires_at)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
tracing::info!(
|
||||
did = %token_data.did,
|
||||
new_expires_at = %new_expires_at,
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
use crate::config::AuthConfig;
|
||||
use crate::oauth::OAuthError;
|
||||
use tranquil_pds::config::AuthConfig;
|
||||
use tranquil_pds::oauth::OAuthError;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||
use chrono::Utc;
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
use super::helpers::extract_token_claims;
|
||||
use crate::oauth::OAuthError;
|
||||
use crate::rate_limit::{OAuthIntrospectLimit, OAuthRateLimited};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::oauth::OAuthError;
|
||||
use tranquil_pds::rate_limit::{OAuthIntrospectLimit, OAuthRateLimited};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::extract::State;
|
||||
use axum::http::StatusCode;
|
||||
use axum::{Form, Json};
|
||||
@@ -27,20 +27,20 @@ pub async fn revoke_token(
|
||||
.oauth_repo
|
||||
.get_token_by_refresh_token(&refresh_token)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?
|
||||
{
|
||||
state
|
||||
.oauth_repo
|
||||
.delete_token_family(db_id)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
} else {
|
||||
let token_id = TokenId::from(token.clone());
|
||||
state
|
||||
.oauth_repo
|
||||
.delete_token(&token_id)
|
||||
.await
|
||||
.map_err(crate::oauth::db_err_to_oauth)?;
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
}
|
||||
}
|
||||
Ok(StatusCode::OK)
|
||||
+4
-4
@@ -3,9 +3,9 @@ mod helpers;
|
||||
mod introspect;
|
||||
mod types;
|
||||
|
||||
use crate::oauth::OAuthError;
|
||||
use crate::rate_limit::{OAuthRateLimited, OAuthTokenLimit};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::oauth::OAuthError;
|
||||
use tranquil_pds::rate_limit::{OAuthRateLimited, OAuthTokenLimit};
|
||||
use tranquil_pds::state::AppState;
|
||||
use axum::body::Bytes;
|
||||
use axum::{Json, extract::State, http::HeaderMap};
|
||||
|
||||
@@ -41,7 +41,7 @@ pub async fn token_endpoint(
|
||||
));
|
||||
};
|
||||
let dpop_proof = headers
|
||||
.get(crate::util::HEADER_DPOP)
|
||||
.get(tranquil_pds::util::HEADER_DPOP)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string());
|
||||
let validated = request.validate()?;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use crate::oauth::OAuthError;
|
||||
use tranquil_pds::oauth::OAuthError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -0,0 +1,116 @@
|
||||
pub mod endpoints;
|
||||
pub mod jwks;
|
||||
pub mod sso_endpoints;
|
||||
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
pub fn oauth_routes() -> axum::Router<AppState> {
|
||||
use axum::{middleware, routing::{get, post}};
|
||||
|
||||
axum::Router::new()
|
||||
.route("/jwks", get(endpoints::oauth_jwks))
|
||||
.route("/par", post(endpoints::pushed_authorization_request))
|
||||
.route("/authorize", get(endpoints::authorize_get))
|
||||
.route("/authorize", post(endpoints::authorize_post))
|
||||
.route(
|
||||
"/authorize/accounts",
|
||||
get(endpoints::authorize_accounts),
|
||||
)
|
||||
.route(
|
||||
"/authorize/select",
|
||||
post(endpoints::authorize_select),
|
||||
)
|
||||
.route("/authorize/2fa", get(endpoints::authorize_2fa_get))
|
||||
.route("/authorize/2fa", post(endpoints::authorize_2fa_post))
|
||||
.route(
|
||||
"/authorize/passkey",
|
||||
get(endpoints::authorize_passkey_start),
|
||||
)
|
||||
.route(
|
||||
"/authorize/passkey",
|
||||
post(endpoints::authorize_passkey_finish),
|
||||
)
|
||||
.route(
|
||||
"/passkey/check",
|
||||
get(endpoints::check_user_has_passkeys),
|
||||
)
|
||||
.route(
|
||||
"/security-status",
|
||||
get(endpoints::check_user_security_status),
|
||||
)
|
||||
.route("/passkey/start", post(endpoints::passkey_start))
|
||||
.route("/passkey/finish", post(endpoints::passkey_finish))
|
||||
.route("/authorize/deny", post(endpoints::authorize_deny))
|
||||
.route(
|
||||
"/register/complete",
|
||||
post(endpoints::register_complete),
|
||||
)
|
||||
.route(
|
||||
"/establish-session",
|
||||
post(endpoints::establish_session),
|
||||
)
|
||||
.route("/authorize/consent", get(endpoints::consent_get))
|
||||
.route("/authorize/consent", post(endpoints::consent_post))
|
||||
.route("/authorize/renew", post(endpoints::authorize_renew))
|
||||
.route(
|
||||
"/authorize/redirect",
|
||||
get(endpoints::authorize_redirect),
|
||||
)
|
||||
.route("/delegation/auth", post(endpoints::delegation_auth))
|
||||
.route(
|
||||
"/delegation/auth-token",
|
||||
post(endpoints::delegation_auth_token),
|
||||
)
|
||||
.route(
|
||||
"/delegation/totp",
|
||||
post(endpoints::delegation_totp_verify),
|
||||
)
|
||||
.route("/token", post(endpoints::token_endpoint))
|
||||
.route("/revoke", post(endpoints::revoke_token))
|
||||
.route("/introspect", post(endpoints::introspect_token))
|
||||
.route("/sso/providers", get(sso_endpoints::get_sso_providers))
|
||||
.route("/sso/initiate", post(sso_endpoints::sso_initiate))
|
||||
.route(
|
||||
"/sso/callback",
|
||||
get(sso_endpoints::sso_callback).post(sso_endpoints::sso_callback_post),
|
||||
)
|
||||
.route("/sso/linked", get(sso_endpoints::get_linked_accounts))
|
||||
.route("/sso/unlink", post(sso_endpoints::unlink_account))
|
||||
.route(
|
||||
"/sso/pending-registration",
|
||||
get(sso_endpoints::get_pending_registration),
|
||||
)
|
||||
.route(
|
||||
"/sso/complete-registration",
|
||||
post(sso_endpoints::complete_registration),
|
||||
)
|
||||
.route(
|
||||
"/sso/check-handle-available",
|
||||
get(sso_endpoints::check_handle_available),
|
||||
)
|
||||
.layer(middleware::from_fn(tranquil_pds::oauth::verify::dpop_nonce_middleware))
|
||||
}
|
||||
|
||||
pub fn well_known_oauth_routes() -> axum::Router<AppState> {
|
||||
use axum::routing::get;
|
||||
|
||||
axum::Router::new()
|
||||
.route(
|
||||
"/oauth-protected-resource",
|
||||
get(endpoints::oauth_protected_resource),
|
||||
)
|
||||
.route(
|
||||
"/oauth-authorization-server",
|
||||
get(endpoints::oauth_authorization_server),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn frontend_client_metadata_route() -> axum::Router<AppState> {
|
||||
use axum::routing::get;
|
||||
|
||||
axum::Router::new()
|
||||
.route(
|
||||
"/oauth-client-metadata.json",
|
||||
get(endpoints::frontend_client_metadata),
|
||||
)
|
||||
}
|
||||
+40
-40
@@ -9,15 +9,15 @@ use serde::{Deserialize, Serialize};
|
||||
use tranquil_db_traits::{SsoAction, SsoProviderType};
|
||||
use tranquil_types::RequestId;
|
||||
|
||||
use super::config::SsoConfig;
|
||||
use crate::api::error::ApiError;
|
||||
use crate::auth::extractor::extract_auth_token_from_header;
|
||||
use crate::auth::{generate_app_password, validate_bearer_token_cached};
|
||||
use crate::rate_limit::{
|
||||
use tranquil_pds::sso::SsoConfig;
|
||||
use tranquil_pds::api::error::ApiError;
|
||||
use tranquil_pds::auth::extractor::extract_auth_token_from_header;
|
||||
use tranquil_pds::auth::{generate_app_password, validate_bearer_token_cached};
|
||||
use tranquil_pds::rate_limit::{
|
||||
AccountCreationLimit, RateLimited, SsoCallbackLimit, SsoInitiateLimit, SsoUnlinkLimit,
|
||||
check_user_rate_limit_with_message,
|
||||
};
|
||||
use crate::state::AppState;
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
fn generate_state() -> String {
|
||||
use rand::RngCore;
|
||||
@@ -367,7 +367,7 @@ async fn handle_sso_login(
|
||||
state: &AppState,
|
||||
request_uri: &str,
|
||||
provider: SsoProviderType,
|
||||
user_info: &crate::sso::providers::SsoUserInfo,
|
||||
user_info: &tranquil_pds::sso::providers::SsoUserInfo,
|
||||
) -> Response {
|
||||
let identity = match state
|
||||
.sso_repo
|
||||
@@ -482,7 +482,7 @@ async fn handle_sso_link(
|
||||
state: &AppState,
|
||||
did: tranquil_types::Did,
|
||||
provider: SsoProviderType,
|
||||
user_info: &crate::sso::providers::SsoUserInfo,
|
||||
user_info: &tranquil_pds::sso::providers::SsoUserInfo,
|
||||
) -> Response {
|
||||
let existing = state
|
||||
.sso_repo
|
||||
@@ -555,7 +555,7 @@ async fn handle_sso_register(
|
||||
state: &AppState,
|
||||
request_uri: &str,
|
||||
provider: SsoProviderType,
|
||||
user_info: &crate::sso::providers::SsoUserInfo,
|
||||
user_info: &tranquil_pds::sso::providers::SsoUserInfo,
|
||||
) -> Response {
|
||||
match state
|
||||
.sso_repo
|
||||
@@ -616,7 +616,7 @@ pub struct LinkedAccountsResponse {
|
||||
|
||||
pub async fn get_linked_accounts(
|
||||
State(state): State<AppState>,
|
||||
auth: crate::auth::Auth<crate::auth::Active>,
|
||||
auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>,
|
||||
) -> Result<Json<LinkedAccountsResponse>, ApiError> {
|
||||
let identities = state
|
||||
.sso_repo
|
||||
@@ -651,7 +651,7 @@ pub struct UnlinkAccountResponse {
|
||||
|
||||
pub async fn unlink_account(
|
||||
State(state): State<AppState>,
|
||||
auth: crate::auth::Auth<crate::auth::Active>,
|
||||
auth: tranquil_pds::auth::Auth<tranquil_pds::auth::Active>,
|
||||
Json(input): Json<UnlinkAccountRequest>,
|
||||
) -> Result<Json<UnlinkAccountResponse>, ApiError> {
|
||||
let _rate_limit = check_user_rate_limit_with_message::<SsoUnlinkLimit>(
|
||||
@@ -763,7 +763,7 @@ pub async fn check_handle_available(
|
||||
}));
|
||||
}
|
||||
|
||||
let validated = match crate::api::validation::validate_short_handle(&query.handle) {
|
||||
let validated = match tranquil_pds::api::validation::validate_short_handle(&query.handle) {
|
||||
Ok(h) => h,
|
||||
Err(e) => {
|
||||
return Ok(Json(CheckHandleResponse {
|
||||
@@ -781,7 +781,7 @@ pub async fn check_handle_available(
|
||||
}
|
||||
let domain = query.domain.as_deref().unwrap_or(&available_domains[0]);
|
||||
let full_handle = format!("{}.{}", validated, domain);
|
||||
let handle_typed: crate::types::Handle = match full_handle.parse() {
|
||||
let handle_typed: tranquil_pds::types::Handle = match full_handle.parse() {
|
||||
Ok(h) => h,
|
||||
Err(_) => return Err(ApiError::InvalidHandle(None)),
|
||||
};
|
||||
@@ -879,12 +879,12 @@ pub async fn complete_registration(
|
||||
.unwrap_or(&input.handle),
|
||||
None => &input.handle,
|
||||
};
|
||||
match crate::api::validation::validate_short_handle(handle_to_validate) {
|
||||
match tranquil_pds::api::validation::validate_short_handle(handle_to_validate) {
|
||||
Ok(h) => format!("{}.{}", h, matched_domain.unwrap_or(&available_domains[0])),
|
||||
Err(_) => return Err(ApiError::InvalidHandle(None)),
|
||||
}
|
||||
} else {
|
||||
match crate::api::validation::validate_full_domain_handle(&input.handle) {
|
||||
match tranquil_pds::api::validation::validate_full_domain_handle(&input.handle) {
|
||||
Ok(h) => h,
|
||||
Err(_) => return Err(ApiError::InvalidHandle(None)),
|
||||
}
|
||||
@@ -914,7 +914,7 @@ pub async fn complete_registration(
|
||||
tranquil_db_traits::CommsChannel::Discord => match &input.discord_username {
|
||||
Some(username) if !username.trim().is_empty() => {
|
||||
let clean = username.trim().to_lowercase();
|
||||
if !crate::api::validation::is_valid_discord_username(&clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_discord_username(&clean) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Invalid Discord username. Must be 2-32 lowercase characters (letters, numbers, underscores, periods)".into(),
|
||||
));
|
||||
@@ -926,7 +926,7 @@ pub async fn complete_registration(
|
||||
tranquil_db_traits::CommsChannel::Telegram => match &input.telegram_username {
|
||||
Some(username) if !username.trim().is_empty() => {
|
||||
let clean = username.trim().trim_start_matches('@');
|
||||
if !crate::api::validation::is_valid_telegram_username(clean) {
|
||||
if !tranquil_pds::api::validation::is_valid_telegram_username(clean) {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"Invalid Telegram username. Must be 5-32 characters, alphanumeric or underscore".into(),
|
||||
));
|
||||
@@ -960,7 +960,7 @@ pub async fn complete_registration(
|
||||
if e.len() > 254 {
|
||||
return Err(ApiError::InvalidEmail);
|
||||
}
|
||||
if !crate::api::validation::is_valid_email(e) {
|
||||
if !tranquil_pds::api::validation::is_valid_email(e) {
|
||||
return Err(ApiError::InvalidEmail);
|
||||
}
|
||||
Some(e.clone())
|
||||
@@ -981,7 +981,7 @@ pub async fn complete_registration(
|
||||
None
|
||||
};
|
||||
|
||||
let handle_typed: crate::types::Handle =
|
||||
let handle_typed: tranquil_pds::types::Handle =
|
||||
handle.parse().map_err(|_| ApiError::InvalidHandle(None))?;
|
||||
let reserved = state
|
||||
.user_repo
|
||||
@@ -1008,7 +1008,7 @@ pub async fn complete_registration(
|
||||
|
||||
let did = match did_type {
|
||||
"web" => {
|
||||
if !crate::api::server::meta::is_self_hosted_did_web_enabled() {
|
||||
if !tranquil_pds::util::is_self_hosted_did_web_enabled() {
|
||||
return Err(ApiError::SelfHostedDidWebDisabled);
|
||||
}
|
||||
let encoded_handle = handle.replace(':', "%3A");
|
||||
@@ -1038,9 +1038,9 @@ pub async fn complete_registration(
|
||||
.secrets
|
||||
.plc_rotation_key
|
||||
.clone()
|
||||
.unwrap_or_else(|| crate::plc::signing_key_to_did_key(&signing_key));
|
||||
.unwrap_or_else(|| tranquil_pds::plc::signing_key_to_did_key(&signing_key));
|
||||
|
||||
let genesis_result = match crate::plc::create_genesis_operation(
|
||||
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
|
||||
&signing_key,
|
||||
&rotation_key,
|
||||
&handle,
|
||||
@@ -1055,7 +1055,7 @@ pub async fn complete_registration(
|
||||
}
|
||||
};
|
||||
|
||||
let plc_client = crate::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
let plc_client = tranquil_pds::plc::PlcClient::with_cache(None, Some(state.cache.clone()));
|
||||
if let Err(e) = plc_client
|
||||
.send_operation(&genesis_result.did, &genesis_result.signed_operation)
|
||||
.await
|
||||
@@ -1071,7 +1071,7 @@ pub async fn complete_registration(
|
||||
};
|
||||
tracing::info!(did = %did, handle = %handle, provider = %pending_preview.provider.as_str(), "Created DID for SSO account");
|
||||
|
||||
let encrypted_key_bytes = match crate::config::encrypt_key(&secret_key_bytes) {
|
||||
let encrypted_key_bytes = match tranquil_pds::config::encrypt_key(&secret_key_bytes) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(e) => {
|
||||
tracing::error!("Error encrypting signing key: {:?}", e);
|
||||
@@ -1089,10 +1089,10 @@ pub async fn complete_registration(
|
||||
};
|
||||
|
||||
let rev = Tid::now(LimitedU32::MIN);
|
||||
let did_typed: crate::types::Did = did
|
||||
let did_typed: tranquil_pds::types::Did = did
|
||||
.parse()
|
||||
.map_err(|_| ApiError::InternalError(Some("Invalid DID".into())))?;
|
||||
let (commit_bytes, _sig) = match crate::api::repo::record::utils::create_signed_commit(
|
||||
let (commit_bytes, _sig) = match tranquil_pds::repo_ops::create_signed_commit(
|
||||
&did_typed,
|
||||
mst_root,
|
||||
rev.as_ref(),
|
||||
@@ -1146,7 +1146,7 @@ pub async fn complete_registration(
|
||||
.map(|s| s.trim().trim_start_matches('@').to_lowercase())
|
||||
.filter(|s| !s.is_empty()),
|
||||
encrypted_key_bytes: encrypted_key_bytes.clone(),
|
||||
encryption_version: crate::config::ENCRYPTION_VERSION,
|
||||
encryption_version: tranquil_pds::config::ENCRYPTION_VERSION,
|
||||
commit_cid: commit_cid.to_string(),
|
||||
repo_rev: rev.as_ref().to_string(),
|
||||
genesis_block_cids,
|
||||
@@ -1189,12 +1189,12 @@ pub async fn complete_registration(
|
||||
.await;
|
||||
|
||||
if let Err(e) =
|
||||
crate::api::repo::record::sequence_identity_event(&state, &did_typed, Some(&handle_typed))
|
||||
tranquil_pds::repo_ops::sequence_identity_event(&state, &did_typed, Some(&handle_typed))
|
||||
.await
|
||||
{
|
||||
tracing::warn!("Failed to sequence identity event for {}: {}", did, e);
|
||||
}
|
||||
if let Err(e) = crate::api::repo::record::sequence_account_event(
|
||||
if let Err(e) = tranquil_pds::repo_ops::sequence_account_event(
|
||||
&state,
|
||||
&did_typed,
|
||||
tranquil_db_traits::AccountStatus::Active,
|
||||
@@ -1208,11 +1208,11 @@ pub async fn complete_registration(
|
||||
"$type": "app.bsky.actor.profile",
|
||||
"displayName": handle_typed.as_str()
|
||||
});
|
||||
if let Err(e) = crate::api::repo::record::create_record_internal(
|
||||
if let Err(e) = tranquil_pds::repo_ops::create_record_internal(
|
||||
&state,
|
||||
&did_typed,
|
||||
&crate::types::PROFILE_COLLECTION,
|
||||
&crate::types::PROFILE_RKEY,
|
||||
&tranquil_pds::types::PROFILE_COLLECTION,
|
||||
&tranquil_pds::types::PROFILE_RKEY,
|
||||
&profile_record,
|
||||
)
|
||||
.await
|
||||
@@ -1287,9 +1287,9 @@ pub async fn complete_registration(
|
||||
tracing::info!(did = %did, "Auto-verified email from SSO provider");
|
||||
|
||||
if is_standalone {
|
||||
let key_bytes = match crate::config::decrypt_key(
|
||||
let key_bytes = match tranquil_pds::config::decrypt_key(
|
||||
&encrypted_key_bytes,
|
||||
Some(crate::config::ENCRYPTION_VERSION),
|
||||
Some(tranquil_pds::config::ENCRYPTION_VERSION),
|
||||
) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
@@ -1298,7 +1298,7 @@ pub async fn complete_registration(
|
||||
}
|
||||
};
|
||||
|
||||
let access_meta = match crate::auth::create_access_token_with_metadata(&did, &key_bytes)
|
||||
let access_meta = match tranquil_pds::auth::create_access_token_with_metadata(&did, &key_bytes)
|
||||
{
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
@@ -1307,7 +1307,7 @@ pub async fn complete_registration(
|
||||
}
|
||||
};
|
||||
let refresh_meta =
|
||||
match crate::auth::create_refresh_token_with_metadata(&did, &key_bytes) {
|
||||
match tranquil_pds::auth::create_refresh_token_with_metadata(&did, &key_bytes) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create refresh token: {:?}", e);
|
||||
@@ -1333,7 +1333,7 @@ pub async fn complete_registration(
|
||||
}
|
||||
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_welcome(
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_welcome(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
user_id.unwrap_or(uuid::Uuid::nil()),
|
||||
@@ -1370,14 +1370,14 @@ pub async fn complete_registration(
|
||||
}
|
||||
|
||||
if let Some(uid) = user_id {
|
||||
let verification_token = crate::auth::verification_token::generate_signup_token(
|
||||
let verification_token = tranquil_pds::auth::verification_token::generate_signup_token(
|
||||
&did_typed,
|
||||
verification_channel,
|
||||
&verification_recipient,
|
||||
);
|
||||
let formatted_token =
|
||||
crate::auth::verification_token::format_token_for_display(&verification_token);
|
||||
if let Err(e) = crate::comms::comms_repo::enqueue_signup_verification(
|
||||
tranquil_pds::auth::verification_token::format_token_for_display(&verification_token);
|
||||
if let Err(e) = tranquil_pds::comms::comms_repo::enqueue_signup_verification(
|
||||
state.user_repo.as_ref(),
|
||||
state.infra_repo.as_ref(),
|
||||
uid,
|
||||
@@ -31,8 +31,6 @@ bs58 = { workspace = true }
|
||||
bytes = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
cid = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
ed25519-dalek = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
@@ -75,7 +73,6 @@ tower = { workspace = true }
|
||||
tower-http = { workspace = true }
|
||||
tower-layer = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
webauthn-rs = { workspace = true }
|
||||
@@ -97,4 +94,7 @@ ctor = { workspace = true }
|
||||
testcontainers = { workspace = true }
|
||||
testcontainers-modules = { workspace = true }
|
||||
tranquil-ripple = { workspace = true }
|
||||
tranquil-sync = { workspace = true }
|
||||
tranquil-api = { workspace = true }
|
||||
tranquil-oauth-server = { workspace = true }
|
||||
wiremock = { workspace = true }
|
||||
|
||||
@@ -1,22 +1,8 @@
|
||||
pub mod actor;
|
||||
pub mod admin;
|
||||
pub mod age_assurance;
|
||||
pub mod backup;
|
||||
pub mod delegation;
|
||||
pub mod discord_webhook;
|
||||
pub mod error;
|
||||
pub mod identity;
|
||||
pub mod moderation;
|
||||
pub mod notification_prefs;
|
||||
pub mod proxy;
|
||||
pub mod proxy_client;
|
||||
pub mod repo;
|
||||
pub mod responses;
|
||||
pub mod server;
|
||||
pub mod telegram_webhook;
|
||||
pub mod temp;
|
||||
pub mod validation;
|
||||
pub mod verification;
|
||||
|
||||
pub use error::ApiError;
|
||||
pub use proxy_client::{AtUriParts, proxy_client, validate_at_uri, validate_limit};
|
||||
|
||||
@@ -74,7 +74,7 @@ pub async fn require_legacy_session_mfa<'a>(
|
||||
state: &AppState,
|
||||
user: &'a AuthenticatedUser,
|
||||
) -> Result<MfaVerified<'a>, Response> {
|
||||
use crate::api::server::reauth::{check_legacy_session_mfa, legacy_mfa_required_response};
|
||||
use crate::auth::reauth::{check_legacy_session_mfa, legacy_mfa_required_response};
|
||||
|
||||
if check_legacy_session_mfa(&*state.session_repo, &user.did).await {
|
||||
Ok(MfaVerified::from_session_reauth(user))
|
||||
@@ -87,7 +87,7 @@ pub async fn require_reauth_window<'a>(
|
||||
state: &AppState,
|
||||
user: &'a AuthenticatedUser,
|
||||
) -> Result<MfaVerified<'a>, Response> {
|
||||
use crate::api::server::reauth::{REAUTH_WINDOW_SECONDS, reauth_required_response};
|
||||
use crate::auth::reauth::{REAUTH_WINDOW_SECONDS, reauth_required_response};
|
||||
use chrono::Utc;
|
||||
|
||||
let status = state
|
||||
@@ -117,7 +117,7 @@ pub async fn require_reauth_window_if_available<'a>(
|
||||
state: &AppState,
|
||||
user: &'a AuthenticatedUser,
|
||||
) -> Result<Option<MfaVerified<'a>>, Response> {
|
||||
use crate::api::server::reauth::{check_reauth_required_cached, reauth_required_response};
|
||||
use crate::auth::reauth::{check_reauth_required_cached, reauth_required_response};
|
||||
|
||||
let has_password = state
|
||||
.user_repo
|
||||
|
||||
@@ -13,6 +13,7 @@ use tranquil_db_traits::OAuthRepository;
|
||||
pub mod account_verified;
|
||||
pub mod email_token;
|
||||
pub mod extractor;
|
||||
pub mod reauth;
|
||||
pub mod legacy_2fa;
|
||||
pub mod login_identifier;
|
||||
pub mod mfa_verified;
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
use axum::{
|
||||
Json,
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use chrono::Utc;
|
||||
use serde::Serialize;
|
||||
use tranquil_db_traits::{SessionRepository, UserRepository};
|
||||
|
||||
pub const REAUTH_WINDOW_SECONDS: i64 = 300;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ReauthMethod {
|
||||
Password,
|
||||
Totp,
|
||||
Passkey,
|
||||
}
|
||||
|
||||
fn is_reauth_required(last_reauth_at: Option<chrono::DateTime<Utc>>) -> bool {
|
||||
match last_reauth_at {
|
||||
None => true,
|
||||
Some(t) => {
|
||||
let elapsed = Utc::now().signed_duration_since(t);
|
||||
elapsed.num_seconds() > REAUTH_WINDOW_SECONDS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_available_reauth_methods(
|
||||
user_repo: &dyn UserRepository,
|
||||
_session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
) -> Vec<ReauthMethod> {
|
||||
let mut methods = Vec::new();
|
||||
|
||||
let has_password = user_repo
|
||||
.get_password_hash_by_did(did)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some();
|
||||
|
||||
if has_password {
|
||||
methods.push(ReauthMethod::Password);
|
||||
}
|
||||
|
||||
let has_totp = user_repo.has_totp_enabled(did).await.unwrap_or(false);
|
||||
if has_totp {
|
||||
methods.push(ReauthMethod::Totp);
|
||||
}
|
||||
|
||||
let has_passkeys = user_repo.has_passkeys(did).await.unwrap_or(false);
|
||||
if has_passkeys {
|
||||
methods.push(ReauthMethod::Passkey);
|
||||
}
|
||||
|
||||
methods
|
||||
}
|
||||
|
||||
pub async fn check_reauth_required_cached(
|
||||
session_repo: &dyn SessionRepository,
|
||||
cache: &std::sync::Arc<dyn crate::cache::Cache>,
|
||||
did: &crate::types::Did,
|
||||
) -> bool {
|
||||
let cache_key = crate::cache_keys::reauth_key(did);
|
||||
if let Some(timestamp_str) = cache.get(&cache_key).await
|
||||
&& let Ok(timestamp) = timestamp_str.parse::<i64>()
|
||||
{
|
||||
let reauth_time = chrono::DateTime::from_timestamp(timestamp, 0);
|
||||
if let Some(t) = reauth_time {
|
||||
let elapsed = Utc::now().signed_duration_since(t);
|
||||
if elapsed.num_seconds() <= REAUTH_WINDOW_SECONDS {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
match session_repo.get_last_reauth_at(did).await {
|
||||
Ok(last_reauth_at) => is_reauth_required(last_reauth_at),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ReauthRequiredError {
|
||||
pub error: String,
|
||||
pub message: String,
|
||||
pub reauth_methods: Vec<ReauthMethod>,
|
||||
}
|
||||
|
||||
pub async fn reauth_required_response(
|
||||
user_repo: &dyn UserRepository,
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
) -> Response {
|
||||
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
|
||||
(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
Json(ReauthRequiredError {
|
||||
error: "ReauthRequired".to_string(),
|
||||
message: "Re-authentication required for this action".to_string(),
|
||||
reauth_methods: methods,
|
||||
}),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
||||
pub async fn check_legacy_session_mfa(
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
) -> bool {
|
||||
match session_repo.get_session_mfa_status(did).await {
|
||||
Ok(Some(status)) => {
|
||||
if status.login_type.is_modern() {
|
||||
return true;
|
||||
}
|
||||
if status.mfa_verified {
|
||||
return true;
|
||||
}
|
||||
if let Some(last_reauth) = status.last_reauth_at {
|
||||
let elapsed = chrono::Utc::now().signed_duration_since(last_reauth);
|
||||
if elapsed.num_seconds() <= REAUTH_WINDOW_SECONDS {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MfaVerificationRequiredError {
|
||||
pub error: String,
|
||||
pub message: String,
|
||||
pub reauth_methods: Vec<ReauthMethod>,
|
||||
}
|
||||
|
||||
pub async fn legacy_mfa_required_response(
|
||||
user_repo: &dyn UserRepository,
|
||||
session_repo: &dyn SessionRepository,
|
||||
did: &crate::types::Did,
|
||||
) -> Response {
|
||||
let methods = get_available_reauth_methods(user_repo, session_repo, did).await;
|
||||
(
|
||||
StatusCode::FORBIDDEN,
|
||||
Json(MfaVerificationRequiredError {
|
||||
error: "MfaVerificationRequired".to_string(),
|
||||
message: "This sensitive operation requires MFA verification. Your session was created via a legacy app that doesn't support MFA during login.".to_string(),
|
||||
reauth_methods: methods,
|
||||
}),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::circuit_breaker::CircuitBreaker;
|
||||
use crate::sync::firehose::SequencedEvent;
|
||||
use tranquil_db_traits::SequencedEvent;
|
||||
use reqwest::Client;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
+28
-601
@@ -17,6 +17,7 @@ pub mod oauth;
|
||||
pub mod plc;
|
||||
pub mod rate_limit;
|
||||
pub mod repo;
|
||||
pub mod repo_ops;
|
||||
pub mod repo_write_lock;
|
||||
pub mod scheduled;
|
||||
pub mod sso;
|
||||
@@ -33,7 +34,7 @@ use axum::{
|
||||
extract::DefaultBodyLimit,
|
||||
http::Method,
|
||||
middleware,
|
||||
routing::{get, post},
|
||||
routing::get,
|
||||
};
|
||||
use http::StatusCode;
|
||||
use serde_json::json;
|
||||
@@ -56,496 +57,30 @@ pub const BUILD_VERSION: &str = concat!(
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const BUILD_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub struct ExternalRoutes {
|
||||
pub xrpc: Router<AppState>,
|
||||
pub oauth: Router<AppState>,
|
||||
pub well_known: Router<AppState>,
|
||||
pub extra: Router<AppState>,
|
||||
}
|
||||
|
||||
impl Default for ExternalRoutes {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
xrpc: Router::new(),
|
||||
oauth: Router::new(),
|
||||
well_known: Router::new(),
|
||||
extra: Router::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app(state: AppState) -> Router {
|
||||
let xrpc_router = Router::new()
|
||||
.route("/_health", get(api::server::health))
|
||||
.route(
|
||||
"/com.atproto.server.describeServer",
|
||||
get(api::server::describe_server),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createAccount",
|
||||
post(api::identity::create_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createSession",
|
||||
post(api::server::create_session),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.getSession",
|
||||
get(api::server::get_session),
|
||||
)
|
||||
.route("/_account.listSessions", get(api::server::list_sessions))
|
||||
.route("/_account.revokeSession", post(api::server::revoke_session))
|
||||
.route(
|
||||
"/_account.revokeAllSessions",
|
||||
post(api::server::revoke_all_sessions),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deleteSession",
|
||||
post(api::server::delete_session),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.refreshSession",
|
||||
post(api::server::refresh_session),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.confirmSignup",
|
||||
post(api::server::confirm_signup),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.resendVerification",
|
||||
post(api::server::resend_verification),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.getServiceAuth",
|
||||
get(api::server::get_service_auth),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.resolveHandle",
|
||||
get(api::identity::resolve_handle),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.createRecord",
|
||||
post(api::repo::create_record),
|
||||
)
|
||||
.route("/com.atproto.repo.putRecord", post(api::repo::put_record))
|
||||
.route("/com.atproto.repo.getRecord", get(api::repo::get_record))
|
||||
.route(
|
||||
"/com.atproto.repo.deleteRecord",
|
||||
post(api::repo::delete_record),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.listRecords",
|
||||
get(api::repo::list_records),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.describeRepo",
|
||||
get(api::repo::describe_repo),
|
||||
)
|
||||
.route("/com.atproto.repo.uploadBlob", post(api::repo::upload_blob))
|
||||
.route(
|
||||
"/com.atproto.repo.applyWrites",
|
||||
post(api::repo::apply_writes),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.sync.getLatestCommit",
|
||||
get(sync::get_latest_commit),
|
||||
)
|
||||
.route("/com.atproto.sync.listRepos", get(sync::list_repos))
|
||||
.route("/com.atproto.sync.getBlob", get(sync::get_blob))
|
||||
.route("/com.atproto.sync.listBlobs", get(sync::list_blobs))
|
||||
.route(
|
||||
"/com.atproto.sync.getRepoStatus",
|
||||
get(sync::get_repo_status),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.checkAccountStatus",
|
||||
get(api::server::check_account_status),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.getRecommendedDidCredentials",
|
||||
get(api::identity::get_recommended_did_credentials),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.repo.listMissingBlobs",
|
||||
get(api::repo::list_missing_blobs),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.sync.notifyOfUpdate",
|
||||
post(sync::notify_of_update),
|
||||
)
|
||||
.route("/com.atproto.sync.requestCrawl", post(sync::request_crawl))
|
||||
.route("/com.atproto.sync.getBlocks", get(sync::get_blocks))
|
||||
.route("/com.atproto.sync.getRepo", get(sync::get_repo))
|
||||
.route("/com.atproto.sync.getRecord", get(sync::get_record))
|
||||
.route(
|
||||
"/com.atproto.sync.subscribeRepos",
|
||||
get(sync::subscribe_repos),
|
||||
)
|
||||
.route("/com.atproto.sync.getHead", get(sync::get_head))
|
||||
.route("/com.atproto.sync.getCheckout", get(sync::get_checkout))
|
||||
.route(
|
||||
"/com.atproto.moderation.createReport",
|
||||
post(api::moderation::create_report),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.getAccountInfo",
|
||||
get(api::admin::get_account_info),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.getAccountInfos",
|
||||
get(api::admin::get_account_infos),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.searchAccounts",
|
||||
get(api::admin::search_accounts),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.activateAccount",
|
||||
post(api::server::activate_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deactivateAccount",
|
||||
post(api::server::deactivate_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.requestAccountDelete",
|
||||
post(api::server::request_account_delete),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deleteAccount",
|
||||
post(api::server::delete_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.requestPasswordReset",
|
||||
post(api::server::request_password_reset),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.resetPassword",
|
||||
post(api::server::reset_password),
|
||||
)
|
||||
.route(
|
||||
"/_account.changePassword",
|
||||
post(api::server::change_password),
|
||||
)
|
||||
.route(
|
||||
"/_account.removePassword",
|
||||
post(api::server::remove_password),
|
||||
)
|
||||
.route(
|
||||
"/_account.setPassword",
|
||||
post(api::server::set_password),
|
||||
)
|
||||
.route(
|
||||
"/_account.getPasswordStatus",
|
||||
get(api::server::get_password_status),
|
||||
)
|
||||
.route(
|
||||
"/_account.getReauthStatus",
|
||||
get(api::server::get_reauth_status),
|
||||
)
|
||||
.route(
|
||||
"/_account.reauthPassword",
|
||||
post(api::server::reauth_password),
|
||||
)
|
||||
.route("/_account.reauthTotp", post(api::server::reauth_totp))
|
||||
.route(
|
||||
"/_account.reauthPasskeyStart",
|
||||
post(api::server::reauth_passkey_start),
|
||||
)
|
||||
.route(
|
||||
"/_account.reauthPasskeyFinish",
|
||||
post(api::server::reauth_passkey_finish),
|
||||
)
|
||||
.route(
|
||||
"/_account.getLegacyLoginPreference",
|
||||
get(api::server::get_legacy_login_preference),
|
||||
)
|
||||
.route(
|
||||
"/_account.updateLegacyLoginPreference",
|
||||
post(api::server::update_legacy_login_preference),
|
||||
)
|
||||
.route("/_account.updateLocale", post(api::server::update_locale))
|
||||
.route(
|
||||
"/_account.listTrustedDevices",
|
||||
get(api::server::list_trusted_devices),
|
||||
)
|
||||
.route(
|
||||
"/_account.revokeTrustedDevice",
|
||||
post(api::server::revoke_trusted_device),
|
||||
)
|
||||
.route(
|
||||
"/_account.updateTrustedDevice",
|
||||
post(api::server::update_trusted_device),
|
||||
)
|
||||
.route(
|
||||
"/_account.createPasskeyAccount",
|
||||
post(api::server::create_passkey_account),
|
||||
)
|
||||
.route(
|
||||
"/_account.startPasskeyRegistrationForSetup",
|
||||
post(api::server::start_passkey_registration_for_setup),
|
||||
)
|
||||
.route(
|
||||
"/_account.completePasskeySetup",
|
||||
post(api::server::complete_passkey_setup),
|
||||
)
|
||||
.route(
|
||||
"/_account.requestPasskeyRecovery",
|
||||
post(api::server::request_passkey_recovery),
|
||||
)
|
||||
.route(
|
||||
"/_account.recoverPasskeyAccount",
|
||||
post(api::server::recover_passkey_account),
|
||||
)
|
||||
.route(
|
||||
"/_account.updateDidDocument",
|
||||
post(api::server::update_did_document),
|
||||
)
|
||||
.route(
|
||||
"/_account.getDidDocument",
|
||||
get(api::server::get_did_document),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.requestEmailUpdate",
|
||||
post(api::server::request_email_update),
|
||||
)
|
||||
.route(
|
||||
"/_checkEmailVerified",
|
||||
post(api::server::check_email_verified),
|
||||
)
|
||||
.route(
|
||||
"/_checkChannelVerified",
|
||||
post(api::server::check_channel_verified),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.confirmEmail",
|
||||
post(api::server::confirm_email),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.updateEmail",
|
||||
post(api::server::update_email),
|
||||
)
|
||||
.route(
|
||||
"/_account.authorizeEmailUpdate",
|
||||
get(api::server::authorize_email_update),
|
||||
)
|
||||
.route(
|
||||
"/_account.checkEmailUpdateStatus",
|
||||
get(api::server::check_email_update_status),
|
||||
)
|
||||
.route(
|
||||
"/_account.checkEmailInUse",
|
||||
post(api::server::check_email_in_use),
|
||||
)
|
||||
.route(
|
||||
"/_account.checkCommsChannelInUse",
|
||||
post(api::server::check_comms_channel_in_use),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.reserveSigningKey",
|
||||
post(api::server::reserve_signing_key),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.verifyMigrationEmail",
|
||||
post(api::server::verify_migration_email),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.resendMigrationVerification",
|
||||
post(api::server::resend_migration_verification),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.updateHandle",
|
||||
post(api::identity::update_handle),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.requestPlcOperationSignature",
|
||||
post(api::identity::request_plc_operation_signature),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.signPlcOperation",
|
||||
post(api::identity::sign_plc_operation),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.identity.submitPlcOperation",
|
||||
post(api::identity::submit_plc_operation),
|
||||
)
|
||||
.route(
|
||||
"/_identity.verifyHandleOwnership",
|
||||
post(api::identity::verify_handle_ownership),
|
||||
)
|
||||
.route("/com.atproto.repo.importRepo", post(api::repo::import_repo))
|
||||
.route(
|
||||
"/com.atproto.admin.deleteAccount",
|
||||
post(api::admin::delete_account),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.updateAccountEmail",
|
||||
post(api::admin::update_account_email),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.updateAccountHandle",
|
||||
post(api::admin::update_account_handle),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.updateAccountPassword",
|
||||
post(api::admin::update_account_password),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.listAppPasswords",
|
||||
get(api::server::list_app_passwords),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createAppPassword",
|
||||
post(api::server::create_app_password),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.revokeAppPassword",
|
||||
post(api::server::revoke_app_password),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createInviteCode",
|
||||
post(api::server::create_invite_code),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createInviteCodes",
|
||||
post(api::server::create_invite_codes),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.getAccountInviteCodes",
|
||||
get(api::server::get_account_invite_codes),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.createTotpSecret",
|
||||
post(api::server::create_totp_secret),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.enableTotp",
|
||||
post(api::server::enable_totp),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.disableTotp",
|
||||
post(api::server::disable_totp),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.getTotpStatus",
|
||||
get(api::server::get_totp_status),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.regenerateBackupCodes",
|
||||
post(api::server::regenerate_backup_codes),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.startPasskeyRegistration",
|
||||
post(api::server::start_passkey_registration),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.finishPasskeyRegistration",
|
||||
post(api::server::finish_passkey_registration),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.listPasskeys",
|
||||
get(api::server::list_passkeys),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.deletePasskey",
|
||||
post(api::server::delete_passkey),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.server.updatePasskey",
|
||||
post(api::server::update_passkey),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.getInviteCodes",
|
||||
get(api::admin::get_invite_codes),
|
||||
)
|
||||
.route("/_admin.getServerStats", get(api::admin::get_server_stats))
|
||||
.route("/_server.getConfig", get(api::admin::get_server_config))
|
||||
.route(
|
||||
"/_admin.updateServerConfig",
|
||||
post(api::admin::update_server_config),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.disableAccountInvites",
|
||||
post(api::admin::disable_account_invites),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.enableAccountInvites",
|
||||
post(api::admin::enable_account_invites),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.disableInviteCodes",
|
||||
post(api::admin::disable_invite_codes),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.getSubjectStatus",
|
||||
get(api::admin::get_subject_status),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.admin.updateSubjectStatus",
|
||||
post(api::admin::update_subject_status),
|
||||
)
|
||||
.route("/com.atproto.admin.sendEmail", post(api::admin::send_email))
|
||||
.route(
|
||||
"/app.bsky.actor.getPreferences",
|
||||
get(api::actor::get_preferences),
|
||||
)
|
||||
.route(
|
||||
"/app.bsky.actor.putPreferences",
|
||||
post(api::actor::put_preferences),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.temp.checkSignupQueue",
|
||||
get(api::temp::check_signup_queue),
|
||||
)
|
||||
.route(
|
||||
"/com.atproto.temp.dereferenceScope",
|
||||
post(api::temp::dereference_scope),
|
||||
)
|
||||
.route(
|
||||
"/_account.getNotificationPrefs",
|
||||
get(api::notification_prefs::get_notification_prefs),
|
||||
)
|
||||
.route(
|
||||
"/_account.updateNotificationPrefs",
|
||||
post(api::notification_prefs::update_notification_prefs),
|
||||
)
|
||||
.route(
|
||||
"/_account.getNotificationHistory",
|
||||
get(api::notification_prefs::get_notification_history),
|
||||
)
|
||||
.route(
|
||||
"/_account.confirmChannelVerification",
|
||||
post(api::verification::confirm_channel_verification),
|
||||
)
|
||||
.route("/_account.verifyToken", post(api::server::verify_token))
|
||||
.route(
|
||||
"/_delegation.listControllers",
|
||||
get(api::delegation::list_controllers),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.addController",
|
||||
post(api::delegation::add_controller),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.removeController",
|
||||
post(api::delegation::remove_controller),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.updateControllerScopes",
|
||||
post(api::delegation::update_controller_scopes),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.listControlledAccounts",
|
||||
get(api::delegation::list_controlled_accounts),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.getAuditLog",
|
||||
get(api::delegation::get_audit_log),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.getScopePresets",
|
||||
get(api::delegation::get_scope_presets),
|
||||
)
|
||||
.route(
|
||||
"/_delegation.createDelegatedAccount",
|
||||
post(api::delegation::create_delegated_account),
|
||||
)
|
||||
.route("/_backup.listBackups", get(api::backup::list_backups))
|
||||
.route("/_backup.getBackup", get(api::backup::get_backup))
|
||||
.route("/_backup.createBackup", post(api::backup::create_backup))
|
||||
.route("/_backup.deleteBackup", post(api::backup::delete_backup))
|
||||
.route("/_backup.setEnabled", post(api::backup::set_backup_enabled))
|
||||
.route("/_backup.exportBlobs", get(api::backup::export_blobs))
|
||||
.route(
|
||||
"/app.bsky.ageassurance.getState",
|
||||
get(api::age_assurance::get_state),
|
||||
)
|
||||
.route(
|
||||
"/app.bsky.unspecced.getAgeAssuranceState",
|
||||
get(api::age_assurance::get_age_assurance_state),
|
||||
)
|
||||
app_with_routes(state, ExternalRoutes::default())
|
||||
}
|
||||
|
||||
pub fn app_with_routes(state: AppState, external: ExternalRoutes) -> Router {
|
||||
let xrpc_router = external.xrpc
|
||||
.fallback(async || (
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
Json(json!({"error": "MethodNotImplemented", "message": "Method not implemented. For app.bsky.* methods, include an atproto-proxy header specifying your AppView."})),
|
||||
@@ -558,120 +93,16 @@ pub fn app(state: AppState) -> Router {
|
||||
.with_state(state.clone()),
|
||||
);
|
||||
|
||||
let oauth_router = Router::new()
|
||||
.route("/jwks", get(oauth::endpoints::oauth_jwks))
|
||||
.route("/par", post(oauth::endpoints::pushed_authorization_request))
|
||||
.route("/authorize", get(oauth::endpoints::authorize_get))
|
||||
.route("/authorize", post(oauth::endpoints::authorize_post))
|
||||
.route(
|
||||
"/authorize/accounts",
|
||||
get(oauth::endpoints::authorize_accounts),
|
||||
)
|
||||
.route(
|
||||
"/authorize/select",
|
||||
post(oauth::endpoints::authorize_select),
|
||||
)
|
||||
.route("/authorize/2fa", get(oauth::endpoints::authorize_2fa_get))
|
||||
.route("/authorize/2fa", post(oauth::endpoints::authorize_2fa_post))
|
||||
.route(
|
||||
"/authorize/passkey",
|
||||
get(oauth::endpoints::authorize_passkey_start),
|
||||
)
|
||||
.route(
|
||||
"/authorize/passkey",
|
||||
post(oauth::endpoints::authorize_passkey_finish),
|
||||
)
|
||||
.route(
|
||||
"/passkey/check",
|
||||
get(oauth::endpoints::check_user_has_passkeys),
|
||||
)
|
||||
.route(
|
||||
"/security-status",
|
||||
get(oauth::endpoints::check_user_security_status),
|
||||
)
|
||||
.route("/passkey/start", post(oauth::endpoints::passkey_start))
|
||||
.route("/passkey/finish", post(oauth::endpoints::passkey_finish))
|
||||
.route("/authorize/deny", post(oauth::endpoints::authorize_deny))
|
||||
.route(
|
||||
"/register/complete",
|
||||
post(oauth::endpoints::register_complete),
|
||||
)
|
||||
.route(
|
||||
"/establish-session",
|
||||
post(oauth::endpoints::establish_session),
|
||||
)
|
||||
.route("/authorize/consent", get(oauth::endpoints::consent_get))
|
||||
.route("/authorize/consent", post(oauth::endpoints::consent_post))
|
||||
.route("/authorize/renew", post(oauth::endpoints::authorize_renew))
|
||||
.route(
|
||||
"/authorize/redirect",
|
||||
get(oauth::endpoints::authorize_redirect),
|
||||
)
|
||||
.route("/delegation/auth", post(oauth::endpoints::delegation_auth))
|
||||
.route(
|
||||
"/delegation/auth-token",
|
||||
post(oauth::endpoints::delegation_auth_token),
|
||||
)
|
||||
.route(
|
||||
"/delegation/totp",
|
||||
post(oauth::endpoints::delegation_totp_verify),
|
||||
)
|
||||
.route("/token", post(oauth::endpoints::token_endpoint))
|
||||
.route("/revoke", post(oauth::endpoints::revoke_token))
|
||||
.route("/introspect", post(oauth::endpoints::introspect_token))
|
||||
.route("/sso/providers", get(sso::endpoints::get_sso_providers))
|
||||
.route("/sso/initiate", post(sso::endpoints::sso_initiate))
|
||||
.route(
|
||||
"/sso/callback",
|
||||
get(sso::endpoints::sso_callback).post(sso::endpoints::sso_callback_post),
|
||||
)
|
||||
.route("/sso/linked", get(sso::endpoints::get_linked_accounts))
|
||||
.route("/sso/unlink", post(sso::endpoints::unlink_account))
|
||||
.route(
|
||||
"/sso/pending-registration",
|
||||
get(sso::endpoints::get_pending_registration),
|
||||
)
|
||||
.route(
|
||||
"/sso/complete-registration",
|
||||
post(sso::endpoints::complete_registration),
|
||||
)
|
||||
.route(
|
||||
"/sso/check-handle-available",
|
||||
get(sso::endpoints::check_handle_available),
|
||||
)
|
||||
.layer(middleware::from_fn(oauth::verify::dpop_nonce_middleware));
|
||||
let oauth_router = external.oauth;
|
||||
|
||||
let well_known_router = Router::new()
|
||||
.route("/did.json", get(api::identity::well_known_did))
|
||||
.route("/atproto-did", get(api::identity::well_known_atproto_did))
|
||||
.route(
|
||||
"/oauth-protected-resource",
|
||||
get(oauth::endpoints::oauth_protected_resource),
|
||||
)
|
||||
.route(
|
||||
"/oauth-authorization-server",
|
||||
get(oauth::endpoints::oauth_authorization_server),
|
||||
);
|
||||
let well_known_router = external.well_known;
|
||||
|
||||
let router = Router::new()
|
||||
.nest_service("/xrpc", xrpc_service)
|
||||
.nest("/oauth", oauth_router)
|
||||
.nest("/.well-known", well_known_router)
|
||||
.route("/metrics", get(metrics::metrics_handler))
|
||||
.route("/health", get(api::server::health))
|
||||
.route("/robots.txt", get(api::server::robots_txt))
|
||||
.route("/favicon.ico", get(api::server::get_logo))
|
||||
.route("/u/{handle}/did.json", get(api::identity::user_did_doc))
|
||||
.route(
|
||||
"/webhook/telegram",
|
||||
post(api::telegram_webhook::handle_telegram_webhook)
|
||||
.layer(DefaultBodyLimit::max(64 * 1024)),
|
||||
)
|
||||
.route(
|
||||
"/webhook/discord",
|
||||
post(api::discord_webhook::handle_discord_webhook)
|
||||
.layer(DefaultBodyLimit::max(64 * 1024)),
|
||||
)
|
||||
.merge(external.extra)
|
||||
.layer(DefaultBodyLimit::max(
|
||||
tranquil_config::get().server.max_blob_size as usize,
|
||||
))
|
||||
@@ -717,10 +148,6 @@ pub fn app(state: AppState) -> Router {
|
||||
let serve_dir = ServeDir::new(frontend_dir).not_found_service(ServeFile::new(&index_path));
|
||||
|
||||
return router
|
||||
.route(
|
||||
"/oauth-client-metadata.json",
|
||||
get(oauth::endpoints::frontend_client_metadata),
|
||||
)
|
||||
.route_service("/", ServeFile::new(&homepage_file))
|
||||
.nest("/app", spa_router)
|
||||
.fallback_service(serve_dir);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
pub mod db;
|
||||
pub mod endpoints;
|
||||
pub mod jwks;
|
||||
pub mod scopes;
|
||||
pub mod verify;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod config;
|
||||
pub mod endpoints;
|
||||
pub mod providers;
|
||||
|
||||
pub use config::SsoConfig;
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::repo::PostgresBlockStore;
|
||||
use crate::repo_write_lock::RepoWriteLocks;
|
||||
use crate::sso::{SsoConfig, SsoManager};
|
||||
use crate::storage::{BackupStorage, BlobStorage, create_backup_storage, create_blob_storage};
|
||||
use crate::sync::firehose::SequencedEvent;
|
||||
use tranquil_db_traits::SequencedEvent;
|
||||
use sqlx::PgPool;
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
@@ -240,7 +240,7 @@ impl AppState {
|
||||
.await,
|
||||
) {
|
||||
(true, Ok(Some(0))) => {
|
||||
let code = crate::api::server::invite::gen_invite_code();
|
||||
let code = crate::util::gen_invite_code();
|
||||
tracing::info!(
|
||||
"No users exist and invite codes are required. Bootstrap invite code: {}",
|
||||
code
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
pub mod blob;
|
||||
pub mod car;
|
||||
pub mod commit;
|
||||
pub mod crawl;
|
||||
pub mod deprecated;
|
||||
pub mod firehose;
|
||||
pub mod frame;
|
||||
pub mod import;
|
||||
pub mod listener;
|
||||
pub mod repo;
|
||||
pub mod subscribe_repos;
|
||||
pub mod util;
|
||||
pub mod verify;
|
||||
|
||||
pub use blob::{get_blob, list_blobs};
|
||||
pub use commit::{get_latest_commit, get_repo_status, list_repos};
|
||||
pub use crawl::{notify_of_update, request_crawl};
|
||||
pub use deprecated::{get_checkout, get_head};
|
||||
pub use repo::{get_blocks, get_record, get_repo};
|
||||
pub use subscribe_repos::subscribe_repos;
|
||||
pub use tranquil_db_traits::AccountStatus;
|
||||
#[cfg(test)]
|
||||
mod verify_tests;
|
||||
|
||||
pub use firehose::SequencedEvent;
|
||||
pub use util::{
|
||||
RepoAccessLevel, RepoAccount, RepoAvailabilityError, assert_repo_availability,
|
||||
get_account_with_status,
|
||||
|
||||
@@ -130,7 +130,7 @@ impl CarVerifier {
|
||||
.ok_or(VerifyError::NoSigningKey)
|
||||
}
|
||||
|
||||
async fn resolve_did_document(&self, did: &Did) -> Result<DidDocument<'static>, VerifyError> {
|
||||
pub(crate) async fn resolve_did_document(&self, did: &Did) -> Result<DidDocument<'static>, VerifyError> {
|
||||
let did_str = did.as_str();
|
||||
if did_str.starts_with("did:plc:") {
|
||||
self.resolve_plc_did(did_str).await
|
||||
@@ -197,7 +197,7 @@ impl CarVerifier {
|
||||
Ok(doc.into_static())
|
||||
}
|
||||
|
||||
fn verify_mst_structure(
|
||||
pub(crate) fn verify_mst_structure(
|
||||
&self,
|
||||
data_cid: &Cid,
|
||||
blocks: &HashMap<Cid, Bytes>,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::sync::verify::{CarVerifier, VerifyError};
|
||||
use super::{CarVerifier, VerifyError};
|
||||
use bytes::Bytes;
|
||||
use cid::Cid;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
@@ -535,3 +535,24 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_invite_random_token() -> String {
|
||||
let mut rng = rand::thread_rng();
|
||||
let chars: Vec<char> = BASE32_ALPHABET.chars().collect();
|
||||
let gen_segment = |rng: &mut rand::rngs::ThreadRng, len: usize| -> String {
|
||||
(0..len)
|
||||
.map(|_| chars[rng.gen_range(0..chars.len())])
|
||||
.collect()
|
||||
};
|
||||
format!("{}-{}", gen_segment(&mut rng, 5), gen_segment(&mut rng, 5))
|
||||
}
|
||||
|
||||
pub fn gen_invite_code() -> String {
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
let hostname_prefix = hostname.replace('.', "-");
|
||||
format!("{}-{}", hostname_prefix, gen_invite_random_token())
|
||||
}
|
||||
|
||||
pub fn is_self_hosted_did_web_enabled() -> bool {
|
||||
tranquil_config::get().server.enable_pds_hosted_did_web
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ fn test_unsigned_commit_has_5_fields() {
|
||||
|
||||
#[test]
|
||||
fn test_create_signed_commit_helper() {
|
||||
use tranquil_pds::api::repo::record::utils::create_signed_commit;
|
||||
use tranquil_pds::repo_ops::create_signed_commit;
|
||||
|
||||
let signing_key = SigningKey::random(&mut rand::thread_rng());
|
||||
let did: Did = "did:plc:testuser123456789abcdef"
|
||||
|
||||
@@ -563,8 +563,19 @@ async fn spawn_server(config: ServerConfig) -> ServerInstance {
|
||||
if let Some((cache, distributed_rate_limiter)) = config.cache {
|
||||
state = state.with_cache(cache, distributed_rate_limiter);
|
||||
}
|
||||
tranquil_pds::sync::listener::start_sequencer_listener(state.clone()).await;
|
||||
let app = tranquil_pds::app(state);
|
||||
tranquil_sync::listener::start_sequencer_listener(state.clone()).await;
|
||||
let app = tranquil_pds::app_with_routes(
|
||||
state,
|
||||
tranquil_pds::ExternalRoutes {
|
||||
xrpc: tranquil_api::api_routes().merge(tranquil_sync::sync_routes()),
|
||||
oauth: tranquil_oauth_server::oauth_routes(),
|
||||
well_known: tranquil_oauth_server::well_known_oauth_routes()
|
||||
.merge(tranquil_api::well_known_api_routes()),
|
||||
extra: tranquil_api::misc_routes()
|
||||
.merge(tranquil_api::webhook_routes())
|
||||
.merge(tranquil_oauth_server::frontend_client_metadata_route()),
|
||||
},
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "tranquil-server"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tranquil-pds = { workspace = true }
|
||||
tranquil-sync = { workspace = true }
|
||||
tranquil-api = { workspace = true }
|
||||
tranquil-oauth-server = { workspace = true }
|
||||
tranquil-config = { workspace = true }
|
||||
|
||||
axum = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
dotenvy = { workspace = true }
|
||||
ed25519-dalek = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio-util = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["frontend", "s3", "valkey"]
|
||||
frontend = ["tranquil-pds/frontend"]
|
||||
s3 = ["tranquil-pds/s3"]
|
||||
valkey = ["tranquil-pds/valkey"]
|
||||
@@ -0,0 +1,12 @@
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let timestamp = Command::new("date")
|
||||
.arg("+%Y-%m-%d %H:%M:%S UTC")
|
||||
.output()
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
|
||||
.unwrap_or_else(|_| "unknown".to_string());
|
||||
|
||||
println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp);
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user