use async_trait::async_trait; use export::{AuthApi, KenAuthError}; use tonic::Request; use crate::AuthService; use crate::api::context::RequestMeta; use crate::protos::auth as proto; use crate::protos::auth::internal::v1::authority_service_server::AuthorityService; use crate::protos::auth::v1::identity_service_server::IdentityService; use crate::protos::auth::v1::mfa_service_server::MfaService; use crate::protos::auth::v1::session_service_server::SessionService; use crate::protos::auth::v1::ssh_key_service_server::SshKeyService; pub mod authority; pub mod context; pub mod convert; pub mod error; pub mod identity; pub mod mfa; pub mod session; pub mod ssh_key; pub mod store; impl AuthService { fn identity(&self) -> crate::api::identity::IdentityApi { crate::api::identity::IdentityApi::new(self.clone()) } fn authority(&self) -> crate::api::authority::AuthorityApi { crate::api::authority::AuthorityApi::new(self.clone()) } fn session(&self) -> crate::api::session::SessionApi { crate::api::session::SessionApi::new(self.clone()) } fn ssh_key(&self) -> crate::api::ssh_key::SshKeyApi { crate::api::ssh_key::SshKeyApi::new(self.clone()) } fn mfa(&self) -> crate::api::mfa::MfaApi { crate::api::mfa::MfaApi::new(self.clone()) } pub fn read_request(&self, message: T) -> Request { let mut request = Request::new(message); request.set_timeout(self.read_timeout); request.extensions_mut().insert(RequestMeta::internal()); request } pub fn write_request(&self, message: T) -> Request { let mut request = Request::new(message); request.set_timeout(self.write_timeout); request.extensions_mut().insert(RequestMeta::internal()); request } } #[async_trait] impl AuthApi for AuthService { async fn check_health(&self) -> Result<(), KenAuthError> { self.cache() .ping() .await .map_err(|error| KenAuthError::Unavailable(error.to_string())) } #[allow(clippy::too_many_arguments)] async fn change_password( &self, user_id: i64, old_password_hash: &str, new_password_hash: &str, keep_session_id: &str, client_ip: &str, ) -> Result { let client = self.identity(); client .change_password(self.write_request(proto::v1::ChangePasswordRequest { user_id, old_password_hash: old_password_hash.to_string(), new_password_hash: new_password_hash.to_string(), keep_session_id: keep_session_id.to_string(), client_ip: client_ip.to_string(), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn get_mfa_status( &self, user_id: i64, ) -> Result { let client = self.mfa(); client .get_mfa_status(self.read_request(proto::v1::GetMfaStatusRequest { user_id })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn configure_totp( &self, user_id: i64, name: &str, client_ip: &str, ) -> Result { let client = self.mfa(); client .configure_totp(self.write_request(proto::v1::ConfigureTotpRequest { user_id, name: name.to_string(), client_ip: client_ip.to_string(), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn enable_totp( &self, user_id: i64, code: &str, password_hash: &str, client_ip: &str, ) -> Result { let client = self.mfa(); client .enable_totp(self.write_request(proto::v1::EnableTotpRequest { user_id, code: code.to_string(), password_hash: password_hash.to_string(), client_ip: client_ip.to_string(), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn disable_totp( &self, user_id: i64, code: &str, client_ip: &str, ) -> Result { let client = self.mfa(); client .disable_totp(self.write_request(proto::v1::DisableTotpRequest { user_id, code: code.to_string(), client_ip: client_ip.to_string(), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn regenerate_recovery_codes( &self, user_id: i64, password_hash: &str, client_ip: &str, ) -> Result { let client = self.mfa(); client .regenerate_recovery_codes(self.write_request( proto::v1::RegenerateRecoveryCodesRequest { user_id, password_hash: password_hash.to_string(), client_ip: client_ip.to_string(), }, )) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } #[allow(clippy::too_many_arguments)] async fn list_user_ssh_keys( &self, user_id: i64, include_revoked: bool, page: proto::common::v1::PageRequest, ) -> Result { let client = self.ssh_key(); client .list_user_ssh_keys(self.read_request(proto::v1::ListUserSshKeysRequest { user_id, include_revoked, page: Some(page), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn add_ssh_key( &self, user_id: i64, name: &str, public_key: &str, description: &str, client_ip: &str, ) -> Result { let client = self.ssh_key(); client .add_ssh_key(self.write_request(proto::v1::AddSshKeyRequest { user_id, name: name.to_string(), public_key: public_key.to_string(), description: description.to_string(), client_ip: client_ip.to_string(), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn revoke_ssh_key( &self, user_id: i64, key_id: &str, reason: &str, bump_security_version: bool, ) -> Result { let client = self.ssh_key(); client .revoke_ssh_key(self.write_request(proto::v1::RevokeSshKeyRequest { user_id, key_id: key_id.to_string(), reason: reason.to_string(), bump_security_version, })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn list_user_sessions( &self, user_id: i64, include_revoked: bool, include_expired: bool, page: proto::common::v1::PageRequest, ) -> Result { let client = self.session(); client .list_user_sessions(self.read_request(proto::v1::ListUserSessionsRequest { user_id, include_revoked, include_expired, page: Some(page), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn revoke_user_sessions( &self, user_id: i64, except_session_id: &str, reason: &str, bump_security_version: bool, ) -> Result { let client = self.session(); client .revoke_user_sessions(self.write_request(proto::v1::RevokeUserSessionsRequest { user_id, except_session_id: except_session_id.to_string(), reason: reason.to_string(), bump_security_version, })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner()) } async fn get_session( &self, user_id: i64, version: i32, session_id: &str, ) -> Result { let client = self.authority(); client .get_session(self.read_request(proto::internal::v1::GetSessionRequest { user_id, version, session_id: session_id.to_string(), })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner().access.unwrap_or_default()) } async fn delete_account(&self, user_id: i64, reason: &str) -> Result<(), KenAuthError> { let client = self.authority(); client .delete_account( self.write_request(proto::internal::v1::DeleteAccountRequest { user_id, reason: reason.to_string(), }), ) .await .map_err(KenAuthError::from_status)?; Ok(()) } async fn cancel_account_deletion(&self, user_id: i64) -> Result<(), KenAuthError> { let client = self.authority(); client .cancel_account_deletion( self.write_request(proto::internal::v1::CancelAccountDeletionRequest { user_id }), ) .await .map_err(KenAuthError::from_status)?; Ok(()) } async fn purge_account(&self, user_id: i64) -> Result<(), KenAuthError> { let client = self.authority(); client .purge_account(self.write_request(proto::internal::v1::PurgeAccountRequest { user_id })) .await .map_err(KenAuthError::from_status)?; Ok(()) } async fn batch_get_authority_users( &self, user_ids: Vec, ) -> Result, KenAuthError> { let client = self.authority(); client .batch_get_user( self.read_request(proto::internal::v1::BatchGetUserRequest { user_ids }), ) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner().users) } async fn get_user_status( &self, user_id: i64, ) -> Result { let client = self.identity(); let user = Some(proto::common::v1::UserRef { key: Some(proto::common::v1::user_ref::Key::UserId(user_id)), }); client .get_user_status(self.read_request(proto::v1::GetUserStatusRequest { user })) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner().status.unwrap_or_default()) } async fn get_user_public_by_ref( &self, key: proto::common::v1::user_ref::Key, ) -> Result { let client = self.identity(); let user = Some(proto::common::v1::UserRef { key: Some(key) }); client .get_user_public(self.read_request(proto::v1::GetUserPublicRequest { user })) .await .map_err(KenAuthError::from_status)? .into_inner() .user .ok_or(KenAuthError::NotFound) } async fn get_authority_user( &self, user_id: i64, ) -> Result { let client = self.authority(); client .get_user(self.read_request(proto::internal::v1::GetUserRequest { user_id })) .await .map_err(KenAuthError::from_status)? .into_inner() .user .ok_or(KenAuthError::NotFound) } #[allow(clippy::too_many_arguments)] async fn update_authority_user( &self, user_id: i64, username: Option, displayname: Option, clear_displayname: bool, public_show_email: Option, version: i32, ) -> Result { let client = self.authority(); client .update_user(self.write_request(proto::internal::v1::UpdateUserRequest { user_id, patch: Some(proto::internal::v1::AuthorityUserPatch { version, username, displayname, clear_displayname, public_show_email, }), })) .await .map_err(KenAuthError::from_status)? .into_inner() .user .ok_or(KenAuthError::NotFound) } async fn batch_get_user_public( &self, user_ids: Vec, ) -> Result, KenAuthError> { let client = self.identity(); client .batch_get_user_public( self.read_request(proto::v1::BatchGetUserPublicRequest { user_ids }), ) .await .map_err(KenAuthError::from_status) .map(|response| response.into_inner().users) } async fn get_user_public( &self, user_id: i64, ) -> Result { let client = self.identity(); let user = Some(proto::common::v1::UserRef { key: Some(proto::common::v1::user_ref::Key::UserId(user_id)), }); client .get_user_public(self.read_request(proto::v1::GetUserPublicRequest { user })) .await .map_err(KenAuthError::from_status)? .into_inner() .user .ok_or(KenAuthError::NotFound) } async fn revoke_session( &self, user_id: i64, session_id: &str, reason: &str, ) -> Result<(), KenAuthError> { let client = self.session(); client .revoke_session(self.write_request(proto::v1::RevokeSessionRequest { user_id, session_id: session_id.to_string(), reason: reason.to_string(), })) .await .map_err(KenAuthError::from_status)?; Ok(()) } async fn lookup_ssh_key( &self, public_key: &str, ) -> Result, KenAuthError> { let client = self.ssh_key(); let response = client .lookup_ssh_key(self.read_request(proto::v1::LookupSshKeyRequest { fingerprint: String::new(), public_key: public_key.to_string(), require_active: true, })) .await .map_err(KenAuthError::from_status)? .into_inner(); if !response.found { return Ok(None); } Ok(response.principal) } async fn ensure_bot_user( &self, username: &str, displayname: &str, system: bool, ) -> Result { let client = self.identity(); client .ensure_bot_user(self.write_request(proto::v1::EnsureBotUserRequest { username: username.to_string(), displayname: displayname.to_string(), system, })) .await .map_err(KenAuthError::from_status)? .into_inner() .user .ok_or(KenAuthError::NotFound) } }