27caef0
ZhenYi
feat(reminders): add delivering status for message reminders and enhance scheduled message handling
27caef0
use std::str::FromStr;
use models::users::user;
use models::users::user_ssh_keys;
use russh::keys::PublicKey;
use russh::server::Auth;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, Set};
use time::OffsetDateTime;
use tokio::task;
use uuid::Uuid;
use crate::ssh::handler::GitSshHandler;
struct SshKeyUser {
user: user::Model,
key_id: Uuid,
}
impl GitSshHandler {
async fn find_user_by_public_key(
&self,
username: &str,
public_key: &PublicKey,
) -> Result<Option<SshKeyUser>, sea_orm::DbErr> {
let db = self.service.smart.git.db_read();
let Some(user) = user::Entity::find()
.filter(user::Column::LowerName.eq(username.to_lowercase()))
.one(db)
.await?
else {
return Ok(None);
};
if !user.is_active {
return Ok(None);
}
let keys = user_ssh_keys::Entity::find()
.filter(user_ssh_keys::Column::UserId.eq(user.uid))
.all(db)
.await?;
let presented = public_key.to_string();
for key in keys {
let matched = PublicKey::from_str(key.content.trim()).ok().map_or_else(
|| key.content.trim() == presented.trim(),
|p| p == *public_key,
);
if matched {
return Ok(Some(SshKeyUser {
user,
key_id: key.id,
}));
}
}
Ok(None)
}
fn update_key_last_used(&self, key_id: Uuid) {
let db = self.service.smart.git.state.database.clone();
let now = OffsetDateTime::now_utc();
task::spawn(async move {
let _ = user_ssh_keys::Entity::update_many()
.set(user_ssh_keys::ActiveModel {
id: Set(key_id),
last_used_at: Set(Some(now)),
..Default::default()
})
.filter(user_ssh_keys::Column::Id.eq(key_id))
.exec(db.write())
.await;
});
}
pub async fn auth_publickey_offered_impl(
&mut self,
user: &str,
public_key: &PublicKey,
) -> Result<Auth, russh::Error> {
if self.over_capacity {
return Ok(Auth::reject());
}
if user.is_empty() || public_key.to_string().len() < 32 {
return Ok(Auth::reject());
}
match self.find_user_by_public_key(user, public_key).await {
Ok(Some(_)) => Ok(Auth::Accept),
Ok(None) => Ok(Auth::reject()),
Err(e) => {
tracing::warn!("ssh auth lookup failed: {e}");
Err(russh::Error::NotAuthenticated)
}
}
}
pub async fn auth_publickey_impl(
&mut self,
user: &str,
public_key: &PublicKey,
) -> Result<Auth, russh::Error> {
if self.over_capacity {
return Ok(Auth::reject());
}
if user.is_empty() || public_key.to_string().len() < 32 {
return Ok(Auth::reject());
}
match self.find_user_by_public_key(user, public_key).await {
Ok(Some(found)) => {
if found.user.is_restricted || found.user.prohibit_login {
return Ok(Auth::reject());
}
let key_id = found.key_id;
self.operator = Some(found.user);
self.update_key_last_used(key_id);
Ok(Auth::Accept)
}
Ok(None) => Ok(Auth::reject()),
Err(e) => {
tracing::warn!("ssh auth lookup failed: {e}");
Err(russh::Error::NotAuthenticated)
}
}
}
}