db: newtype Rkey and final newtype touches for now

Lewis: May this revision serve well! <lu5a@proton.me>
This commit is contained in:
Lewis
2026-07-12 08:03:14 +02:00
parent fbfa15b0b4
commit e41f34746a
68 changed files with 326 additions and 331 deletions
@@ -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")?;
+2 -4
View File
@@ -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)
+1 -1
View File
@@ -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);
-2
View File
@@ -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)?,
}
};
+20 -6
View File
@@ -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,
+2 -6
View File
@@ -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(&current_handle_typed)
.get_did_web_info_by_handle(&current_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)
},
)?;
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -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(
+3 -6
View File
@@ -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,
}))
}
+1 -1
View File
@@ -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();
}
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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),
}
}
}
+6 -4
View File
@@ -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,
+1 -8
View File
@@ -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),
+4 -4
View File
@@ -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(
+1 -1
View File
@@ -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
+47 -16
View File
@@ -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
+6 -7
View File
@@ -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,
+12 -4
View File
@@ -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()),
+1 -1
View File
@@ -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
+11 -11
View File
@@ -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
+4 -4
View File
@@ -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(),
})
}
+1 -1
View File
@@ -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 } => {
+1 -4
View File
@@ -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),
}))
}
+15 -44
View File
@@ -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>,
+2 -2
View File
@@ -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) => {
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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(
+10 -7
View File
@@ -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")
));
}
}
+7 -7
View File
@@ -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"));
}
}
+1 -3
View File
@@ -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,
})
}
-2
View File
@@ -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"),
+7 -6
View File
@@ -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(),
+3 -3
View File
@@ -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,
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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,
+12 -10
View File
@@ -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]
+2 -2
View File
@@ -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"));
+10 -10
View File
@@ -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,
+21 -21
View File
@@ -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
+3 -5
View File
@@ -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) => {
+3 -2
View File
@@ -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,
}),
)