Proxy Shared User Drafts

This commit is contained in:
Alexey
2026-09-14 20:19:48 +03:00
parent b37f1ebdeb
commit 0ac236955a
59 changed files with 1485 additions and 401 deletions
-17
View File
@@ -21,7 +21,6 @@ pub(super) async fn create_user_route(
}
let expected_revision = parse_if_match(req.headers());
let body = read_json::<CreateUserRequest>(req.into_body(), body_limit).await?;
let requested_enabled = body.enabled;
let result = create_user(body, expected_revision, shared).await;
let (mut data, revision) = match result {
Ok(ok) => ok,
@@ -34,22 +33,6 @@ pub(super) async fn create_user_route(
};
let runtime_cfg = config_rx.borrow().clone();
data.user.in_runtime = runtime_cfg.access.users.contains_key(&data.user.username);
if let Some(enabled) = requested_enabled {
let (_, cancelled) = shared
.proxy_shared
.set_user_enabled(&data.user.username, enabled);
if !enabled {
if cancelled > 0 {
shared.runtime_events.record(
"api.user.disable.runtime",
format!(
"username={} cancelled_sessions={}",
data.user.username, cancelled
),
);
}
}
}
shared.runtime_events.record(
"api.user.create.ok",
format!("username={}", data.user.username),
+2 -28
View File
@@ -61,7 +61,6 @@ pub(super) async fn handle(
};
let runtime_cfg = config_rx.borrow().clone();
data.in_runtime = runtime_cfg.access.users.contains_key(&data.username);
shared.proxy_shared.set_user_enabled(base_user, true);
shared
.runtime_events
.record("api.user.enable.ok", format!("username={}", base_user));
@@ -104,13 +103,9 @@ pub(super) async fn handle(
};
let runtime_cfg = config_rx.borrow().clone();
data.in_runtime = runtime_cfg.access.users.contains_key(&data.username);
let (newly_disabled, cancelled) = shared.proxy_shared.set_user_enabled(base_user, false);
shared.runtime_events.record(
"api.user.disable.ok",
format!(
"username={} newly_disabled={} cancelled_sessions={}",
base_user, newly_disabled, cancelled
),
format!("username={}", base_user),
);
let status = if data.in_runtime {
StatusCode::OK
@@ -270,11 +265,6 @@ pub(super) async fn handle(
}
let expected_revision = parse_if_match(req.headers());
let body = read_json::<PatchUserRequest>(req.into_body(), body_limit).await?;
let enabled_update = match &body.enabled {
Patch::Unchanged => None,
Patch::Remove => Some(true),
Patch::Set(enabled) => Some(*enabled),
};
let result = patch_user(user, body, expected_revision, shared).await;
let (mut data, revision) = match result {
Ok(ok) => ok,
@@ -288,20 +278,6 @@ pub(super) async fn handle(
};
let runtime_cfg = config_rx.borrow().clone();
data.in_runtime = runtime_cfg.access.users.contains_key(&data.username);
if let Some(enabled) = enabled_update {
let (_, cancelled) = shared
.proxy_shared
.set_user_enabled(&data.username, enabled);
if !enabled {
shared.runtime_events.record(
"api.user.disable.runtime",
format!(
"username={} cancelled_sessions={}",
data.username, cancelled
),
);
}
}
shared
.runtime_events
.record("api.user.patch.ok", format!("username={}", data.username));
@@ -335,11 +311,9 @@ pub(super) async fn handle(
return Err(error);
}
};
shared.proxy_shared.set_user_enabled(&deleted_user, true);
let cancelled = shared.proxy_shared.cancel_user_sessions(&deleted_user);
shared.runtime_events.record(
"api.user.delete.ok",
format!("username={} cancelled_sessions={}", deleted_user, cancelled),
format!("username={}", deleted_user),
);
let runtime_cfg = config_rx.borrow().clone();
let in_runtime = runtime_cfg.access.users.contains_key(&deleted_user);
-1
View File
@@ -70,7 +70,6 @@ use model::{
PatchUserRequest, ResetUserQuotaResponse, RotateSecretRequest, SummaryData, UserActiveIps,
is_valid_username,
};
use patch::Patch;
use runtime_edge::{
EdgeConnectionsCacheEntry, build_runtime_connections_summary_data,
build_runtime_events_recent_data, build_runtime_tls_fingerprints_data,
+9 -1
View File
@@ -124,7 +124,14 @@ pub(in crate::api) async fn create_user(
let revision =
save_access_sections_to_disk(&shared.config_path, &cfg, &touched_sections).await?;
drop(_guard);
shared
.proxy_shared
.stage_user(
&body.username,
&secret,
cfg.access.is_user_enabled(&body.username),
)
.ok_or_else(|| ApiFailure::internal("failed to stage user admission policy"))?;
if let Some(limit) = updated_limit {
shared
@@ -132,6 +139,7 @@ pub(in crate::api) async fn create_user(
.set_user_limit(&body.username, limit)
.await;
}
drop(_guard);
let (detected_ip_v4, detected_ip_v6) = shared.detected_link_ips();
let users = users_from_config(
+10 -2
View File
@@ -31,6 +31,10 @@ pub(in crate::api) async fn rotate_secret(
.map_err(|e| ApiFailure::bad_request(format!("config validation failed: {}", e)))?;
let revision =
save_access_sections_to_disk(&shared.config_path, &cfg, &[AccessSection::Users]).await?;
shared
.proxy_shared
.stage_user(user, &secret, cfg.access.is_user_enabled(user))
.ok_or_else(|| ApiFailure::internal("failed to stage rotated user credential"))?;
drop(_guard);
let (detected_ip_v4, detected_ip_v6) = shared.detected_link_ips();
@@ -109,6 +113,7 @@ pub(in crate::api) async fn delete_user(
.map_err(|e| ApiFailure::bad_request(format!("config validation failed: {}", e)))?;
let revision =
save_access_sections_to_disk(&shared.config_path, &cfg, &touched_sections).await?;
let deleted_incarnation = shared.proxy_shared.delete_user(user).incarnation;
let configured_users = cfg.access.users.keys().cloned().collect();
if let Err(error) = shared
.quota_state
@@ -121,9 +126,12 @@ pub(in crate::api) async fn delete_user(
"Deleted user quota checkpoint cleanup will be reconciled on restart"
);
}
drop(_guard);
shared.ip_tracker.remove_user_limit(user).await;
shared.ip_tracker.clear_user_ips(user).await;
shared
.ip_tracker
.clear_user_ips_if_not_newer(user, deleted_incarnation)
.await;
drop(_guard);
Ok((user.to_string(), revision))
}
+21 -1
View File
@@ -170,12 +170,23 @@ pub(in crate::api) async fn patch_user(
} else {
save_access_sections_to_disk(&shared.config_path, &cfg, &touched_sections).await?
};
drop(_guard);
if touches_users || touches_user_enabled {
let secret = cfg
.access
.users
.get(user)
.ok_or_else(|| ApiFailure::internal("updated user secret is missing"))?;
shared
.proxy_shared
.stage_user(user, secret, cfg.access.is_user_enabled(user))
.ok_or_else(|| ApiFailure::internal("failed to stage user admission policy"))?;
}
match max_unique_ips_change {
Some(Some(limit)) => shared.ip_tracker.set_user_limit(user, limit).await,
Some(None) => shared.ip_tracker.remove_user_limit(user).await,
None => {}
}
drop(_guard);
let (detected_ip_v4, detected_ip_v6) = shared.detected_link_ips();
let users = users_from_config(
&cfg,
@@ -223,6 +234,15 @@ pub(in crate::api) async fn set_user_enabled(
let revision =
save_access_sections_to_disk(&shared.config_path, &cfg, &[AccessSection::UserEnabled])
.await?;
let secret = cfg
.access
.users
.get(user)
.ok_or_else(|| ApiFailure::internal("updated user secret is missing"))?;
shared
.proxy_shared
.stage_user(user, secret, enabled)
.ok_or_else(|| ApiFailure::internal("failed to stage user admission policy"))?;
drop(_guard);
let (detected_ip_v4, detected_ip_v6) = shared.detected_link_ips();