lgtm
This commit is contained in:
parent
084117cea8
commit
a8de0a614d
36 changed files with 1329 additions and 522 deletions
|
|
@ -193,13 +193,31 @@ async fn verify_is_admin(
|
|||
Ok(body["is_admin"].as_bool().unwrap_or(false))
|
||||
}
|
||||
|
||||
async fn lookup_unused_invite(
|
||||
fn redeemable_invite_filter(code: &str, user_id: &str) -> Result<String, &'static str> {
|
||||
validate_invite_code(code)?;
|
||||
if user_id.is_empty()
|
||||
|| user_id.len() > 32
|
||||
|| !user_id.bytes().all(|b| b.is_ascii_alphanumeric())
|
||||
{
|
||||
return Err("Invalid user id");
|
||||
}
|
||||
Ok(format!(
|
||||
"code=\"{}\" && (used_by_id=\"\" || used_by_id=\"{}\")",
|
||||
code, user_id
|
||||
))
|
||||
}
|
||||
|
||||
async fn lookup_redeemable_invite(
|
||||
state: &AppState,
|
||||
pb_url: &str,
|
||||
token: &str,
|
||||
code: &str,
|
||||
user_id: &str,
|
||||
) -> Result<Option<serde_json::Value>, Response> {
|
||||
let filter = format!("code=\"{}\" && used_by_id=\"\"", code);
|
||||
let filter = match redeemable_invite_filter(code, user_id) {
|
||||
Ok(filter) => filter,
|
||||
Err(msg) => return Err((StatusCode::BAD_REQUEST, msg).into_response()),
|
||||
};
|
||||
let lookup_url = format!(
|
||||
"{pb_url}/api/collections/invites/records?filter={}&perPage=1",
|
||||
urlencoding::encode(&filter)
|
||||
|
|
@ -590,7 +608,7 @@ pub async fn post_redeem_invite(
|
|||
}
|
||||
};
|
||||
|
||||
let invite = match lookup_unused_invite(&state, pb_url, &token, &req.code).await {
|
||||
let invite = match lookup_redeemable_invite(&state, pb_url, &token, &req.code, &user.id).await {
|
||||
Ok(Some(invite)) => invite,
|
||||
Ok(None) => {
|
||||
return (StatusCode::NOT_FOUND, "Invalid or already used invite code").into_response()
|
||||
|
|
@ -617,13 +635,17 @@ pub async fn post_redeem_invite(
|
|||
return StatusCode::BAD_GATEWAY.into_response();
|
||||
}
|
||||
};
|
||||
let used_by_id = invite["used_by_id"].as_str().unwrap_or_default();
|
||||
if !used_by_id.is_empty() && used_by_id != user.id {
|
||||
return (StatusCode::NOT_FOUND, "Invalid or already used invite code").into_response();
|
||||
}
|
||||
|
||||
if invite_type == "admin" {
|
||||
if let Err(response) = grant_license_for_invite(&state, pb_url, &token, &user.id).await {
|
||||
if let Err(response) = mark_invite_used(&state, pb_url, &token, invite_id, &user.id).await {
|
||||
return response;
|
||||
}
|
||||
|
||||
if let Err(response) = mark_invite_used(&state, pb_url, &token, invite_id, &user.id).await {
|
||||
if let Err(response) = grant_license_for_invite(&state, pb_url, &token, &user.id).await {
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -635,6 +657,10 @@ pub async fn post_redeem_invite(
|
|||
.into_response();
|
||||
}
|
||||
|
||||
if !used_by_id.is_empty() {
|
||||
return (StatusCode::NOT_FOUND, "Invalid or already used invite code").into_response();
|
||||
}
|
||||
|
||||
match active_referral_checkout_user(&state, invite_id).await {
|
||||
Ok(Some(active_user_id)) if active_user_id != user.id => {
|
||||
return (
|
||||
|
|
@ -663,6 +689,26 @@ pub async fn post_redeem_invite(
|
|||
.into_response()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn redeemable_invite_filter_allows_unused_or_same_user_invite() {
|
||||
let filter = redeemable_invite_filter("abc123", "user123").unwrap();
|
||||
assert_eq!(
|
||||
filter,
|
||||
"code=\"abc123\" && (used_by_id=\"\" || used_by_id=\"user123\")"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redeemable_invite_filter_rejects_unsafe_values() {
|
||||
assert!(redeemable_invite_filter("bad-code", "user123").is_err());
|
||||
assert!(redeemable_invite_filter("abc123", "bad-user").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
/// List invites. Users only see invites they created, including admins.
|
||||
pub async fn get_invites(
|
||||
State(shared): State<Arc<SharedState>>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue