rate limit token creation on CLOUD_HOSTED (10/min per user) (#8664)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@@ -16568,6 +16568,7 @@ dependencies = [
|
||||
"argon2",
|
||||
"axum 0.8.4",
|
||||
"chrono",
|
||||
"dashmap 6.1.0",
|
||||
"http 1.4.0",
|
||||
"hyper 1.9.0",
|
||||
"lazy_static",
|
||||
|
||||
@@ -21,6 +21,7 @@ windmill-api-auth.workspace = true
|
||||
windmill-audit.workspace = true
|
||||
windmill-git-sync.workspace = true
|
||||
|
||||
dashmap.workspace = true
|
||||
argon2.workspace = true
|
||||
axum.workspace = true
|
||||
chrono.workspace = true
|
||||
|
||||
@@ -12,6 +12,7 @@ use sqlx::{Postgres, Transaction};
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
use std::sync::LazyLock;
|
||||
use std::time::Duration;
|
||||
|
||||
use windmill_api_auth::ApiAuthed;
|
||||
@@ -60,6 +61,43 @@ use windmill_git_sync::handle_deployment_metadata;
|
||||
|
||||
pub const COOKIE_PATH: &str = "/";
|
||||
|
||||
const TOKEN_CREATE_LIMIT_PER_MINUTE: i32 = 10;
|
||||
|
||||
struct TokenRateLimitEntry {
|
||||
count: i32,
|
||||
minute_bucket: i64,
|
||||
}
|
||||
|
||||
static TOKEN_CREATE_RATE_LIMIT: LazyLock<dashmap::DashMap<String, TokenRateLimitEntry>> =
|
||||
LazyLock::new(dashmap::DashMap::new);
|
||||
|
||||
fn check_token_create_rate_limit(username: &str) -> Result<()> {
|
||||
if !*CLOUD_HOSTED {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let current_minute = chrono::Utc::now().timestamp() / 60;
|
||||
|
||||
let mut entry = TOKEN_CREATE_RATE_LIMIT
|
||||
.entry(username.to_string())
|
||||
.or_insert(TokenRateLimitEntry { count: 0, minute_bucket: current_minute });
|
||||
|
||||
if entry.minute_bucket != current_minute {
|
||||
entry.count = 0;
|
||||
entry.minute_bucket = current_minute;
|
||||
}
|
||||
|
||||
if entry.count >= TOKEN_CREATE_LIMIT_PER_MINUTE {
|
||||
return Err(Error::Generic(
|
||||
StatusCode::TOO_MANY_REQUESTS,
|
||||
"Too many token creation requests. Please try again later.".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
entry.count += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
.route("/list", get(list_users))
|
||||
@@ -1975,6 +2013,8 @@ async fn create_token(
|
||||
authed: ApiAuthed,
|
||||
Json(token_config): Json<NewToken>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
check_token_create_rate_limit(&authed.username)?;
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let token = create_token_internal(&mut *tx, &db, &authed, token_config).await?;
|
||||
|
||||
Reference in New Issue
Block a user