feat: add host id allocation
Signed-off-by: sisungo <[email protected]>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
use crate::client;
|
||||
use clap::Parser;
|
||||
use semios_account::{
|
||||
protocol::{CreateGroupArgs, HostIdRule},
|
||||
record::HostGid,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
pub struct Cli {
|
||||
#[arg(short = 'D', long, default_value_t)]
|
||||
description: String,
|
||||
|
||||
#[arg(short = 'N', long)]
|
||||
fullname: Option<String>,
|
||||
|
||||
#[arg(short = 'G', long)]
|
||||
host_gid: Option<HostGid>,
|
||||
|
||||
groupname: String,
|
||||
}
|
||||
|
||||
pub fn main(cli: Cli) -> anyhow::Result<()> {
|
||||
let mut client = client()?;
|
||||
|
||||
let host_gid = match cli.host_gid {
|
||||
Some(x) => HostIdRule::Manual(x),
|
||||
None => HostIdRule::Auto(1000, HostGid::MAX),
|
||||
};
|
||||
|
||||
client.create_group(CreateGroupArgs {
|
||||
groupname: cli.groupname,
|
||||
fullname: cli.fullname,
|
||||
host_gid,
|
||||
description: cli.description,
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use crate::client;
|
||||
use anyhow::anyhow;
|
||||
use clap::Parser;
|
||||
use semios_account::{
|
||||
protocol::CreateUserArgs,
|
||||
protocol::{CreateUserArgs, HostIdRule},
|
||||
record::{HostUid, SecureTag},
|
||||
wellknown,
|
||||
};
|
||||
@@ -67,10 +67,15 @@ pub fn main(cli: Cli) -> anyhow::Result<()> {
|
||||
let mut defaults = HashMap::new();
|
||||
defaults.insert(wellknown::CLI_LOGIN_SHELL.into(), cli.cli_shell);
|
||||
|
||||
let host_uid = match cli.host_uid {
|
||||
Some(x) => HostIdRule::Manual(x),
|
||||
None => HostIdRule::Auto(1000, HostUid::MAX),
|
||||
};
|
||||
|
||||
client.create_user(CreateUserArgs {
|
||||
username: cli.username,
|
||||
fullname: cli.fullname,
|
||||
host_uid: cli.host_uid,
|
||||
host_uid,
|
||||
description: cli.description,
|
||||
secure_tags,
|
||||
extra_records: Default::default(),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
mod authenticate;
|
||||
mod create_group;
|
||||
mod create_user;
|
||||
mod info;
|
||||
mod update_auth;
|
||||
@@ -15,6 +16,9 @@ pub enum Cli {
|
||||
/// Create new user
|
||||
CreateUser(create_user::Cli),
|
||||
|
||||
/// Create new group
|
||||
CreateGroup(create_group::Cli),
|
||||
|
||||
/// Authenticate user
|
||||
Authenticate(authenticate::Cli),
|
||||
|
||||
@@ -28,6 +32,7 @@ fn main() {
|
||||
let result = match cli {
|
||||
Cli::Info(cli) => info::main(cli),
|
||||
Cli::CreateUser(cli) => create_user::main(cli),
|
||||
Cli::CreateGroup(cli) => create_group::main(cli),
|
||||
Cli::Authenticate(cli) => authenticate::main(cli),
|
||||
Cli::UpdateAuth(cli) => update_auth::main(cli),
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ use semios_account::{
|
||||
record::{GroupInfo, SecureTag, UserInfo},
|
||||
secret::Secret,
|
||||
};
|
||||
use std::{fmt::Debug, path::PathBuf, pin::Pin, sync::Arc};
|
||||
use std::{fmt::Debug, path::PathBuf, pin::Pin, range::RangeInclusive, sync::Arc};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn launch_server(app_state: Arc<AppState>) -> anyhow::Result<()> {
|
||||
@@ -215,11 +215,21 @@ async fn create_user(
|
||||
args: CreateUserArgs,
|
||||
) -> Result<(), Error> {
|
||||
require_secure_tags(&state, caller, &[SecureTag::CreateUser]).await?;
|
||||
let host_uid = match args.host_uid {
|
||||
HostIdRule::Unallocated => None,
|
||||
HostIdRule::Manual(x) => Some(x),
|
||||
HostIdRule::Auto(a, b) => Some(
|
||||
state
|
||||
.local_db
|
||||
.allocate_host_uid(RangeInclusive::from(a..=b))
|
||||
.ok_or(Error::AllocHostId)?,
|
||||
),
|
||||
};
|
||||
let user_info = UserInfo {
|
||||
uuid: Uuid::new_v7(uuid::Timestamp::now(uuid::ContextV7::new())),
|
||||
fullname: args.fullname.unwrap_or_else(|| args.username.clone()),
|
||||
username: args.username,
|
||||
host_uid: args.host_uid,
|
||||
host_uid,
|
||||
description: args.description,
|
||||
secure_tags: args.secure_tags,
|
||||
extra_records: args.extra_records,
|
||||
@@ -241,11 +251,21 @@ async fn create_group(
|
||||
args: CreateGroupArgs,
|
||||
) -> Result<(), Error> {
|
||||
require_secure_tags(&state, caller, &[SecureTag::CreateUser]).await?;
|
||||
let host_gid = match args.host_gid {
|
||||
HostIdRule::Unallocated => None,
|
||||
HostIdRule::Manual(x) => Some(x),
|
||||
HostIdRule::Auto(a, b) => Some(
|
||||
state
|
||||
.local_db
|
||||
.allocate_host_gid(RangeInclusive::from(a..=b))
|
||||
.ok_or(Error::AllocHostId)?,
|
||||
),
|
||||
};
|
||||
let group_info = GroupInfo {
|
||||
uuid: Uuid::new_v7(uuid::Timestamp::now(uuid::ContextV7::new())),
|
||||
fullname: args.fullname.unwrap_or_else(|| args.groupname.clone()),
|
||||
groupname: args.groupname,
|
||||
host_gid: args.host_gid,
|
||||
host_gid,
|
||||
description: args.description,
|
||||
creation_time: timestamp_s(),
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use rusqlite::params;
|
||||
use rusqlite::{OptionalExtension, ToSql, params, types::FromSql};
|
||||
use semios_account::{
|
||||
auth::{UserAuthMethodFlags, UserAuthMethodRecord},
|
||||
error::Error,
|
||||
@@ -6,7 +6,7 @@ use semios_account::{
|
||||
secret::{Secret, SecurityFlags},
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{path::Path, sync::Mutex};
|
||||
use std::{path::Path, range::RangeInclusive, sync::Mutex};
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Schema of the database.
|
||||
@@ -285,6 +285,18 @@ impl LocalDb {
|
||||
self._find_uuid_by("group", "group_host_gid", &host_gid.to_string())
|
||||
}
|
||||
|
||||
pub fn allocate_host_uid(&self, range: RangeInclusive<HostUid>) -> Option<HostUid> {
|
||||
self._allocate_host_id_max("user", "user_host_uid", range)
|
||||
.ok()
|
||||
.map(|x| x.map(|y| y + 1).unwrap_or(range.start))
|
||||
}
|
||||
|
||||
pub fn allocate_host_gid(&self, range: RangeInclusive<HostGid>) -> Option<HostGid> {
|
||||
self._allocate_host_id_max("group", "group_host_gid", range)
|
||||
.ok()
|
||||
.map(|x| x.map(|y| y + 1).unwrap_or(range.start))
|
||||
}
|
||||
|
||||
pub fn is_first_run(&self) -> bool {
|
||||
!self._kv_get("initialized").is_some()
|
||||
}
|
||||
@@ -349,6 +361,25 @@ impl LocalDb {
|
||||
.ok()
|
||||
}
|
||||
|
||||
fn _allocate_host_id_max<T: FromSql + ToSql + PartialEq>(
|
||||
&self,
|
||||
table: &'static str,
|
||||
column: &'static str,
|
||||
range: RangeInclusive<T>,
|
||||
) -> rusqlite::Result<Option<T>> {
|
||||
let lock = self.0.lock().unwrap();
|
||||
let mut stmt = lock.prepare_cached(&format!(
|
||||
"SELECT MAX(CAST(\"{column}\" AS INTEGER))
|
||||
FROM \"{table}\"
|
||||
WHERE CAST(\"{column}\" AS INTEGER) BETWEEN ?1 AND ?2"
|
||||
))?;
|
||||
let max: Option<T> = stmt.query_one(params![range.start, range.last], |row| row.get(0))?;
|
||||
if max == Some(range.last) {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(max)
|
||||
}
|
||||
|
||||
fn _kv_get(&self, key: &str) -> Option<String> {
|
||||
let lock = self.0.lock().unwrap();
|
||||
let mut stmt = lock
|
||||
|
||||
@@ -92,4 +92,8 @@ impl Client {
|
||||
pub fn create_user(&mut self, args: CreateUserArgs) -> Result<(), Error> {
|
||||
self.invoke(CREATE_USER.into(), args)
|
||||
}
|
||||
|
||||
pub fn create_group(&mut self, args: CreateGroupArgs) -> Result<(), Error> {
|
||||
self.invoke(CREATE_GROUP.into(), args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ pub enum Error {
|
||||
#[error("the specified auth method is not activated for the user")]
|
||||
NotActivatedAuthMethod,
|
||||
|
||||
/// Cannot allocate host ID
|
||||
#[error("cannot allocate host id")]
|
||||
AllocHostId,
|
||||
|
||||
/// TODO
|
||||
#[error("already exists")]
|
||||
AlreadyExists,
|
||||
|
||||
@@ -184,7 +184,7 @@ pub struct CreateUserArgs {
|
||||
pub fullname: Option<String>,
|
||||
|
||||
/// Specify a host UID.
|
||||
pub host_uid: Option<HostUid>,
|
||||
pub host_uid: HostIdRule<HostUid>,
|
||||
|
||||
/// User description.
|
||||
#[serde(default)]
|
||||
@@ -225,7 +225,7 @@ pub struct CreateGroupArgs {
|
||||
pub fullname: Option<String>,
|
||||
|
||||
/// Group ID on current host.
|
||||
pub host_gid: Option<HostGid>,
|
||||
pub host_gid: HostIdRule<HostGid>,
|
||||
|
||||
/// Group description.
|
||||
#[serde(default)]
|
||||
@@ -238,6 +238,14 @@ pub struct RemoveGroupArgs {
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HostIdRule<T> {
|
||||
Unallocated,
|
||||
Auto(T, T),
|
||||
Manual(T),
|
||||
}
|
||||
|
||||
// ==- Methods: Authentication -==
|
||||
|
||||
pub const GET_USER_AUTH_METHODS: &str = "GetUserAuthMethods";
|
||||
|
||||
Reference in New Issue
Block a user