use nodedb_types::id::DatabaseId;
use smallvec::SmallVec;
use crate::control::security::audit::AuditEvent;
use crate::control::security::credential::record::UserRecord;
use crate::control::security::identity::{AuthMethod, AuthenticatedIdentity, DatabaseSet};
use crate::control::state::SharedState;
use crate::types::TenantId;
pub fn resolve_certificate_identity(
state: &SharedState,
cn: &str,
peer_addr: &str,
) -> crate::Result<AuthenticatedIdentity> {
let identity = stored_user_identity(state, cn, AuthMethod::Certificate).ok_or_else(|| {
state.audit_record(
AuditEvent::AuthFailure,
None,
peer_addr,
&format!("mTLS auth failed: no user for cert CN '{cn}'"),
);
state.auth_metrics.record_auth_failure("certificate");
crate::Error::RejectedAuthz {
tenant_id: TenantId::new(0),
resource: format!("no user mapped to certificate CN '{cn}'"),
}
})?;
state.audit_record(
AuditEvent::AuthSuccess,
Some(identity.tenant_id),
peer_addr,
&format!("mTLS cert auth: {cn}"),
);
state.auth_metrics.record_auth_success("certificate");
Ok(identity)
}
fn build_owner_database_set(state: &SharedState, user: &UserRecord) -> DatabaseSet {
if user.is_superuser {
return DatabaseSet::All;
}
if user.is_service_account && !user.accessible_databases.is_empty() {
return DatabaseSet::Some(SmallVec::from_iter(
user.accessible_databases.iter().copied(),
));
}
let db_ids = state
.credentials
.catalog()
.list_user_grant_databases(user.user_id)
.ok()
.unwrap_or_else(|| vec![DatabaseId::DEFAULT]);
DatabaseSet::Some(SmallVec::from_iter(db_ids))
}
pub fn stored_user_identity(
state: &SharedState,
username: &str,
method: AuthMethod,
) -> Option<AuthenticatedIdentity> {
let user = state.credentials.get_user(username)?;
let mut identity = state.credentials.to_identity(username, method)?;
identity.default_database =
(user.default_database_id != 0).then(|| DatabaseId::new(user.default_database_id));
identity.accessible_databases = build_owner_database_set(state, &user);
Some(identity)
}
pub fn verify_api_key_identity(
state: &SharedState,
token: &str,
peer_addr: &str,
protocol: &str,
) -> Option<AuthenticatedIdentity> {
let key_record = state.api_keys.verify_key(token)?;
let user = state.credentials.get_user(&key_record.username)?;
let owner_set = build_owner_database_set(state, &user);
let key_set = if key_record.accessible_databases.is_empty() {
owner_set.clone()
} else {
DatabaseSet::Some(SmallVec::from_iter(
key_record.accessible_databases.iter().copied(),
))
};
let effective = owner_set.intersect(&key_set);
let identity =
state
.api_keys
.to_identity(&key_record, user.roles, user.is_superuser, effective);
state.audit_record(
AuditEvent::AuthSuccess,
Some(identity.tenant_id),
peer_addr,
&format!(
"{protocol} api_key auth: {} (key {})",
identity.username, key_record.key_id
),
);
state.auth_metrics.record_auth_success("api_key");
Some(identity)
}
pub fn trust_identity(state: &SharedState, username: &str) -> Option<AuthenticatedIdentity> {
stored_user_identity(state, username, AuthMethod::Trust)
}
pub fn configured_trust_identity(state: &SharedState) -> Option<AuthenticatedIdentity> {
let username = state.credentials.configured_trust_superuser().ok()??;
stored_user_identity(state, &username, AuthMethod::Trust)
}