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, Role};
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 = state
.credentials
.to_identity(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()
.as_ref()
.and_then(|cat| cat.list_user_grant_databases(user.user_id).ok())
.unwrap_or_else(|| vec![DatabaseId::DEFAULT]);
DatabaseSet::Some(SmallVec::from_iter(db_ids))
}
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) -> AuthenticatedIdentity {
if let Some(id) = state.credentials.to_identity(username, AuthMethod::Trust) {
id
} else {
AuthenticatedIdentity {
user_id: 0,
username: username.to_string(),
tenant_id: TenantId::new(1),
auth_method: AuthMethod::Trust,
roles: vec![Role::Superuser],
is_superuser: true,
default_database: None,
accessible_databases: DatabaseSet::All,
}
}
}