use crate::types::{DatabaseId, TenantId};
use super::Session;
impl Session {
pub(super) fn resolve_database(
identity: &crate::control::security::identity::AuthenticatedIdentity,
explicit: Option<DatabaseId>,
) -> DatabaseId {
if let Some(db) = explicit {
return db;
}
if let Some(db) = identity.default_database {
return db;
}
DatabaseId::DEFAULT
}
pub(super) async fn handle_auth_frame(
&mut self,
body: &serde_json::Value,
) -> crate::Result<Vec<u8>> {
let (identity, warning) = super::super::session_auth::authenticate(
&self.state,
&self.auth_mode,
body,
&self.peer_addr.to_string(),
)
.await?;
let explicit_db = if let Some(db_name) = body["database"].as_str() {
if db_name.is_empty() {
None
} else {
let resolved = self
.state
.credentials
.catalog()
.get_database_id_by_name(db_name)
.ok()
.flatten();
match resolved {
Some(db_id) => Some(db_id),
None => {
let msg = format!(
r#"{{"status":"error","code":"DATABASE_NOT_FOUND","error":"database '{db_name}' does not exist"}}"#
);
return Ok(msg.into_bytes());
}
}
}
} else {
None
};
let resolved_db = Self::resolve_database(&identity, explicit_db);
if !identity.can_access_database(resolved_db) {
let msg =
r#"{"status":"error","code":"ACCESS_DENIED","error":"access denied to database"}"#;
return Ok(msg.as_bytes().to_vec());
}
self.current_database = Some(resolved_db);
let warning_field = match &warning {
Some(w) => format!(r#","warning":"{}""#, w.replace('"', "'")),
None => String::new(),
};
let resp = format!(
r#"{{"status":"ok","username":"{}","tenant_id":{}{}}}"#,
identity.username,
identity.tenant_id.as_u64(),
warning_field
);
let token_expiry_ms =
if identity.auth_method == crate::control::security::identity::AuthMethod::OidcBearer {
body["token"].as_str().and_then(extract_jwt_exp_ms)
} else {
None
};
self.register_session(&identity, token_expiry_ms);
self.identity = Some(identity);
Ok(resp.into_bytes())
}
pub(super) fn ensure_authenticated(&mut self) -> crate::Result<()> {
if self.identity.is_none() {
if self.auth_mode == crate::config::auth::AuthMode::Trust {
let trust_id = super::super::session_auth::configured_trust_identity(&self.state)
.ok_or_else(|| crate::Error::RejectedAuthz {
tenant_id: TenantId::new(0),
resource: "configured trust identity is unavailable".into(),
})?;
self.register_session(&trust_id, None);
self.identity = Some(trust_id);
} else {
return Err(crate::Error::RejectedAuthz {
tenant_id: TenantId::new(0),
resource: r#"not authenticated. Send {"op":"auth",...} first."#.into(),
});
}
}
Ok(())
}
}
fn extract_jwt_exp_ms(token: &str) -> Option<u64> {
let parts: Vec<&str> = token.splitn(3, '.').collect();
let payload_b64 = parts.get(1)?;
let bytes = crate::control::security::util::base64_url_decode(payload_b64)?;
let claims: serde_json::Value = sonic_rs::from_slice(&bytes).ok()?;
let exp = claims["exp"].as_u64()?;
if exp == 0 {
None
} else {
Some(exp.saturating_mul(1000))
}
}