mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-07-19 22:42:25 +00:00
db: newtype Rkey and final newtype touches for now
Lewis: May this revision serve well! <lu5a@proton.me>
This commit is contained in:
@@ -204,7 +204,7 @@ pub async fn get_account_infos(
|
||||
let accounts = state
|
||||
.repos
|
||||
.infra
|
||||
.get_admin_account_infos_by_dids(&dids_typed)
|
||||
.get_admin_account_infos_by_dids(&dids)
|
||||
.await
|
||||
.log_db_err("fetching account infos")?;
|
||||
|
||||
|
||||
@@ -29,10 +29,8 @@ pub async fn disable_invite_codes(
|
||||
{
|
||||
error!("DB error disabling invite codes: {:?}", e);
|
||||
}
|
||||
if let Some(accounts) = &input.accounts {
|
||||
let accounts_typed: Vec<tranquil_types::Did> =
|
||||
accounts.iter().filter_map(|a| a.parse().ok()).collect();
|
||||
if let Err(e) = state
|
||||
if let Some(accounts) = &input.accounts
|
||||
&& let Err(e) = state
|
||||
.repos
|
||||
.infra
|
||||
.disable_invite_codes_by_account(accounts)
|
||||
|
||||
@@ -219,7 +219,7 @@ pub async fn verify_credential(
|
||||
password_hash: Option<&PasswordHash>,
|
||||
) -> Option<CredentialMatch> {
|
||||
let main_valid = password_hash
|
||||
.map(|h| bcrypt::verify(password, h).unwrap_or(false))
|
||||
.map(|h| bcrypt::verify(password, h.as_str()).unwrap_or(false))
|
||||
.unwrap_or(false);
|
||||
if main_valid {
|
||||
return Some(CredentialMatch::MainPassword);
|
||||
|
||||
@@ -471,8 +471,6 @@ pub async fn resolve_controller(
|
||||
Some(user) => user.did,
|
||||
None => tranquil_pds::handle::resolve_handle(&handle)
|
||||
.await
|
||||
.map_err(|_| ApiError::ControllerNotFound)?
|
||||
.parse()
|
||||
.map_err(|_| ApiError::ControllerNotFound)?,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -204,7 +204,7 @@ pub async fn create_account(
|
||||
let verifier = ServiceTokenVerifier::new();
|
||||
let create_account_lxm = Nsid::from("com.atproto.server.createAccount".to_string());
|
||||
match verifier
|
||||
.verify_service_token(&token, Some("com.atproto.server.createAccount"))
|
||||
.verify_service_token(&token, Some(&create_account_lxm))
|
||||
.await
|
||||
{
|
||||
Ok(claims) => {
|
||||
@@ -301,7 +301,7 @@ pub async fn create_account(
|
||||
};
|
||||
let hostname = &cfg.server.hostname;
|
||||
let key_result =
|
||||
match super::provision::resolve_signing_key(&state, input.signing_key.as_deref()).await {
|
||||
match super::provision::resolve_signing_key(&state, input.signing_key.as_ref()).await {
|
||||
Ok(k) => k,
|
||||
Err(e) => return e.into_response(),
|
||||
};
|
||||
@@ -339,13 +339,22 @@ pub async fn create_account(
|
||||
return ApiError::InvalidDid(e.to_string()).into_response();
|
||||
}
|
||||
info!(did = %d, "Creating external did:web account");
|
||||
d.clone()
|
||||
match d.parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => return ApiError::InvalidDid("Invalid DID format".into()).into_response(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(d) = &input.did {
|
||||
if d.starts_with("did:plc:") && is_migration {
|
||||
info!(did = %d, "Migration with existing did:plc");
|
||||
d.clone()
|
||||
match d.parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => {
|
||||
return ApiError::InvalidDid("Invalid DID format".into())
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
} else if d.starts_with("did:web:") {
|
||||
if !is_did_web_byod
|
||||
&& let Err(e) =
|
||||
@@ -354,7 +363,13 @@ pub async fn create_account(
|
||||
{
|
||||
return ApiError::InvalidDid(e.to_string()).into_response();
|
||||
}
|
||||
d.clone()
|
||||
match d.parse() {
|
||||
Ok(d) => d,
|
||||
Err(_) => {
|
||||
return ApiError::InvalidDid("Invalid DID format".into())
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
} else if !d.trim().is_empty() {
|
||||
return ApiError::InvalidDid(
|
||||
"Only did:web DIDs can be provided; leave empty for did:plc. For migration with existing did:plc, provide service auth.".into()
|
||||
@@ -528,7 +543,6 @@ pub async fn create_account(
|
||||
let session = match super::provision::create_and_store_session(
|
||||
&state,
|
||||
&did,
|
||||
&did_for_commit,
|
||||
&secret_key_bytes,
|
||||
"transition:generic transition:chat.bsky",
|
||||
None,
|
||||
|
||||
@@ -167,7 +167,7 @@ async fn serve_handle_did_doc(state: &AppState, handle: &str, hostname: &str) ->
|
||||
let user = match state
|
||||
.repos
|
||||
.user
|
||||
.get_user_for_did_doc_build(&expected_did_typed)
|
||||
.get_user_for_did_doc_build(&expected_did)
|
||||
.await
|
||||
{
|
||||
Ok(Some(u)) => u,
|
||||
@@ -221,7 +221,7 @@ pub async fn user_did_doc(State(state): State<AppState>, Path(handle): Path<Stri
|
||||
let user = match state
|
||||
.repos
|
||||
.user
|
||||
.get_did_web_info_by_handle(¤t_handle_typed)
|
||||
.get_did_web_info_by_handle(¤t_handle)
|
||||
.await
|
||||
{
|
||||
Ok(Some(u)) => u,
|
||||
@@ -627,10 +627,6 @@ pub async fn update_handle(
|
||||
.parse()
|
||||
.map_err(|_| ApiError::InvalidHandle(Some("Invalid handle format".into())))?;
|
||||
if new_handle == current_handle {
|
||||
let handle_typed: Handle = match new_handle.parse() {
|
||||
Ok(h) => h,
|
||||
Err(_) => return Err(ApiError::InvalidHandle(None)),
|
||||
};
|
||||
if let Err(e) =
|
||||
tranquil_pds::repo_ops::sequence_identity_event(&state, &did, Some(&handle)).await
|
||||
{
|
||||
|
||||
@@ -255,7 +255,6 @@ pub struct SessionResult {
|
||||
|
||||
pub async fn create_and_store_session(
|
||||
state: &AppState,
|
||||
did_str: &str,
|
||||
did: &Did,
|
||||
signing_key_bytes: &[u8],
|
||||
scope: &str,
|
||||
@@ -269,7 +268,7 @@ pub async fn create_and_store_session(
|
||||
let refresh_meta =
|
||||
tranquil_pds::auth::create_refresh_token_with_metadata(did, signing_key_bytes).map_err(
|
||||
|e| {
|
||||
tracing::error!("Error creating access token: {:?}", e);
|
||||
tracing::error!("Error creating refresh token: {:?}", e);
|
||||
ApiError::InternalError(None)
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -6,7 +6,7 @@ use axum::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use tracing::{error, info};
|
||||
use tracing::{error, info, warn};
|
||||
use tranquil_pds::api::ApiError;
|
||||
use tranquil_pds::api::proxy_client::{is_ssrf_safe, proxy_client};
|
||||
use tranquil_pds::auth::{AnyUser, Auth};
|
||||
@@ -149,7 +149,7 @@ async fn proxy_to_report_service(
|
||||
let service_token = match tranquil_pds::auth::create_service_token(
|
||||
&auth_user.did,
|
||||
service_did,
|
||||
Some("com.atproto.moderation.createReport"),
|
||||
Some(&report_lxm),
|
||||
&key_bytes,
|
||||
) {
|
||||
Ok(t) => t,
|
||||
|
||||
@@ -237,7 +237,7 @@ pub async fn import_repo(
|
||||
state
|
||||
.repos
|
||||
.repo
|
||||
.update_repo_root(user_id, &new_root_cid_link, &new_rev_str)
|
||||
.update_repo_root(user_id, &new_root_cid_link, &new_rev_tid)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to update repo root: {:?}", e);
|
||||
@@ -248,7 +248,7 @@ pub async fn import_repo(
|
||||
state
|
||||
.repos
|
||||
.repo
|
||||
.insert_user_blocks(user_id, &all_block_cids, &new_rev_str)
|
||||
.insert_user_blocks(user_id, &all_block_cids, &new_rev_tid)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to insert user_blocks: {:?}", e);
|
||||
|
||||
@@ -191,7 +191,7 @@ pub async fn create_app_password(
|
||||
}
|
||||
Ok(Json(CreateAppPasswordOutput {
|
||||
name: name.to_string(),
|
||||
password,
|
||||
password: password.into_inner(),
|
||||
created_at: created_at.to_rfc3339(),
|
||||
privileged: privilege.is_privileged(),
|
||||
scopes: final_scopes,
|
||||
|
||||
@@ -395,7 +395,7 @@ pub async fn update_email(
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CheckEmailVerifiedInput {
|
||||
pub identifier: String,
|
||||
pub identifier: AtIdentifier,
|
||||
}
|
||||
|
||||
pub async fn check_email_verified(
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct CreateInviteCodeInput {
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CreateInviteCodeOutput {
|
||||
pub code: String,
|
||||
pub code: InviteCodeValue,
|
||||
}
|
||||
|
||||
pub async fn create_invite_code(
|
||||
@@ -117,10 +117,7 @@ pub async fn create_invite_codes(
|
||||
infra_repo
|
||||
.create_invite_codes_batch(&codes, use_count, admin_user_id, Some(&account))
|
||||
.await
|
||||
.map(|_| AccountCodes {
|
||||
account: account.to_string(),
|
||||
codes,
|
||||
})
|
||||
.map(|_| AccountCodes { account, codes })
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
@@ -146,7 +143,7 @@ pub struct GetAccountInviteCodesParams {
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InviteCode {
|
||||
pub code: String,
|
||||
pub code: InviteCodeValue,
|
||||
pub available: i32,
|
||||
pub disabled: bool,
|
||||
pub for_account: String,
|
||||
|
||||
@@ -71,7 +71,7 @@ pub async fn create_passkey_account(
|
||||
let verifier = ServiceTokenVerifier::new();
|
||||
let create_account_lxm = Nsid::from("com.atproto.server.createAccount".to_string());
|
||||
match verifier
|
||||
.verify_service_token(&token, Some("com.atproto.server.createAccount"))
|
||||
.verify_service_token(&token, Some(&create_account_lxm))
|
||||
.await
|
||||
{
|
||||
Ok(claims) => {
|
||||
@@ -144,7 +144,7 @@ pub async fn create_passkey_account(
|
||||
let did_type = input.did_type.as_deref().unwrap_or("plc");
|
||||
|
||||
let key_result =
|
||||
match crate::identity::provision::resolve_signing_key(&state, input.signing_key.as_deref())
|
||||
match crate::identity::provision::resolve_signing_key(&state, input.signing_key.as_ref())
|
||||
.await
|
||||
{
|
||||
Ok(k) => k,
|
||||
@@ -192,7 +192,7 @@ pub async fn create_passkey_account(
|
||||
d,
|
||||
hostname,
|
||||
&input.handle,
|
||||
input.signing_key.as_deref(),
|
||||
input.signing_key.as_ref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -200,7 +200,8 @@ pub async fn create_passkey_account(
|
||||
}
|
||||
info!(did = %d, "Creating external did:web passkey account (reserved key)");
|
||||
}
|
||||
d.to_string()
|
||||
d.parse()
|
||||
.map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?
|
||||
}
|
||||
_ => {
|
||||
if let Some(ref auth_did) = byod_auth {
|
||||
@@ -213,7 +214,9 @@ pub async fn create_passkey_account(
|
||||
)));
|
||||
}
|
||||
info!(did = %provided_did, "Creating BYOD did:plc passkey account (migration)");
|
||||
provided_did.clone()
|
||||
provided_did
|
||||
.parse()
|
||||
.map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?
|
||||
} else {
|
||||
return Err(ApiError::InvalidRequest(
|
||||
"BYOD migration requires a did:plc or did:web DID".into(),
|
||||
@@ -545,7 +548,7 @@ pub async fn complete_passkey_setup(
|
||||
Ok(Json(CompletePasskeySetupOutput {
|
||||
did: input.did.clone(),
|
||||
handle: user.handle,
|
||||
app_password,
|
||||
app_password: app_password.into_inner(),
|
||||
app_password_name,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ pub async fn reauth_password(
|
||||
.unwrap_or_default();
|
||||
|
||||
let app_password_valid = app_password_hashes.iter().fold(false, |acc, h| {
|
||||
acc | bcrypt::verify(&input.password, h).unwrap_or(false)
|
||||
acc | bcrypt::verify(&input.password, h.as_str()).unwrap_or(false)
|
||||
});
|
||||
|
||||
if !app_password_valid {
|
||||
|
||||
@@ -118,7 +118,7 @@ pub async fn get_service_auth(
|
||||
&auth.auth_source,
|
||||
auth.scope.as_deref(),
|
||||
params.aud.as_str(),
|
||||
method.as_str(),
|
||||
method,
|
||||
) {
|
||||
return e.into_response();
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ pub async fn create_session(
|
||||
let row = match state
|
||||
.repos
|
||||
.user
|
||||
.get_login_full_by_identifier(normalized_identifier.as_str())
|
||||
.get_login_full_by_identifier(&login_identifier)
|
||||
.await
|
||||
{
|
||||
Ok(Some(row)) => row,
|
||||
@@ -277,7 +277,7 @@ pub async fn create_session(
|
||||
&row.did,
|
||||
&key_bytes,
|
||||
app_password_scopes.as_deref(),
|
||||
app_password_controller.as_deref(),
|
||||
app_password_controller.as_ref(),
|
||||
None,
|
||||
) {
|
||||
Ok(m) => m,
|
||||
|
||||
@@ -248,14 +248,14 @@ pub struct TokenWithMetadata {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TokenVerifyError {
|
||||
Expired,
|
||||
Invalid,
|
||||
Invalid(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for TokenVerifyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Expired => write!(f, "Token expired"),
|
||||
Self::Invalid => write!(f, "Token invalid"),
|
||||
Self::Invalid(reason) => write!(f, "{}", reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,10 +129,12 @@ fn build_smarthost(
|
||||
|
||||
fn build_direct_mx(cfg: &tranquil_config::TranquilConfig) -> Result<SendMode, SendError> {
|
||||
let helo = resolve_helo(cfg)?;
|
||||
let resolver = Arc::new(TokioAsyncResolver::tokio_from_system_conf().unwrap_or_else(|e| {
|
||||
tracing::warn!("falling back to default DNS resolvers: {}", e);
|
||||
TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default())
|
||||
}));
|
||||
let resolver = Arc::new(
|
||||
TokioAsyncResolver::tokio_from_system_conf().unwrap_or_else(|e| {
|
||||
tracing::warn!("falling back to default DNS resolvers: {}", e);
|
||||
TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default())
|
||||
}),
|
||||
);
|
||||
let max_concurrent = cfg.email.direct_mx.max_concurrent_sends.max(1);
|
||||
Ok(SendMode::DirectMx {
|
||||
resolver,
|
||||
|
||||
@@ -51,14 +51,7 @@ impl AccountStatus {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_firehose(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::Active => None,
|
||||
other => Some(other.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_firehose_typed(&self) -> Option<Self> {
|
||||
pub fn for_firehose(&self) -> Option<Self> {
|
||||
match self {
|
||||
Self::Active => None,
|
||||
other => Some(*other),
|
||||
|
||||
@@ -149,12 +149,12 @@ pub trait UserRepository: Send + Sync {
|
||||
|
||||
async fn get_login_check_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginCheck>, DbError>;
|
||||
|
||||
async fn get_login_info_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginInfo>, DbError>;
|
||||
|
||||
async fn get_2fa_status_by_did(&self, did: &Did) -> Result<Option<User2faStatus>, DbError>;
|
||||
@@ -211,7 +211,7 @@ pub trait UserRepository: Send + Sync {
|
||||
|
||||
async fn check_email_verified_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<bool>, DbError>;
|
||||
|
||||
async fn check_channel_verified_by_did(
|
||||
@@ -428,7 +428,7 @@ pub trait UserRepository: Send + Sync {
|
||||
|
||||
async fn get_login_full_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginFull>, DbError>;
|
||||
|
||||
async fn get_confirm_signup_by_did(
|
||||
|
||||
@@ -118,7 +118,7 @@ impl BlobRepository for PostgresBlobRepository {
|
||||
FROM repo_seq
|
||||
WHERE did = $1 AND rev > $2 AND blobs IS NOT NULL"#,
|
||||
did.as_str(),
|
||||
since
|
||||
since.as_str()
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
|
||||
@@ -159,7 +159,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
let result = sqlx::query!(
|
||||
r#"INSERT INTO invite_codes (code, available_uses, created_by_user, for_account)
|
||||
SELECT $1, $2, id, $3 FROM users WHERE is_admin = true LIMIT 1"#,
|
||||
code,
|
||||
code.as_str(),
|
||||
use_count,
|
||||
for_account_str
|
||||
)
|
||||
@@ -200,7 +200,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
) -> Result<Option<i32>, DbError> {
|
||||
let result = sqlx::query_scalar!(
|
||||
"SELECT available_uses FROM invite_codes WHERE code = $1 FOR UPDATE",
|
||||
code
|
||||
code.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -215,7 +215,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
) -> Result<ValidatedInviteCode<'a>, InviteCodeError> {
|
||||
let result = sqlx::query!(
|
||||
r#"SELECT available_uses, COALESCE(disabled, false) as "disabled!" FROM invite_codes WHERE code = $1"#,
|
||||
code
|
||||
code.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -270,7 +270,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
JOIN users u ON icu.used_by_user = u.id
|
||||
WHERE icu.code = $1
|
||||
ORDER BY icu.used_at DESC"#,
|
||||
code
|
||||
code.as_str()
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
@@ -279,7 +279,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
Ok(results
|
||||
.into_iter()
|
||||
.map(|r| InviteCodeUse {
|
||||
code: code.to_string(),
|
||||
code: code.clone(),
|
||||
used_by_did: Did::from(r.did),
|
||||
used_by_handle: Some(Handle::from(r.handle)),
|
||||
used_at: r.used_at,
|
||||
@@ -348,9 +348,19 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?,
|
||||
(None, InviteCodeSortOrder::Recent) => sqlx::query_as!(
|
||||
InviteCodeRow,
|
||||
.map_err(map_sqlx_error)?
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
to_row(
|
||||
r.code,
|
||||
r.available_uses,
|
||||
r.disabled,
|
||||
r.created_by_user,
|
||||
r.created_at,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
(None, InviteCodeSortOrder::Recent) => sqlx::query!(
|
||||
r#"SELECT ic.code, ic.available_uses, ic.disabled, ic.created_by_user, ic.created_at
|
||||
FROM invite_codes ic
|
||||
ORDER BY created_at DESC
|
||||
@@ -382,9 +392,19 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?,
|
||||
(None, InviteCodeSortOrder::Usage) => sqlx::query_as!(
|
||||
InviteCodeRow,
|
||||
.map_err(map_sqlx_error)?
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
to_row(
|
||||
r.code,
|
||||
r.available_uses,
|
||||
r.disabled,
|
||||
r.created_by_user,
|
||||
r.created_at,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
(None, InviteCodeSortOrder::Usage) => sqlx::query!(
|
||||
r#"SELECT ic.code, ic.available_uses, ic.disabled, ic.created_by_user, ic.created_at
|
||||
FROM invite_codes ic
|
||||
ORDER BY available_uses DESC
|
||||
@@ -393,7 +413,18 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?,
|
||||
.map_err(map_sqlx_error)?
|
||||
.into_iter()
|
||||
.map(|r| {
|
||||
to_row(
|
||||
r.code,
|
||||
r.available_uses,
|
||||
r.disabled,
|
||||
r.created_by_user,
|
||||
r.created_at,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
Ok(results)
|
||||
@@ -476,7 +507,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
FROM invite_codes ic
|
||||
JOIN users u ON ic.created_by_user = u.id
|
||||
WHERE ic.code = $1"#,
|
||||
code
|
||||
code.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -578,7 +609,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id"#,
|
||||
did_str,
|
||||
public_key_did_key,
|
||||
public_key_did_key.as_str(),
|
||||
private_key_bytes,
|
||||
expires_at
|
||||
)
|
||||
@@ -600,7 +631,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
AND used_at IS NULL
|
||||
AND expires_at > NOW()
|
||||
FOR UPDATE"#,
|
||||
public_key_did_key
|
||||
public_key_did_key.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -1165,7 +1196,7 @@ impl InfraRepository for PostgresInfraRepository {
|
||||
let row = sqlx::query!(
|
||||
r#"SELECT id, did, public_key_did_key, private_key_bytes, expires_at, used_at
|
||||
FROM reserved_signing_keys WHERE public_key_did_key = $1"#,
|
||||
public_key_did_key
|
||||
public_key_did_key.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
|
||||
@@ -7,9 +7,8 @@ use tranquil_db_traits::{
|
||||
ScopePreference, TokenFamilyId, TrustedDeviceRow, TwoFactorChallenge,
|
||||
};
|
||||
use tranquil_oauth::{
|
||||
AuthorizationRequestParameters, AuthorizedClientData, ClientAuth, Code as OAuthCode,
|
||||
DeviceData, DeviceId as OAuthDeviceId, RefreshToken as OAuthRefreshToken, RequestData,
|
||||
SessionId as OAuthSessionId, TokenData, TokenId as OAuthTokenId,
|
||||
AuthorizationRequestParameters, AuthorizedClientData, ClientAuth, DeviceData, RequestData,
|
||||
SessionId as OAuthSessionId, TokenData,
|
||||
};
|
||||
use tranquil_types::{
|
||||
AuthorizationCode, ClientId, DPoPProofId, DeviceId, Did, Handle, RefreshToken, RequestId,
|
||||
@@ -61,7 +60,7 @@ impl OAuthRepository for PostgresOAuthRepository {
|
||||
RETURNING id
|
||||
"#,
|
||||
data.did.as_str(),
|
||||
&data.token_id.0,
|
||||
data.token_id.as_str(),
|
||||
data.created_at,
|
||||
data.updated_at,
|
||||
data.expires_at,
|
||||
@@ -442,7 +441,7 @@ impl OAuthRepository for PostgresOAuthRepository {
|
||||
client_auth_json,
|
||||
parameters_json,
|
||||
data.expires_at,
|
||||
data.code.as_ref().map(|c| c.0.as_str()),
|
||||
data.code.as_deref(),
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
@@ -720,7 +719,7 @@ impl OAuthRepository for PostgresOAuthRepository {
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
"#,
|
||||
device_id.as_str(),
|
||||
&data.session_id.0,
|
||||
data.session_id.as_str(),
|
||||
data.user_agent,
|
||||
data.ip_address,
|
||||
data.last_seen_at,
|
||||
@@ -744,7 +743,7 @@ impl OAuthRepository for PostgresOAuthRepository {
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
Ok(row.map(|r| DeviceData {
|
||||
session_id: OAuthSessionId(r.session_id),
|
||||
session_id: OAuthSessionId::from(r.session_id),
|
||||
user_agent: r.user_agent,
|
||||
ip_address: r.ip_address,
|
||||
last_seen_at: r.last_seen_at,
|
||||
|
||||
@@ -770,7 +770,7 @@ impl RepoRepository for PostgresRepoRepository {
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(since_rev)
|
||||
.bind(since_rev.as_str())
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
@@ -780,6 +780,10 @@ impl RepoRepository for PostgresRepoRepository {
|
||||
|
||||
async fn insert_commit_event(&self, data: &CommitEventData) -> Result<(), DbError> {
|
||||
let (block_cids, block_data) = inline_to_paired_blocks(data.blocks.as_deref());
|
||||
let blob_strs: Option<Vec<String>> = data
|
||||
.blobs
|
||||
.as_ref()
|
||||
.map(|blobs| blobs.iter().map(|c| c.to_string()).collect());
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO repo_seq (did, event_type, commit_cid, prev_cid, ops, blobs, block_cids, block_data, prev_data_cid, rev)
|
||||
@@ -790,7 +794,7 @@ impl RepoRepository for PostgresRepoRepository {
|
||||
data.commit_cid.as_ref().map(|c| c.as_str()),
|
||||
data.prev_cid.as_ref().map(|c| c.as_str()),
|
||||
data.ops,
|
||||
data.blobs.as_deref(),
|
||||
blob_strs.as_deref(),
|
||||
&block_cids as &[Vec<u8>],
|
||||
&block_data as &[Vec<u8>],
|
||||
data.prev_data_cid.as_ref().map(|c| c.as_str()),
|
||||
@@ -828,7 +832,7 @@ impl RepoRepository for PostgresRepoRepository {
|
||||
|
||||
async fn insert_account_event(&self, did: &Did, status: AccountStatus) -> Result<(), DbError> {
|
||||
let active = status.is_active();
|
||||
let status_str = status.for_firehose();
|
||||
let status_str = status.for_firehose().map(|s| s.as_str());
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO repo_seq (did, event_type, active, status)
|
||||
@@ -1467,6 +1471,10 @@ impl RepoRepository for PostgresRepoRepository {
|
||||
|
||||
let event = input.commit_event;
|
||||
let (event_block_cids, event_block_data) = inline_into_paired_blocks(event.blocks);
|
||||
let event_blob_strs: Option<Vec<String>> = event
|
||||
.blobs
|
||||
.as_ref()
|
||||
.map(|blobs| blobs.iter().map(|c| c.to_string()).collect());
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO repo_seq (did, event_type, commit_cid, prev_cid, ops, blobs, block_cids, block_data, prev_data_cid, rev)
|
||||
@@ -1477,7 +1485,7 @@ impl RepoRepository for PostgresRepoRepository {
|
||||
event.commit_cid.as_ref().map(|c| c.as_str()),
|
||||
event.prev_cid.as_ref().map(|c| c.as_str()),
|
||||
event.ops,
|
||||
event.blobs.as_deref(),
|
||||
event_blob_strs.as_deref(),
|
||||
&event_block_cids as &[Vec<u8>],
|
||||
&event_block_data as &[Vec<u8>],
|
||||
event.prev_data_cid.as_ref().map(|c| c.as_str()),
|
||||
|
||||
@@ -161,7 +161,7 @@ impl SessionRepository for PostgresSessionRepository {
|
||||
let result = sqlx::query!(
|
||||
"DELETE FROM session_tokens WHERE did = $1 AND access_jti != $2",
|
||||
did.as_str(),
|
||||
except_jti
|
||||
except_jti.as_str()
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
|
||||
@@ -201,7 +201,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
JOIN users u ON t.did = u.did
|
||||
LEFT JOIN user_keys k ON u.id = k.user_id
|
||||
WHERE t.token_id = $1"#,
|
||||
token_id
|
||||
token_id.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -588,11 +588,11 @@ impl UserRepository for PostgresUserRepository {
|
||||
|
||||
async fn check_email_verified_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<bool>, DbError> {
|
||||
let row = sqlx::query_scalar!(
|
||||
"SELECT email_verified FROM users WHERE did = $1 OR email = $1 OR handle = $1",
|
||||
identifier
|
||||
identifier.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -1423,11 +1423,11 @@ impl UserRepository for PostgresUserRepository {
|
||||
|
||||
async fn get_login_check_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginCheck>, DbError> {
|
||||
sqlx::query!(
|
||||
"SELECT did, password_hash FROM users WHERE handle = $1 OR did = $1",
|
||||
identifier
|
||||
identifier.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -1442,7 +1442,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
|
||||
async fn get_login_info_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginInfo>, DbError> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -1454,7 +1454,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
FROM users
|
||||
WHERE handle = $1 OR did = $1
|
||||
"#,
|
||||
identifier
|
||||
identifier.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -1602,7 +1602,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
|
||||
async fn get_login_full_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginFull>, DbError> {
|
||||
sqlx::query!(
|
||||
r#"SELECT
|
||||
@@ -1616,7 +1616,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
FROM users u
|
||||
JOIN user_keys k ON u.id = k.user_id
|
||||
WHERE u.handle = $1 OR u.did = $1"#,
|
||||
identifier
|
||||
identifier.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -3236,7 +3236,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
"UPDATE users SET discord_id = $2, discord_verified = TRUE, updated_at = NOW() WHERE LOWER(discord_username) = LOWER($1) AND discord_username IS NOT NULL AND handle = $3 RETURNING id",
|
||||
discord_username,
|
||||
discord_id,
|
||||
h
|
||||
h.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
@@ -3298,7 +3298,7 @@ impl UserRepository for PostgresUserRepository {
|
||||
"UPDATE users SET telegram_chat_id = $2, telegram_verified = TRUE, updated_at = NOW() WHERE LOWER(telegram_username) = LOWER($1) AND telegram_username IS NOT NULL AND handle = $3 RETURNING id",
|
||||
telegram_username,
|
||||
chat_id,
|
||||
h
|
||||
h.as_str()
|
||||
)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
|
||||
@@ -341,8 +341,8 @@ mod tests {
|
||||
.unwrap_err();
|
||||
|
||||
assert!(
|
||||
!matches!(err, ResolveError::InvalidNsid(_)),
|
||||
"negative cache hit should not return InvalidNsid - the NSID is valid, it just failed resolution recently. got: {}",
|
||||
matches!(err, ResolveError::NegativelyCached { .. }),
|
||||
"negative cache hit must surface as NegativelyCached, got: {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
@@ -438,7 +438,7 @@ mod tests {
|
||||
let result = registry
|
||||
.resolve_and_cache_with(&nsid("pet.nel.flaky"), |n| async move {
|
||||
Err::<LexiconDoc, _>(ResolveError::DnsLookup {
|
||||
domain: n,
|
||||
domain: n.into_inner(),
|
||||
reason: "simulated failure".to_string(),
|
||||
})
|
||||
})
|
||||
@@ -597,7 +597,7 @@ mod tests {
|
||||
calls.fetch_add(1, Ordering::SeqCst);
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
Err::<LexiconDoc, _>(ResolveError::DnsLookup {
|
||||
domain: n,
|
||||
domain: n.into_inner(),
|
||||
reason: "simulated".to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ impl LexiconRegistry {
|
||||
pub fn resolve_ref(&self, reference: &str, context_nsid: &str) -> Option<ResolvedRef> {
|
||||
match parse_ref(reference) {
|
||||
ParsedRef::Local(local) => {
|
||||
let doc = self.get_doc(context_nsid)?;
|
||||
let doc = self.get_doc_by_key(context_nsid)?;
|
||||
Self::def_to_resolved(&doc, local)
|
||||
}
|
||||
ParsedRef::Qualified { nsid, fragment } => {
|
||||
|
||||
@@ -53,8 +53,6 @@ async fn read_body_limited(resp: reqwest::Response, max_bytes: usize) -> Result<
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ResolveError {
|
||||
#[error("failed to derive authority from NSID: {0}")]
|
||||
InvalidNsid(String),
|
||||
#[error("DNS lookup failed for {domain}: {reason}")]
|
||||
DnsLookup { domain: String, reason: String },
|
||||
#[error("no DID found in DNS TXT records for {domain}")]
|
||||
@@ -79,7 +77,7 @@ pub fn nsid_to_authority(nsid: &Nsid) -> String {
|
||||
let mut segments: Vec<&str> = nsid.split('.').collect();
|
||||
segments.pop();
|
||||
segments.reverse();
|
||||
Ok(segments.join("."))
|
||||
segments.join(".")
|
||||
}
|
||||
|
||||
pub async fn resolve_did_from_dns(authority: &str) -> Result<Did, ResolveError> {
|
||||
@@ -308,7 +306,6 @@ mod tests {
|
||||
nsid_to_authority(&nsid("com.germnetwork.social.post")),
|
||||
"social.germnetwork.com"
|
||||
);
|
||||
assert!(nsid_to_authority("tooShort").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -420,7 +420,7 @@ pub async fn consent_post(
|
||||
let redirect_uri = &request_data.parameters.redirect_uri;
|
||||
let intermediate_url = build_intermediate_redirect_url(
|
||||
redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
|
||||
@@ -105,12 +105,12 @@ pub async fn authorize_get(
|
||||
let normalized = NormalizedLoginIdentifier::normalize(login_hint, hostname_for_handles);
|
||||
tracing::info!(normalized = %normalized, "Normalized login_hint");
|
||||
|
||||
match state
|
||||
.repos
|
||||
.user
|
||||
.get_login_check_by_identifier(normalized.as_str())
|
||||
.await
|
||||
{
|
||||
let hint_identifier = tranquil_types::AtIdentifier::new(normalized.as_str()).ok();
|
||||
let hint_lookup = match hint_identifier {
|
||||
Some(ref id) => state.repos.user.get_login_check_by_identifier(id).await,
|
||||
None => Ok(None),
|
||||
};
|
||||
match hint_lookup {
|
||||
Ok(Some(user)) => {
|
||||
tracing::info!(did = %user.did, has_password = user.password_hash.is_some(), "Found user for login_hint");
|
||||
let is_delegated = state
|
||||
@@ -399,12 +399,12 @@ pub async fn authorize_post(
|
||||
pds_hostname = %tranquil_config::get().server.hostname,
|
||||
"Normalized username for lookup"
|
||||
);
|
||||
let user = match state
|
||||
.repos
|
||||
.user
|
||||
.get_login_info_by_identifier(normalized_username.as_str())
|
||||
.await
|
||||
{
|
||||
let login_identifier = tranquil_types::AtIdentifier::new(normalized_username.as_str()).ok();
|
||||
let login_lookup = match login_identifier {
|
||||
Some(ref id) => state.repos.user.get_login_info_by_identifier(id).await,
|
||||
None => Ok(None),
|
||||
};
|
||||
let user = match login_lookup {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => {
|
||||
let _ = bcrypt::verify(
|
||||
@@ -478,7 +478,7 @@ pub async fn authorize_post(
|
||||
}
|
||||
|
||||
let password_valid = match &user.password_hash {
|
||||
Some(hash) => match bcrypt::verify(&form.password, hash) {
|
||||
Some(hash) => match bcrypt::verify(&form.password, hash.as_str()) {
|
||||
Ok(valid) => valid,
|
||||
Err(_) => {
|
||||
return show_login_error("An error occurred. Please try again.", json_response);
|
||||
@@ -708,7 +708,7 @@ pub async fn authorize_post(
|
||||
if json_response {
|
||||
let redirect_url = build_intermediate_redirect_url(
|
||||
&request_data.parameters.redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
@@ -725,7 +725,7 @@ pub async fn authorize_post(
|
||||
} else {
|
||||
let redirect_url = build_success_redirect(
|
||||
&request_data.parameters.redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
|
||||
@@ -19,11 +19,10 @@ pub async fn check_user_has_passkeys(
|
||||
let bare_identifier =
|
||||
BareLoginIdentifier::from_identifier(&query.identifier, hostname_for_handles);
|
||||
|
||||
let user = state
|
||||
.repos
|
||||
.user
|
||||
.get_login_check_by_identifier(bare_identifier.as_str())
|
||||
.await;
|
||||
let user = match tranquil_types::AtIdentifier::new(bare_identifier.as_str()) {
|
||||
Ok(ref id) => state.repos.user.get_login_check_by_identifier(id).await,
|
||||
Err(_) => Ok(None),
|
||||
};
|
||||
|
||||
let has_passkeys = match user {
|
||||
Ok(Some(u)) => tranquil_api::server::has_passkeys_for_user(&state, &u.did).await,
|
||||
@@ -52,11 +51,10 @@ pub async fn check_user_security_status(
|
||||
let normalized_identifier =
|
||||
NormalizedLoginIdentifier::normalize(&query.identifier, hostname_for_handles);
|
||||
|
||||
let user = state
|
||||
.repos
|
||||
.user
|
||||
.get_login_check_by_identifier(normalized_identifier.as_str())
|
||||
.await;
|
||||
let user = match tranquil_types::AtIdentifier::new(normalized_identifier.as_str()) {
|
||||
Ok(ref id) => state.repos.user.get_login_check_by_identifier(id).await,
|
||||
Err(_) => Ok(None),
|
||||
};
|
||||
|
||||
let (has_passkeys, has_totp, has_password, is_delegated, did): (
|
||||
bool,
|
||||
@@ -238,12 +236,11 @@ async fn passkey_start_named(
|
||||
let normalized_username =
|
||||
NormalizedLoginIdentifier::normalize(&identifier, hostname_for_handles);
|
||||
|
||||
let user = match state
|
||||
.repos
|
||||
.user
|
||||
.get_login_info_by_identifier(normalized_username.as_str())
|
||||
.await
|
||||
{
|
||||
let passkey_lookup = match tranquil_types::AtIdentifier::new(normalized_username.as_str()) {
|
||||
Ok(ref id) => state.repos.user.get_login_info_by_identifier(id).await,
|
||||
Err(_) => Ok(None),
|
||||
};
|
||||
let user = match passkey_lookup {
|
||||
Ok(Some(u)) => u,
|
||||
Ok(None) => {
|
||||
return (
|
||||
@@ -670,7 +667,7 @@ pub async fn passkey_finish(
|
||||
|
||||
let redirect_url = build_intermediate_redirect_url(
|
||||
&request_data.parameters.redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
|
||||
@@ -140,13 +140,13 @@ pub async fn register_complete(
|
||||
};
|
||||
|
||||
let mut password_valid = password_hashes.iter().fold(false, |acc, hash| {
|
||||
acc | bcrypt::verify(&form.app_password, hash).unwrap_or(false)
|
||||
acc | bcrypt::verify(&form.app_password, hash.as_str()).unwrap_or(false)
|
||||
});
|
||||
|
||||
if !password_valid
|
||||
&& let Ok(Some(account_hash)) = state.repos.user.get_password_hash_by_did(&did).await
|
||||
{
|
||||
password_valid = bcrypt::verify(&form.app_password, &account_hash).unwrap_or(false);
|
||||
password_valid = bcrypt::verify(&form.app_password, account_hash.as_str()).unwrap_or(false);
|
||||
}
|
||||
|
||||
if !password_valid {
|
||||
@@ -290,7 +290,7 @@ pub async fn register_complete(
|
||||
|
||||
let redirect_url = build_intermediate_redirect_url(
|
||||
&request_data.parameters.redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
|
||||
@@ -185,7 +185,7 @@ pub async fn authorize_2fa_post(
|
||||
}
|
||||
let redirect_url = build_intermediate_redirect_url(
|
||||
&request_data.parameters.redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
@@ -329,7 +329,7 @@ pub async fn authorize_2fa_post(
|
||||
}
|
||||
let redirect_url = build_intermediate_redirect_url(
|
||||
&request_data.parameters.redirect_uri,
|
||||
&code.0,
|
||||
code.as_str(),
|
||||
request_data.parameters.state.as_deref(),
|
||||
request_data.parameters.response_mode.map(|m| m.as_str()),
|
||||
);
|
||||
|
||||
@@ -294,7 +294,7 @@ pub async fn delegation_auth(
|
||||
let password_valid = controller
|
||||
.password_hash
|
||||
.as_ref()
|
||||
.map(|hash| bcrypt::verify(password, hash).unwrap_or_default())
|
||||
.map(|hash| bcrypt::verify(password, hash.as_str()).unwrap_or_default())
|
||||
.unwrap_or_default();
|
||||
|
||||
if !password_valid {
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct ParRequest {
|
||||
#[serde(default)]
|
||||
pub login_hint: Option<String>,
|
||||
#[serde(default)]
|
||||
pub dpop_jkt: Option<String>,
|
||||
pub dpop_jkt: Option<JwkThumbprint>,
|
||||
#[serde(default)]
|
||||
pub client_secret: Option<String>,
|
||||
#[serde(default)]
|
||||
|
||||
@@ -16,7 +16,6 @@ use tranquil_pds::oauth::{
|
||||
verify_client_auth,
|
||||
};
|
||||
use tranquil_pds::state::AppState;
|
||||
use tranquil_types::{AuthorizationCode, Did, RefreshToken as RefreshTokenType};
|
||||
|
||||
const ACCESS_TOKEN_EXPIRY_SECONDS: u64 = 300;
|
||||
const REFRESH_TOKEN_EXPIRY_DAYS_CONFIDENTIAL: i64 = 60;
|
||||
@@ -115,13 +114,13 @@ pub async fn handle_authorization_code_grant(
|
||||
));
|
||||
}
|
||||
if let Some(expected_jkt) = &authorized.parameters.dpop_jkt
|
||||
&& result.jkt.as_str() != expected_jkt
|
||||
&& result.jkt != *expected_jkt
|
||||
{
|
||||
return Err(OAuthError::InvalidDpopProof(
|
||||
"DPoP key binding mismatch".to_string(),
|
||||
));
|
||||
}
|
||||
Some(result.jkt.as_str().to_string())
|
||||
Some(result.jkt.clone())
|
||||
} else if authorized.parameters.dpop_jkt.is_some() || client_metadata.requires_dpop() {
|
||||
return Err(OAuthError::UseDpopNonce(
|
||||
DPoPVerifier::new(AuthConfig::get().dpop_secret().as_bytes()).generate_nonce(),
|
||||
@@ -169,9 +168,9 @@ pub async fn handle_authorization_code_grant(
|
||||
};
|
||||
|
||||
let access_token = create_access_token_with_delegation(
|
||||
&token_id.0,
|
||||
&token_id,
|
||||
&did,
|
||||
dpop_jkt.as_deref(),
|
||||
dpop_jkt.as_ref(),
|
||||
final_scope.as_deref(),
|
||||
controller_did.as_ref(),
|
||||
)?;
|
||||
@@ -207,7 +206,7 @@ pub async fn handle_authorization_code_grant(
|
||||
.map_err(tranquil_pds::oauth::db_err_to_oauth)?;
|
||||
tracing::info!(
|
||||
did = %did,
|
||||
token_id = %token_id.0,
|
||||
token_id = %token_id,
|
||||
client_id = %authorized.client_id,
|
||||
"Authorization code grant completed, token created"
|
||||
);
|
||||
@@ -215,9 +214,7 @@ pub async fn handle_authorization_code_grant(
|
||||
let oauth_repo = state.repos.oauth.clone();
|
||||
let did_clone = did.clone();
|
||||
async move {
|
||||
if let Ok(did_typed) = did_clone.parse::<tranquil_types::Did>()
|
||||
&& let Err(e) = enforce_token_limit_for_user(oauth_repo.as_ref(), &did_typed).await
|
||||
{
|
||||
if let Err(e) = enforce_token_limit_for_user(oauth_repo.as_ref(), &did_clone).await {
|
||||
tracing::warn!("Failed to enforce token limit for user: {:?}", e);
|
||||
}
|
||||
}
|
||||
@@ -284,7 +281,7 @@ pub async fn handle_refresh_token_grant(
|
||||
rotated_at = %rotated_at,
|
||||
"Refresh token reuse within grace period, returning existing tokens"
|
||||
);
|
||||
let dpop_jkt = token_data.parameters.dpop_jkt.as_deref();
|
||||
let dpop_jkt = token_data.parameters.dpop_jkt.as_ref();
|
||||
let access_token = create_access_token_with_delegation(
|
||||
&token_data.token_id,
|
||||
&token_data.did,
|
||||
@@ -367,13 +364,13 @@ pub async fn handle_refresh_token_grant(
|
||||
));
|
||||
}
|
||||
if let Some(expected_jkt) = &token_data.parameters.dpop_jkt
|
||||
&& result.jkt.as_str() != expected_jkt
|
||||
&& result.jkt != *expected_jkt
|
||||
{
|
||||
return Err(OAuthError::InvalidDpopProof(
|
||||
"DPoP key binding mismatch".to_string(),
|
||||
));
|
||||
}
|
||||
Some(result.jkt.as_str().to_string())
|
||||
Some(result.jkt.clone())
|
||||
} else if token_data.parameters.dpop_jkt.is_some() {
|
||||
return Err(OAuthError::InvalidRequest(
|
||||
"DPoP proof required".to_string(),
|
||||
|
||||
@@ -46,19 +46,19 @@ pub fn create_access_token_with_delegation(
|
||||
let actual_scope = scope.unwrap_or("atproto");
|
||||
let mut payload = json!({
|
||||
"iss": issuer,
|
||||
"sub": sub,
|
||||
"sub": sub.as_str(),
|
||||
"aud": issuer,
|
||||
"iat": now,
|
||||
"exp": exp,
|
||||
"jti": jti,
|
||||
"sid": session_id,
|
||||
"sid": session_id.as_str(),
|
||||
"scope": actual_scope
|
||||
});
|
||||
if let Some(jkt) = dpop_jkt {
|
||||
payload["cnf"] = json!({ "jkt": jkt });
|
||||
payload["cnf"] = json!({ "jkt": jkt.as_str() });
|
||||
}
|
||||
if let Some(controller) = controller_did {
|
||||
payload["act"] = json!({ "sub": controller });
|
||||
payload["act"] = json!({ "sub": controller.as_str() });
|
||||
}
|
||||
let header = json!({
|
||||
"alg": "HS256",
|
||||
|
||||
@@ -10,7 +10,7 @@ use tranquil_pds::rate_limit::{OAuthRateLimited, OAuthTokenLimit};
|
||||
use tranquil_pds::state::AppState;
|
||||
|
||||
pub use grants::{handle_authorization_code_grant, handle_refresh_token_grant};
|
||||
pub use helpers::{TokenClaims, create_access_token, extract_token_claims, verify_pkce};
|
||||
pub use helpers::{TokenClaims, extract_token_claims, verify_pkce};
|
||||
pub use introspect::{
|
||||
IntrospectRequest, IntrospectResponse, RevokeRequest, introspect_token, revoke_token,
|
||||
};
|
||||
|
||||
@@ -1042,7 +1042,8 @@ pub async fn complete_registration(
|
||||
));
|
||||
}
|
||||
tracing::info!(did = %d, "Creating external did:web SSO account");
|
||||
d.to_string()
|
||||
d.parse()
|
||||
.map_err(|_| ApiError::InvalidDid("Invalid DID format".into()))?
|
||||
}
|
||||
_ => {
|
||||
let genesis_result = match tranquil_pds::plc::create_genesis_operation(
|
||||
@@ -1345,7 +1346,7 @@ pub async fn complete_registration(
|
||||
redirect_url: "/app/dashboard".to_string(),
|
||||
access_jwt: Some(access_meta.token),
|
||||
refresh_jwt: Some(refresh_meta.token),
|
||||
app_password: Some(app_password),
|
||||
app_password: Some(app_password.into_inner()),
|
||||
app_password_name: Some(app_password_name),
|
||||
}));
|
||||
}
|
||||
@@ -1359,7 +1360,7 @@ pub async fn complete_registration(
|
||||
),
|
||||
access_jwt: None,
|
||||
refresh_jwt: None,
|
||||
app_password: Some(app_password),
|
||||
app_password: Some(app_password.into_inner()),
|
||||
app_password_name: Some(app_password_name),
|
||||
}));
|
||||
}
|
||||
@@ -1403,7 +1404,7 @@ pub async fn complete_registration(
|
||||
redirect_url,
|
||||
access_jwt: None,
|
||||
refresh_jwt: None,
|
||||
app_password: Some(app_password),
|
||||
app_password: Some(app_password.into_inner()),
|
||||
app_password_name: Some(app_password_name),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -8,60 +8,31 @@ pub use tranquil_types::{AuthorizationCode, DeviceId, RefreshToken, RequestId, T
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
|
||||
#[serde(transparent)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct RequestId(pub String);
|
||||
pub struct SessionId(String);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
|
||||
#[serde(transparent)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct TokenId(pub String);
|
||||
impl SessionId {
|
||||
pub fn new(s: impl Into<String>) -> Self {
|
||||
Self(s.into())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
|
||||
#[serde(transparent)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct DeviceId(pub String);
|
||||
pub fn generate() -> Self {
|
||||
Self(uuid::Uuid::new_v4().to_string())
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenId {
|
||||
pub fn generate() -> Self {
|
||||
Self(uuid::Uuid::new_v4().to_string())
|
||||
impl From<String> for SessionId {
|
||||
fn from(s: String) -> Self {
|
||||
Self(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub fn generate() -> Self {
|
||||
Self(uuid::Uuid::new_v4().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionId {
|
||||
pub fn generate() -> Self {
|
||||
Self(uuid::Uuid::new_v4().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Code {
|
||||
pub fn generate() -> Self {
|
||||
use rand::Rng;
|
||||
let bytes: [u8; 32] = rand::thread_rng().r#gen();
|
||||
Self(base64::Engine::encode(
|
||||
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
|
||||
bytes,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl RefreshToken {
|
||||
pub fn generate() -> Self {
|
||||
use rand::Rng;
|
||||
let bytes: [u8; 32] = rand::thread_rng().r#gen();
|
||||
Self(base64::Engine::encode(
|
||||
&base64::engine::general_purpose::URL_SAFE_NO_PAD,
|
||||
bytes,
|
||||
))
|
||||
impl std::fmt::Display for SessionId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +117,7 @@ pub struct AuthorizationRequestParameters {
|
||||
pub code_challenge_method: CodeChallengeMethod,
|
||||
pub response_mode: Option<ResponseMode>,
|
||||
pub login_hint: Option<String>,
|
||||
pub dpop_jkt: Option<String>,
|
||||
pub dpop_jkt: Option<tranquil_types::JwkThumbprint>,
|
||||
pub prompt: Option<Prompt>,
|
||||
#[serde(flatten)]
|
||||
pub extra: Option<JsonValue>,
|
||||
|
||||
@@ -291,7 +291,7 @@ async fn proxy_handler(
|
||||
&auth_user.auth_source,
|
||||
auth_user.scope.as_deref(),
|
||||
&resolved.did,
|
||||
method,
|
||||
&method_nsid,
|
||||
) {
|
||||
return e.into_response();
|
||||
}
|
||||
@@ -353,7 +353,7 @@ async fn proxy_handler(
|
||||
match crate::auth::create_service_token(
|
||||
&auth_user.did,
|
||||
&token_aud,
|
||||
Some(token_lxm),
|
||||
Some(&token_lxm),
|
||||
&key_bytes,
|
||||
) {
|
||||
Ok(new_token) => {
|
||||
|
||||
@@ -214,7 +214,7 @@ pub fn resolve_handle_input(input: &str) -> Result<Handle, HandleValidationError
|
||||
"{}.{}",
|
||||
validated,
|
||||
matched_domain.unwrap_or(&available_domains[0])
|
||||
))
|
||||
)))
|
||||
} else {
|
||||
validate_full_domain_handle(input)
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ pub async fn verify_password_mfa<'a>(
|
||||
|
||||
match hash {
|
||||
Some(h) => {
|
||||
if bcrypt::verify(password, &h).unwrap_or(false) {
|
||||
if bcrypt::verify(password, h.as_str()).unwrap_or(false) {
|
||||
Ok(MfaVerified::from_password(user))
|
||||
} else {
|
||||
Err(crate::api::error::ApiError::InvalidPassword(
|
||||
|
||||
@@ -82,7 +82,7 @@ pub fn decrypt_totp_secret(
|
||||
crate::config::decrypt_key(encrypted, Some(version))
|
||||
}
|
||||
|
||||
pub fn generate_app_password() -> String {
|
||||
pub fn generate_app_password() -> crate::types::PlainPassword {
|
||||
use rand::Rng;
|
||||
let chars: &[u8] = b"abcdefghijklmnopqrstuvwxyz234567";
|
||||
let mut rng = rand::thread_rng();
|
||||
@@ -93,7 +93,7 @@ pub fn generate_app_password() -> String {
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
segments.join("-")
|
||||
crate::types::PlainPassword::new(segments.join("-"))
|
||||
}
|
||||
|
||||
const KEY_CACHE_TTL_SECS: u64 = 300;
|
||||
@@ -631,26 +631,29 @@ mod tests {
|
||||
fn test_lxm_permits_exact_match() {
|
||||
assert!(lxm_permits(
|
||||
"com.atproto.repo.uploadBlob",
|
||||
"com.atproto.repo.uploadBlob"
|
||||
&n("com.atproto.repo.uploadBlob")
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lxm_permits_wildcard() {
|
||||
assert!(lxm_permits("*", "com.atproto.repo.uploadBlob"));
|
||||
assert!(lxm_permits("*", "anything.at.all"));
|
||||
assert!(lxm_permits("*", &n("com.atproto.repo.uploadBlob")));
|
||||
assert!(lxm_permits("*", &n("anything.at.all")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lxm_permits_mismatch() {
|
||||
assert!(!lxm_permits(
|
||||
"com.atproto.repo.uploadBlob",
|
||||
"com.atproto.repo.createRecord"
|
||||
&n("com.atproto.repo.createRecord")
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lxm_permits_partial_not_wildcard() {
|
||||
assert!(!lxm_permits("com.atproto.*", "com.atproto.repo.uploadBlob"));
|
||||
assert!(!lxm_permits(
|
||||
"com.atproto.*",
|
||||
&n("com.atproto.repo.uploadBlob")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +75,11 @@ pub async fn verify_handle_ownership(
|
||||
expected_did: &Did,
|
||||
) -> Result<(), HandleResolutionError> {
|
||||
let resolved_did = resolve_handle(handle).await?;
|
||||
if resolved_did == expected_did {
|
||||
if resolved_did == *expected_did {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(HandleResolutionError::DidMismatch {
|
||||
expected: expected_did.to_string(),
|
||||
expected: expected_did.clone(),
|
||||
actual: resolved_did,
|
||||
})
|
||||
}
|
||||
@@ -103,10 +103,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_is_service_domain_handle() {
|
||||
assert!(is_service_domain_handle("user.example.com", "example.com"));
|
||||
assert!(is_service_domain_handle("example.com", "example.com"));
|
||||
assert!(is_service_domain_handle("myhandle", "example.com"));
|
||||
assert!(!is_service_domain_handle("user.other.com", "example.com"));
|
||||
assert!(!is_service_domain_handle("myhandle.xyz", "example.com"));
|
||||
assert!(is_service_domain_handle("nel.oyster.cafe", "oyster.cafe"));
|
||||
assert!(is_service_domain_handle("oyster.cafe", "oyster.cafe"));
|
||||
assert!(is_service_domain_handle("myhandle", "oyster.cafe"));
|
||||
assert!(!is_service_domain_handle("lyna.nel.pet", "oyster.cafe"));
|
||||
assert!(!is_service_domain_handle("myhandle.xyz", "oyster.cafe"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ pub struct OAuthTokenInfo {
|
||||
pub did: Did,
|
||||
pub token_id: TokenId,
|
||||
pub scope: Option<String>,
|
||||
pub dpop_jkt: Option<String>,
|
||||
pub controller_did: Option<Did>,
|
||||
}
|
||||
|
||||
@@ -87,7 +86,7 @@ pub async fn verify_oauth_access_token(
|
||||
"DPoP proof has already been used".to_string(),
|
||||
));
|
||||
}
|
||||
if result.jkt.as_str() != expected_jkt {
|
||||
if result.jkt != *expected_jkt {
|
||||
return Err(OAuthError::InvalidDpopProof(
|
||||
"DPoP key binding mismatch".to_string(),
|
||||
));
|
||||
@@ -179,7 +178,6 @@ pub fn extract_oauth_token_info(token: &str) -> Result<OAuthTokenInfo, OAuthErro
|
||||
did,
|
||||
token_id,
|
||||
scope,
|
||||
dpop_jkt,
|
||||
controller_did,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ use uuid::Uuid;
|
||||
#[derive(Debug)]
|
||||
pub enum CommitError {
|
||||
InvalidDid(String),
|
||||
InvalidTid(String),
|
||||
SigningFailed(String),
|
||||
SerializationFailed(String),
|
||||
KeyNotFound,
|
||||
@@ -47,7 +46,6 @@ impl std::fmt::Display for CommitError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidDid(e) => write!(f, "Invalid DID: {}", e),
|
||||
Self::InvalidTid(e) => write!(f, "Invalid TID: {}", e),
|
||||
Self::SigningFailed(e) => write!(f, "Failed to sign commit: {}", e),
|
||||
Self::SerializationFailed(e) => write!(f, "Failed to serialize signed commit: {}", e),
|
||||
Self::KeyNotFound => write!(f, "Signing key not found"),
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::sync::firehose::SequencedEvent;
|
||||
use cid::Cid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
use tranquil_db_traits::SequenceNumber;
|
||||
use tranquil_scopes::RepoAction;
|
||||
use tranquil_types::{CidLink, Did, Handle, Tid};
|
||||
|
||||
@@ -27,7 +28,7 @@ pub struct FrameHeader {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CommitFrame {
|
||||
pub seq: i64,
|
||||
pub seq: SequenceNumber,
|
||||
pub rebase: bool,
|
||||
#[serde(rename = "tooBig")]
|
||||
pub too_big: bool,
|
||||
@@ -76,7 +77,7 @@ pub struct AccountFrame {
|
||||
pub active: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<tranquil_db_traits::AccountStatus>,
|
||||
pub seq: i64,
|
||||
pub seq: SequenceNumber,
|
||||
pub time: String,
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ pub struct SyncFrame {
|
||||
pub rev: Tid,
|
||||
#[serde(with = "serde_bytes")]
|
||||
pub blocks: Vec<u8>,
|
||||
pub seq: i64,
|
||||
pub seq: SequenceNumber,
|
||||
pub time: String,
|
||||
}
|
||||
|
||||
@@ -139,7 +140,7 @@ impl std::fmt::Display for CommitFrameError {
|
||||
impl std::error::Error for CommitFrameError {}
|
||||
|
||||
pub struct CommitFrameBuilder {
|
||||
seq: i64,
|
||||
seq: SequenceNumber,
|
||||
did: Did,
|
||||
commit_cid: Cid,
|
||||
ops_json: serde_json::Value,
|
||||
@@ -150,7 +151,7 @@ pub struct CommitFrameBuilder {
|
||||
|
||||
impl CommitFrameBuilder {
|
||||
pub fn new(
|
||||
seq: i64,
|
||||
seq: SequenceNumber,
|
||||
did: Did,
|
||||
commit_cid: &CidLink,
|
||||
ops_json: serde_json::Value,
|
||||
@@ -222,7 +223,7 @@ impl TryFrom<SequencedEvent> for CommitFrame {
|
||||
CommitFrameError::InvalidCommitCid("Missing commit_cid in event".to_string())
|
||||
})?;
|
||||
let builder = CommitFrameBuilder::new(
|
||||
event.seq.as_i64(),
|
||||
event.seq,
|
||||
event.did.clone(),
|
||||
&commit_cid,
|
||||
event.ops.unwrap_or_default(),
|
||||
|
||||
@@ -143,7 +143,7 @@ pub fn find_blob_refs(value: &JsonValue, depth: usize) -> Vec<BlobRef> {
|
||||
.and_then(|v| v.as_str())
|
||||
.map(String::from);
|
||||
return vec![BlobRef {
|
||||
cid: link.clone(),
|
||||
cid,
|
||||
mime_type: mime,
|
||||
}];
|
||||
}
|
||||
@@ -173,7 +173,7 @@ pub fn extract_links(value: &Ipld, links: &mut Vec<Cid>) {
|
||||
#[derive(Debug)]
|
||||
pub struct ImportedRecord {
|
||||
pub collection: Nsid,
|
||||
pub rkey: String,
|
||||
pub rkey: Rkey,
|
||||
pub cid: Cid,
|
||||
pub blob_refs: Vec<BlobRef>,
|
||||
}
|
||||
@@ -229,7 +229,7 @@ fn walk_mst_node(
|
||||
let parts: Vec<&str> = full_key.split('/').collect();
|
||||
if parts.len() >= 2 {
|
||||
let collection = Nsid::from(parts[..parts.len() - 1].join("/"));
|
||||
let rkey = parts[parts.len() - 1].to_string();
|
||||
let rkey = Rkey::from(parts[parts.len() - 1].to_string());
|
||||
records.push(ImportedRecord {
|
||||
collection,
|
||||
rkey,
|
||||
|
||||
@@ -258,7 +258,7 @@ fn format_account_event(event: &SequencedEvent) -> Result<Vec<u8>, SyncFrameErro
|
||||
did: event.did.clone(),
|
||||
active: event.active.unwrap_or(true),
|
||||
status: event.status.filter(|s| !s.is_active()),
|
||||
seq: event.seq.as_i64(),
|
||||
seq: event.seq,
|
||||
time: format_atproto_time(event.created_at),
|
||||
};
|
||||
let bytes = serialize_event_frame(FrameType::Account, &frame, 256)?;
|
||||
@@ -355,7 +355,7 @@ async fn format_sync_event(
|
||||
did: event.did.clone(),
|
||||
rev,
|
||||
blocks: car_bytes,
|
||||
seq: event.seq.as_i64(),
|
||||
seq: event.seq,
|
||||
time: format_atproto_time(event.created_at),
|
||||
},
|
||||
512,
|
||||
|
||||
@@ -75,7 +75,7 @@ impl RecordValidator {
|
||||
&self,
|
||||
record: &Value,
|
||||
collection: &Nsid,
|
||||
rkey: Option<&str>,
|
||||
rkey: Option<&Rkey>,
|
||||
) -> Result<ValidationStatus, ValidationError> {
|
||||
let (record_type, obj) = validate_preamble(record, collection)?;
|
||||
let registry = tranquil_lexicon::LexiconRegistry::global();
|
||||
@@ -135,7 +135,7 @@ fn validate_preamble<'a>(
|
||||
fn check_banned_content(
|
||||
record_type: &str,
|
||||
obj: &serde_json::Map<String, Value>,
|
||||
rkey: Option<&str>,
|
||||
rkey: Option<&Rkey>,
|
||||
) -> Result<(), ValidationError> {
|
||||
match record_type {
|
||||
"app.bsky.feed.post" => {
|
||||
|
||||
@@ -121,8 +121,10 @@ async fn delete_oldest_tokens_evicts_lowest_created_at() {
|
||||
.expect("list failed");
|
||||
assert_eq!(remaining.len(), 3, "three newest tokens should remain");
|
||||
|
||||
let remaining_ids: std::collections::HashSet<String> =
|
||||
remaining.iter().map(|t| t.token_id.0.clone()).collect();
|
||||
let remaining_ids: std::collections::HashSet<String> = remaining
|
||||
.iter()
|
||||
.map(|t| t.token_id.as_str().to_string())
|
||||
.collect();
|
||||
let expected_ids: std::collections::HashSet<String> = token_ids[2..].iter().cloned().collect();
|
||||
assert_eq!(
|
||||
remaining_ids, expected_ids,
|
||||
|
||||
@@ -17,13 +17,13 @@ fn c(s: &str) -> Nsid {
|
||||
fn test_type_mismatch() {
|
||||
let validator = RecordValidator::new();
|
||||
let record = json!({
|
||||
"$type": "com.example.other",
|
||||
"$type": "cafe.oyster.other",
|
||||
"createdAt": now()
|
||||
});
|
||||
assert!(matches!(
|
||||
validator.validate(&record, "com.example.expected"),
|
||||
validator.validate(&record, &c("cafe.oyster.expected")),
|
||||
Err(ValidationError::TypeMismatch { expected, actual })
|
||||
if expected == "com.example.expected" && actual == "com.example.other"
|
||||
if expected == "cafe.oyster.expected" && actual == "cafe.oyster.other"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ fn test_missing_type() {
|
||||
let validator = RecordValidator::new();
|
||||
let record = json!({"text": "Hello"});
|
||||
assert!(matches!(
|
||||
validator.validate(&record, "com.example.test"),
|
||||
validator.validate(&record, &c("cafe.oyster.record")),
|
||||
Err(ValidationError::MissingType)
|
||||
));
|
||||
}
|
||||
@@ -42,7 +42,7 @@ fn test_not_object() {
|
||||
let validator = RecordValidator::new();
|
||||
let record = json!("just a string");
|
||||
assert!(matches!(
|
||||
validator.validate(&record, "com.example.test"),
|
||||
validator.validate(&record, &c("cafe.oyster.record")),
|
||||
Err(ValidationError::InvalidRecord(_))
|
||||
));
|
||||
}
|
||||
@@ -52,7 +52,9 @@ fn test_unknown_type_lenient() {
|
||||
let validator = RecordValidator::new();
|
||||
let record = json!({"$type": "com.custom.record", "data": "test"});
|
||||
assert_eq!(
|
||||
validator.validate(&record, "com.custom.record").unwrap(),
|
||||
validator
|
||||
.validate(&record, &c("com.custom.record"))
|
||||
.unwrap(),
|
||||
ValidationStatus::Unknown
|
||||
);
|
||||
}
|
||||
@@ -62,7 +64,7 @@ fn test_unknown_type_strict() {
|
||||
let validator = RecordValidator::new().require_lexicon(true);
|
||||
let record = json!({"$type": "com.custom.record", "data": "test"});
|
||||
assert!(matches!(
|
||||
validator.validate(&record, "com.custom.record"),
|
||||
validator.validate(&record, &c("com.custom.record")),
|
||||
Err(ValidationError::UnknownType(_))
|
||||
));
|
||||
}
|
||||
@@ -73,7 +75,7 @@ fn test_datetime_validation() {
|
||||
|
||||
let valid = json!({"$type": "com.custom.record", "createdAt": "2024-01-15T10:30:00.000Z"});
|
||||
assert_eq!(
|
||||
validator.validate(&valid, "com.custom.record").unwrap(),
|
||||
validator.validate(&valid, &c("com.custom.record")).unwrap(),
|
||||
ValidationStatus::Unknown
|
||||
);
|
||||
|
||||
@@ -81,14 +83,14 @@ fn test_datetime_validation() {
|
||||
json!({"$type": "com.custom.record", "createdAt": "2024-01-15T10:30:00+05:30"});
|
||||
assert_eq!(
|
||||
validator
|
||||
.validate(&with_offset, "com.custom.record")
|
||||
.validate(&with_offset, &c("com.custom.record"))
|
||||
.unwrap(),
|
||||
ValidationStatus::Unknown
|
||||
);
|
||||
|
||||
let invalid = json!({"$type": "com.custom.record", "createdAt": "2024/01/15"});
|
||||
assert!(matches!(
|
||||
validator.validate(&invalid, "com.custom.record"),
|
||||
validator.validate(&invalid, &c("com.custom.record")),
|
||||
Err(ValidationError::InvalidDatetime { .. })
|
||||
));
|
||||
}
|
||||
|
||||
@@ -166,10 +166,10 @@ fn test_permissions_whitespace_only() {
|
||||
#[test]
|
||||
fn test_permissions_repo_collection_wildcard_prefix() {
|
||||
let perms = ScopePermissions::from_scope_string(Some("repo:app.bsky.*?action=create"));
|
||||
assert!(perms.allows_repo(RepoAction::Create, "app.bsky.feed.post"));
|
||||
assert!(perms.allows_repo(RepoAction::Create, "app.bsky.actor.profile"));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, "com.atproto.repo.blob"));
|
||||
assert!(!perms.allows_repo(RepoAction::Update, "app.bsky.feed.post"));
|
||||
assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post")));
|
||||
assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.actor.profile")));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, &c("com.atproto.repo.blob")));
|
||||
assert!(!perms.allows_repo(RepoAction::Update, &c("app.bsky.feed.post")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1420,12 +1420,12 @@ async fn parity_repo_root_operations() {
|
||||
let new_root = test_cid(99);
|
||||
let rev1 = Tid::from("rev1".to_string());
|
||||
f.pg.repo
|
||||
.update_repo_root(pg_uid, &new_root, "rev1")
|
||||
.update_repo_root(pg_uid, &new_root, &rev1)
|
||||
.await
|
||||
.unwrap();
|
||||
f.store
|
||||
.repo
|
||||
.update_repo_root(store_uid, &new_root, "rev1")
|
||||
.update_repo_root(store_uid, &new_root, &rev1)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -405,11 +405,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_namespace_authority_single_segment() {
|
||||
assert_eq!(extract_namespace_authority("single"), "single");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_under_authority() {
|
||||
assert!(is_under_authority("io.atcr.manifest", "io.atcr"));
|
||||
|
||||
@@ -350,7 +350,7 @@ mod tests {
|
||||
fn test_atproto_scope_is_identity_only() {
|
||||
let perms = ScopePermissions::from_scope_string(Some("atproto"));
|
||||
assert!(!perms.has_full_access());
|
||||
assert!(!perms.allows_repo(RepoAction::Create, "app.bsky.feed.post"));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post")));
|
||||
assert!(!perms.allows_blob("image/png"));
|
||||
assert!(!perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getTimeline")));
|
||||
assert!(!perms.allows_account(AccountAttr::Email, AccountAction::Manage));
|
||||
@@ -359,7 +359,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_transition_generic_allows_everything() {
|
||||
let perms = ScopePermissions::from_scope_string(Some("transition:generic"));
|
||||
assert!(perms.allows_repo(RepoAction::Create, "app.bsky.feed.post"));
|
||||
assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post")));
|
||||
assert!(perms.allows_blob("image/png"));
|
||||
}
|
||||
|
||||
@@ -410,7 +410,7 @@ mod tests {
|
||||
assert!(perms.allows_email_read());
|
||||
assert!(perms.allows_account(AccountAttr::Email, AccountAction::Read));
|
||||
assert!(!perms.allows_account(AccountAttr::Email, AccountAction::Manage));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, "app.bsky.feed.post"));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -427,10 +427,10 @@ mod tests {
|
||||
let perms = ScopePermissions::from_scope_string(Some(
|
||||
"repo:app.bsky.feed.post?action=create&action=delete",
|
||||
));
|
||||
assert!(perms.allows_repo(RepoAction::Create, "app.bsky.feed.post"));
|
||||
assert!(perms.allows_repo(RepoAction::Delete, "app.bsky.feed.post"));
|
||||
assert!(!perms.allows_repo(RepoAction::Update, "app.bsky.feed.post"));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, "app.bsky.feed.like"));
|
||||
assert!(perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.post")));
|
||||
assert!(perms.allows_repo(RepoAction::Delete, &c("app.bsky.feed.post")));
|
||||
assert!(!perms.allows_repo(RepoAction::Update, &c("app.bsky.feed.post")));
|
||||
assert!(!perms.allows_repo(RepoAction::Create, &c("app.bsky.feed.like")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -561,11 +561,11 @@ mod tests {
|
||||
assert!(perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getAuthorFeed")));
|
||||
assert!(perms.allows_rpc(
|
||||
"did:web:api.bsky.app#bsky_appview",
|
||||
"app.bsky.feed.getAuthorFeed"
|
||||
&c("app.bsky.feed.getAuthorFeed")
|
||||
));
|
||||
assert!(perms.allows_rpc(
|
||||
"did:web:api.bsky.app#other_service",
|
||||
"app.bsky.feed.getAuthorFeed"
|
||||
&c("app.bsky.feed.getAuthorFeed")
|
||||
));
|
||||
assert!(!perms.allows_rpc("did:web:other.app", &c("app.bsky.feed.getAuthorFeed")));
|
||||
assert!(!perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getTimeline")));
|
||||
@@ -579,7 +579,7 @@ mod tests {
|
||||
assert!(perms.allows_rpc("did:web:api.bsky.app", &c("app.bsky.feed.getAuthorFeed")));
|
||||
assert!(perms.allows_rpc(
|
||||
"did:web:api.bsky.app#bsky_appview",
|
||||
"app.bsky.feed.getAuthorFeed"
|
||||
&c("app.bsky.feed.getAuthorFeed")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,10 @@ pub fn encode_payload_with_mutations(
|
||||
prev_cid: event.prev_cid.as_ref().and_then(cid_link_to_bytes),
|
||||
prev_data_cid: event.prev_data_cid.as_ref().and_then(cid_link_to_bytes),
|
||||
ops: ops_bytes,
|
||||
blobs: event.blobs.clone(),
|
||||
blobs: event
|
||||
.blobs
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(|c| c.as_str().to_owned()).collect()),
|
||||
blocks: match event.blocks.as_ref() {
|
||||
Some(EventBlocks::Inline(v)) => Some(v.clone()),
|
||||
Some(EventBlocks::LegacyCids(_)) | None => None,
|
||||
|
||||
@@ -916,7 +916,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::BlobRepository for MetastoreCli
|
||||
self.pool
|
||||
.send(MetastoreRequest::Blob(BlobRequest::ListBlobsSinceRev {
|
||||
did: did.clone(),
|
||||
since: since.to_owned(),
|
||||
since: since.to_string(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
@@ -1515,7 +1515,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::SessionRepository for Metastore
|
||||
self.pool.send(MetastoreRequest::Session(
|
||||
SessionRequest::DeleteSessionsByDidExceptJti {
|
||||
did: did.clone(),
|
||||
except_jti: except_jti.to_owned(),
|
||||
except_jti: except_jti.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -1841,7 +1841,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::Infra(InfraRequest::CreateInviteCode {
|
||||
code: code.to_owned(),
|
||||
code: code.clone(),
|
||||
use_count,
|
||||
for_account: for_account.cloned(),
|
||||
tx,
|
||||
@@ -1876,7 +1876,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::Infra(
|
||||
InfraRequest::GetInviteCodeAvailableUses {
|
||||
code: code.to_owned(),
|
||||
code: code.clone(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -1890,7 +1890,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::Infra(InfraRequest::ValidateInviteCode {
|
||||
code: code.to_owned(),
|
||||
code: code.clone(),
|
||||
tx,
|
||||
}))
|
||||
.map_err(|e| InviteCodeError::DatabaseError(DbError::Connection(e.to_string())))?;
|
||||
@@ -1916,7 +1916,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::Infra(InfraRequest::GetInviteCodeUses {
|
||||
code: code.to_owned(),
|
||||
code: code.to_string(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
@@ -1926,7 +1926,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::Infra(
|
||||
InfraRequest::DisableInviteCodesByCode {
|
||||
codes: codes.to_vec(),
|
||||
codes: codes.iter().map(|c| c.to_string()).collect(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -1978,7 +1978,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::Infra(
|
||||
InfraRequest::GetInviteCodeUsesBatch {
|
||||
codes: codes.to_vec(),
|
||||
codes: codes.iter().map(|c| c.to_string()).collect(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -2003,7 +2003,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::Infra(InfraRequest::GetInviteCodeInfo {
|
||||
code: code.to_owned(),
|
||||
code: code.to_string(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
@@ -2063,7 +2063,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
self.pool
|
||||
.send(MetastoreRequest::Infra(InfraRequest::ReserveSigningKey {
|
||||
did: did.cloned(),
|
||||
public_key_did_key: public_key_did_key.to_owned(),
|
||||
public_key_did_key: public_key_did_key.to_string(),
|
||||
private_key_bytes: private_key_bytes.to_vec(),
|
||||
expires_at,
|
||||
tx,
|
||||
@@ -2078,7 +2078,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::Infra(
|
||||
InfraRequest::GetReservedSigningKey {
|
||||
public_key_did_key: public_key_did_key.to_owned(),
|
||||
public_key_did_key: public_key_did_key.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -2489,7 +2489,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::InfraRepository for MetastoreCl
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::Infra(
|
||||
InfraRequest::GetReservedSigningKeyFull {
|
||||
public_key_did_key: public_key_did_key.to_owned(),
|
||||
public_key_did_key: public_key_did_key.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -3370,7 +3370,7 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool
|
||||
.send(MetastoreRequest::User(UserRequest::GetOAuthTokenWithUser {
|
||||
token_id: token_id.to_owned(),
|
||||
token_id: token_id.to_string(),
|
||||
tx,
|
||||
}))?;
|
||||
recv(rx).await
|
||||
@@ -3447,12 +3447,12 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
|
||||
async fn get_login_check_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginCheck>, DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::User(
|
||||
UserRequest::GetLoginCheckByIdentifier {
|
||||
identifier: identifier.to_owned(),
|
||||
identifier: identifier.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -3461,12 +3461,12 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
|
||||
async fn get_login_info_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginInfo>, DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::User(
|
||||
UserRequest::GetLoginInfoByIdentifier {
|
||||
identifier: identifier.to_owned(),
|
||||
identifier: identifier.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -3698,12 +3698,12 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
|
||||
async fn check_email_verified_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<bool>, DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::User(
|
||||
UserRequest::CheckEmailVerifiedByIdentifier {
|
||||
identifier: identifier.to_owned(),
|
||||
identifier: identifier.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
@@ -4442,12 +4442,12 @@ impl<S: StorageIO + 'static> tranquil_db_traits::UserRepository for MetastoreCli
|
||||
|
||||
async fn get_login_full_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
identifier: &AtIdentifier,
|
||||
) -> Result<Option<UserLoginFull>, DbError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.pool.send(MetastoreRequest::User(
|
||||
UserRequest::GetLoginFullByIdentifier {
|
||||
identifier: identifier.to_owned(),
|
||||
identifier: identifier.to_string(),
|
||||
tx,
|
||||
},
|
||||
))?;
|
||||
|
||||
@@ -203,7 +203,7 @@ impl<S: StorageIO + 'static> CommitOps<S> {
|
||||
|
||||
let mutation_set = CommitMutationSet {
|
||||
new_root_cid: new_cid_bytes.clone(),
|
||||
new_rev: input.new_rev.clone(),
|
||||
new_rev: input.new_rev.to_string(),
|
||||
record_upserts: input
|
||||
.record_upserts
|
||||
.iter()
|
||||
|
||||
@@ -304,7 +304,7 @@ impl<S: StorageIO + 'static> EventOps<S> {
|
||||
|
||||
let user_seqs = self.scan_did_events(user_hash, start_seq)?;
|
||||
|
||||
let mut seen = std::collections::BTreeSet::new();
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
user_seqs
|
||||
.into_iter()
|
||||
.try_fold(Vec::new(), |mut acc, seq_u64| {
|
||||
@@ -318,12 +318,7 @@ impl<S: StorageIO + 'static> EventOps<S> {
|
||||
match self.bridge.get_event_by_seq(seq_sn)? {
|
||||
Some(event) if event.rev.is_some() => {
|
||||
if let Some(blobs) = event.blobs {
|
||||
acc.extend(
|
||||
blobs
|
||||
.into_iter()
|
||||
.filter(|b| seen.insert(b.clone()))
|
||||
.map(CidLink::from),
|
||||
);
|
||||
acc.extend(blobs.into_iter().filter(|b| seen.insert(b.clone())));
|
||||
}
|
||||
Ok(acc)
|
||||
}
|
||||
|
||||
@@ -736,9 +736,7 @@ impl DelegationRequest {
|
||||
| Self::RevokeDelegation { delegated_did, .. }
|
||||
| Self::UpdateDelegationScopes { delegated_did, .. }
|
||||
| Self::GetDelegation { delegated_did, .. }
|
||||
| Self::LogDelegationAction { delegated_did, .. } => {
|
||||
did_to_routing(delegated_did.as_str())
|
||||
}
|
||||
| Self::LogDelegationAction { delegated_did, .. } => did_to_routing(delegated_did),
|
||||
Self::GetAccountsControlledBy { controller_did, .. }
|
||||
| Self::ControlsAnyAccounts {
|
||||
did: controller_did,
|
||||
|
||||
@@ -374,7 +374,7 @@ impl InfraOps {
|
||||
}
|
||||
|
||||
let value = InviteCodeValue {
|
||||
code: code.to_owned(),
|
||||
code: code.as_str().to_owned(),
|
||||
available_uses: use_count,
|
||||
disabled: false,
|
||||
for_account: for_account.map(|d| d.to_string()),
|
||||
@@ -400,7 +400,7 @@ impl InfraOps {
|
||||
|
||||
codes.iter().try_for_each(|code| {
|
||||
let value = InviteCodeValue {
|
||||
code: code.clone(),
|
||||
code: code.as_str().to_owned(),
|
||||
available_uses: use_count,
|
||||
disabled: false,
|
||||
for_account: for_account.map(|d| d.to_string()),
|
||||
|
||||
@@ -247,7 +247,7 @@ impl OAuthOps {
|
||||
refresh_token: data
|
||||
.current_refresh_token
|
||||
.as_ref()
|
||||
.map(|r| r.0.clone())
|
||||
.map(|r| r.to_string())
|
||||
.unwrap_or_default(),
|
||||
previous_refresh_token: None,
|
||||
scope: data.scope.clone().unwrap_or_default(),
|
||||
@@ -877,7 +877,7 @@ impl OAuthOps {
|
||||
) -> Result<(), MetastoreError> {
|
||||
let now_ms = Utc::now().timestamp_millis();
|
||||
let value = OAuthDeviceValue {
|
||||
session_id: data.session_id.0.clone(),
|
||||
session_id: data.session_id.as_str().to_owned(),
|
||||
user_agent: data.user_agent.clone(),
|
||||
ip_address: data.ip_address.clone(),
|
||||
last_seen_at_ms: data.last_seen_at.timestamp_millis(),
|
||||
@@ -900,7 +900,7 @@ impl OAuthOps {
|
||||
)?;
|
||||
|
||||
Ok(val.map(|v| DeviceData {
|
||||
session_id: tranquil_oauth::SessionId(v.session_id),
|
||||
session_id: tranquil_oauth::SessionId::from(v.session_id),
|
||||
user_agent: v.user_agent,
|
||||
ip_address: v.ip_address,
|
||||
last_seen_at: DateTime::from_timestamp_millis(v.last_seen_at_ms).unwrap_or_default(),
|
||||
|
||||
@@ -2931,7 +2931,7 @@ impl UserOps {
|
||||
|
||||
let user_hash = UserHash::from_did(input.did.as_str());
|
||||
let recovery = RecoveryTokenValue {
|
||||
token_hash: input.setup_token_hash.clone(),
|
||||
token_hash: input.setup_token_hash.as_str().to_owned(),
|
||||
expires_at_ms: input.setup_expires_at.timestamp_millis(),
|
||||
};
|
||||
self.users
|
||||
|
||||
@@ -98,10 +98,9 @@ pub async fn list_blobs(
|
||||
.list_blobs_since_rev(&did, &Tid::from(since.clone()))
|
||||
.await
|
||||
.map(|cids| {
|
||||
let mut cid_strs: Vec<String> = cids.into_iter().map(|c| c.to_string()).collect();
|
||||
cid_strs.sort();
|
||||
cid_strs
|
||||
.into_iter()
|
||||
let mut cids = cids;
|
||||
cids.sort();
|
||||
cids.into_iter()
|
||||
.filter(|c| c.as_str() > cursor_cid)
|
||||
.take(usize::try_from(limit + 1).unwrap_or(0))
|
||||
.collect()
|
||||
@@ -112,7 +111,6 @@ pub async fn list_blobs(
|
||||
.blob
|
||||
.list_blobs_by_user(user_id, Some(cursor_cid), limit + 1)
|
||||
.await
|
||||
.map(|cids| cids.into_iter().map(|c| c.to_string()).collect())
|
||||
};
|
||||
match cids_result {
|
||||
Ok(cids) => {
|
||||
|
||||
@@ -118,6 +118,7 @@ pub async fn list_repos(
|
||||
let rev = get_rev_from_commit(&state, &cid_str)
|
||||
.await
|
||||
.or_else(|| row.repo_rev.clone())
|
||||
.map(|t| t.into_inner())
|
||||
.unwrap_or_default();
|
||||
let status = if row.takedown_ref.is_some() {
|
||||
AccountStatus::Takendown
|
||||
@@ -131,7 +132,7 @@ pub async fn list_repos(
|
||||
head: cid_str,
|
||||
rev,
|
||||
active: status.is_active(),
|
||||
status: status.for_firehose_typed(),
|
||||
status: status.for_firehose(),
|
||||
});
|
||||
}
|
||||
let next_cursor = if has_more {
|
||||
@@ -203,7 +204,7 @@ pub async fn get_repo_status(
|
||||
Json(GetRepoStatusOutput {
|
||||
did: account.did,
|
||||
active: account.status.is_active(),
|
||||
status: account.status.for_firehose_typed(),
|
||||
status: account.status.for_firehose(),
|
||||
rev,
|
||||
}),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user