feat: add host id allocation
Signed-off-by: sisungo <[email protected]>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user