Files
komodo/bin/core/src/auth/local.rs
Maxwell Becker e3d8e603ec 1.15.5 (#116)
* 1.15.5
- Update your user's username and password
- **Admin**: Delete Users

* update username / password / delete user backend

* bump version

* alerter default disabled

* delete users and update username / password

* set password "" after update
2024-10-11 19:42:43 -07:00

137 lines
3.5 KiB
Rust

use std::str::FromStr;
use anyhow::{anyhow, Context};
use async_timing_util::unix_timestamp_ms;
use axum::http::HeaderMap;
use komodo_client::{
api::auth::{
CreateLocalUser, CreateLocalUserResponse, LoginLocalUser,
LoginLocalUserResponse,
},
entities::user::{User, UserConfig},
};
use mongo_indexed::Document;
use mungos::mongodb::bson::{doc, oid::ObjectId};
use resolver_api::Resolve;
use crate::{
config::core_config,
helpers::hash_password,
state::{db_client, jwt_client, State},
};
impl Resolve<CreateLocalUser, HeaderMap> for State {
#[instrument(name = "CreateLocalUser", skip(self))]
async fn resolve(
&self,
CreateLocalUser { username, password }: CreateLocalUser,
_: HeaderMap,
) -> anyhow::Result<CreateLocalUserResponse> {
let core_config = core_config();
if !core_config.local_auth {
return Err(anyhow!("Local auth is not enabled"));
}
if username.is_empty() {
return Err(anyhow!("Username cannot be empty string"));
}
if ObjectId::from_str(&username).is_ok() {
return Err(anyhow!("Username cannot be valid ObjectId"));
}
if password.is_empty() {
return Err(anyhow!("Password cannot be empty string"));
}
let hashed_password = hash_password(password)?;
let no_users_exist =
db_client().users.find_one(Document::new()).await?.is_none();
if !no_users_exist && core_config.disable_user_registration {
return Err(anyhow!("User registration is disabled"));
}
let ts = unix_timestamp_ms() as i64;
let user = User {
id: Default::default(),
username,
enabled: no_users_exist || core_config.enable_new_users,
admin: no_users_exist,
super_admin: no_users_exist,
create_server_permissions: no_users_exist,
create_build_permissions: no_users_exist,
updated_at: ts,
last_update_view: 0,
recents: Default::default(),
all: Default::default(),
config: UserConfig::Local {
password: hashed_password,
},
};
let user_id = db_client()
.users
.insert_one(user)
.await
.context("failed to create user")?
.inserted_id
.as_object_id()
.context("inserted_id is not ObjectId")?
.to_string();
let jwt = jwt_client()
.generate(user_id)
.context("failed to generate jwt for user")?;
Ok(CreateLocalUserResponse { jwt })
}
}
impl Resolve<LoginLocalUser, HeaderMap> for State {
#[instrument(name = "LoginLocalUser", level = "debug", skip(self))]
async fn resolve(
&self,
LoginLocalUser { username, password }: LoginLocalUser,
_: HeaderMap,
) -> anyhow::Result<LoginLocalUserResponse> {
if !core_config().local_auth {
return Err(anyhow!("local auth is not enabled"));
}
let user = db_client()
.users
.find_one(doc! { "username": &username })
.await
.context("failed at db query for users")?
.with_context(|| {
format!("did not find user with username {username}")
})?;
let UserConfig::Local {
password: user_pw_hash,
} = user.config
else {
return Err(anyhow!(
"non-local auth users can not log in with a password"
));
};
let verified = bcrypt::verify(password, &user_pw_hash)
.context("failed at verify password")?;
if !verified {
return Err(anyhow!("invalid credentials"));
}
let jwt = jwt_client()
.generate(user.id)
.context("failed at generating jwt for user")?;
Ok(LoginLocalUserResponse { jwt })
}
}