use std::collections::HashMap; use kenauth::proto::common::v1 as auth_public; use models::users::user::{self, ProfileVisibility}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use super::AuthService; use crate::errors::AuthError; use crate::share::types::UserPublic; const MAX_BATCH_USERS: usize = 100; impl AuthService { fn filter_public_user( &self, caller: i64, local: Option<&user::Model>, public: &auth_public::UserPublic, allow_use: bool, ) -> Result { if !allow_use || public.system { return Err(AuthError::NotFound); } let visibility = local .map(|row| row.visibility) .unwrap_or(ProfileVisibility::Public); if visibility == ProfileVisibility::Private && caller != public.user_id { return Err(AuthError::NotFound); } let display_name = if public.displayname.trim().is_empty() { public.username.clone() } else { public.displayname.clone() }; let avatar_raw = local .map(|row| row.local_avatar_url.as_str()) .filter(|value| !value.is_empty()) .unwrap_or(public.avatar_url.as_str()); Ok(UserPublic { user_id: public.user_id, username: public.username.clone(), display_name, avatar_url: space::services::spaces::resolve_user_avatar_url( public.user_id, avatar_raw, ), }) } pub async fn public_user(&self, caller: i64, uid: i64) -> Result { let client = self .state .kenauth .as_ref() .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?; let public = client.get_user_public(uid).await.map_err(map_kenauth)?; let authority = client.get_authority_user(uid).await.map_err(map_kenauth)?; let local = user::Entity::find_by_id(uid).one(self.db().read()).await?; self.filter_public_user(caller, local.as_ref(), &public, authority.allow_use) } pub async fn public_users( &self, caller: i64, ids: Vec, ) -> Result, AuthError> { if ids.len() > MAX_BATCH_USERS { return Err(AuthError::BadRequest(format!( "ids must contain at most {MAX_BATCH_USERS} entries" ))); } if ids.is_empty() { return Ok(Vec::new()); } let mut unique: Vec = ids; unique.sort_unstable(); unique.dedup(); let client = self .state .kenauth .as_ref() .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?; let publics = client .batch_get_user_public(unique.clone()) .await .map_err(map_kenauth)?; let authorities = client .batch_get_authority_users(unique.clone()) .await .map_err(map_kenauth)?; let locals: HashMap = user::Entity::find() .filter(user::Column::Uid.is_in(unique)) .all(self.db().read()) .await? .into_iter() .map(|row| (row.uid, row)) .collect(); let allow: HashMap = authorities .into_iter() .map(|row| (row.id, row.allow_use)) .collect(); let mut out = Vec::with_capacity(publics.len()); for public in publics { let local = locals.get(&public.user_id); let allow_use = allow.get(&public.user_id).copied().unwrap_or(false); if let Ok(user) = self.filter_public_user(caller, local, &public, allow_use) { out.push(user); } } Ok(out) } pub async fn public_user_by_name( &self, caller: i64, username: &str, ) -> Result { let name = username.trim(); if name.is_empty() { return Err(AuthError::NotFound); } let client = self .state .kenauth .as_ref() .ok_or_else(|| AuthError::Internal("kenoauth is not configured".to_string()))?; let public = client .get_user_public_by_ref(auth_public::user_ref::Key::Username(name.to_string())) .await .map_err(map_kenauth)?; self.public_user(caller, public.user_id).await } } fn map_kenauth(error: kenauth::KenAuthError) -> AuthError { use kenauth::KenAuthError; match error { KenAuthError::NotFound => AuthError::NotFound, KenAuthError::Unauthenticated => AuthError::Unauthorized, KenAuthError::Rejected(message) => AuthError::BadRequest(message), KenAuthError::Unavailable(message) | KenAuthError::Config(message) | KenAuthError::Internal(message) => AuthError::Internal(message), } }