forked from github-starred/komodo
* add "all permissions" feature on user and user group schema * prepare support for group all * implement user.all and user_group.all for broad base permissioning * clean up unused deps * sync support user group permissions regex * 1.11 * fix fe ? issue * this doesn't work * sync handle user group all set * retain above non earlier * remove permissions that already exist * update docs * add user group docs * minimize user group permissions for execute * sync toml * add sync name to slack alert title * add syncs to alerter white/blacklist * use \\ instead of $reg * share resource type base permissions api users and user groups * manage user / group base permissions ui * manage user / group base resource type permissions * update api permission handling * manage all resource permissions in table * user show group membership * update client to 1.11
133 lines
3.3 KiB
Rust
133 lines
3.3 KiB
Rust
use std::str::FromStr;
|
|
|
|
use anyhow::{anyhow, Context};
|
|
use async_timing_util::unix_timestamp_ms;
|
|
use axum::http::HeaderMap;
|
|
use mongo_indexed::Document;
|
|
use monitor_client::{
|
|
api::auth::{
|
|
CreateLocalUser, CreateLocalUserResponse, LoginLocalUser,
|
|
LoginLocalUserResponse,
|
|
},
|
|
entities::user::{User, UserConfig},
|
|
};
|
|
use mungos::mongodb::bson::{doc, oid::ObjectId};
|
|
use resolver_api::Resolve;
|
|
|
|
use crate::{
|
|
config::core_config,
|
|
state::State,
|
|
state::{db_client, jwt_client},
|
|
};
|
|
|
|
const BCRYPT_COST: u32 = 10;
|
|
|
|
impl Resolve<CreateLocalUser, HeaderMap> for State {
|
|
#[instrument(name = "CreateLocalUser", skip(self))]
|
|
async fn resolve(
|
|
&self,
|
|
CreateLocalUser { username, password }: CreateLocalUser,
|
|
_: HeaderMap,
|
|
) -> anyhow::Result<CreateLocalUserResponse> {
|
|
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"));
|
|
}
|
|
|
|
let password = bcrypt::hash(password, BCRYPT_COST)
|
|
.context("failed to hash password")?;
|
|
|
|
let no_users_exist = db_client()
|
|
.await
|
|
.users
|
|
.find_one(Document::new())
|
|
.await?
|
|
.is_none();
|
|
|
|
let ts = unix_timestamp_ms() as i64;
|
|
|
|
let user = User {
|
|
id: Default::default(),
|
|
username,
|
|
enabled: no_users_exist,
|
|
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 },
|
|
};
|
|
|
|
let user_id = db_client()
|
|
.await
|
|
.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()
|
|
.await
|
|
.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 })
|
|
}
|
|
}
|