v.34.1
This commit is contained in:
1 parent
4afa88a65e
commit
2ab44c4c62
49 files changed
+1523
-176
No files matched your search
@@ -0,0 +1,119 @@
|
||||
// General-purpose API credentials are independent of browser sessions,
|
||||
// certificate deployment credentials, and the Prometheus scrape credential.
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct CreateApiToken {
|
||||
permission: String,
|
||||
#[serde(default)]
|
||||
comment: String,
|
||||
allowed_cidrs: Vec<String>,
|
||||
}
|
||||
|
||||
async fn list_api_tokens(State(state): State<Arc<App>>) -> Result<Json<Value>, ApiError> {
|
||||
let tokens = state.store.api_tokens().await.map_err(auth::internal)?;
|
||||
Ok(Json(
|
||||
json!({"tokens": tokens, "max_tokens": crate::db::API_TOKEN_CAPACITY}),
|
||||
))
|
||||
}
|
||||
|
||||
async fn create_api_token(
|
||||
State(state): State<Arc<App>>,
|
||||
Extension(session): Extension<Session>,
|
||||
Json(input): Json<CreateApiToken>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
if !matches!(input.permission.as_str(), "ro" | "rw")
|
||||
|| input.comment.len() > 128
|
||||
|| input.comment.chars().any(char::is_control)
|
||||
|| input.allowed_cidrs.is_empty()
|
||||
|| input.allowed_cidrs.len() > 32
|
||||
|| input.allowed_cidrs.iter().any(|v| {
|
||||
v.parse::<ipnet::IpNet>()
|
||||
.ok()
|
||||
.is_none_or(|net| net.prefix_len() == 0)
|
||||
})
|
||||
{
|
||||
return Err(ApiError(
|
||||
StatusCode::UNPROCESSABLE_ENTITY,
|
||||
"invalid API permission, comment or CIDR allowlist".into(),
|
||||
));
|
||||
}
|
||||
let secret = auth::random_secret();
|
||||
let id = format!("api-{}", &secret[..12]);
|
||||
let inserted = state
|
||||
.store
|
||||
.create_api_token(
|
||||
&id,
|
||||
&auth::secret_hash(&secret),
|
||||
&input.permission,
|
||||
&input.comment,
|
||||
&input.allowed_cidrs,
|
||||
&session.username,
|
||||
)
|
||||
.await
|
||||
.map_err(auth::internal)?;
|
||||
if !inserted {
|
||||
return Err(ApiError(
|
||||
StatusCode::CONFLICT,
|
||||
"API token already exists".into(),
|
||||
));
|
||||
}
|
||||
Ok(Json(
|
||||
json!({"id": id, "token": secret, "permission": input.permission, "allowed_cidrs": input.allowed_cidrs}),
|
||||
))
|
||||
}
|
||||
async fn revoke_api_token(
|
||||
State(state): State<Arc<App>>,
|
||||
Extension(session): Extension<Session>,
|
||||
Path(id): Path<String>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
if !crate::config::valid_id(&id) {
|
||||
return Err(ApiError(StatusCode::NOT_FOUND, "unknown API token".into()));
|
||||
}
|
||||
if !state
|
||||
.store
|
||||
.revoke_api_token(&id, &session.username)
|
||||
.await
|
||||
.map_err(auth::internal)?
|
||||
{
|
||||
return Err(ApiError(StatusCode::NOT_FOUND, "unknown API token".into()));
|
||||
}
|
||||
Ok(Json(json!({"revoked": id})))
|
||||
}
|
||||
async fn prometheus_token_status(State(state): State<Arc<App>>) -> Result<Json<Value>, ApiError> {
|
||||
let created_at = state
|
||||
.store
|
||||
.prometheus_token_status()
|
||||
.await
|
||||
.map_err(auth::internal)?;
|
||||
Ok(Json(
|
||||
json!({"configured": created_at.is_some(), "created_at": created_at}),
|
||||
))
|
||||
}
|
||||
async fn rotate_prometheus_token(
|
||||
State(state): State<Arc<App>>,
|
||||
Extension(session): Extension<Session>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
let secret = auth::random_secret();
|
||||
let hash = auth::secret_hash(&secret);
|
||||
state
|
||||
.store
|
||||
.rotate_prometheus_token(&hash, &session.username)
|
||||
.await
|
||||
.map_err(auth::internal)?;
|
||||
state.prometheus_token_hash.store(Some(Arc::new(hash)));
|
||||
Ok(Json(json!({"configured": true, "token": secret})))
|
||||
}
|
||||
async fn revoke_prometheus_token(
|
||||
State(state): State<Arc<App>>,
|
||||
Extension(session): Extension<Session>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
state
|
||||
.store
|
||||
.revoke_prometheus_token(&session.username)
|
||||
.await
|
||||
.map_err(auth::internal)?;
|
||||
state
|
||||
.prometheus_token_hash
|
||||
.store(Some(Arc::new("revoked".into())));
|
||||
Ok(Json(json!({"configured": false})))
|
||||
}
|
||||
Reference in new issue
Block a user