mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
20230727 unix int modularity (#1907)
This commit is contained in:
parent
0293234d3c
commit
99b761c966
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2674,7 +2674,6 @@ dependencies = [
|
||||||
"lru 0.8.1",
|
"lru 0.8.1",
|
||||||
"notify-debouncer-full",
|
"notify-debouncer-full",
|
||||||
"profiles",
|
"profiles",
|
||||||
"reqwest",
|
|
||||||
"rpassword 7.2.0",
|
"rpassword 7.2.0",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
"selinux",
|
"selinux",
|
||||||
|
@ -2687,6 +2686,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tss-esapi",
|
"tss-esapi",
|
||||||
"users",
|
"users",
|
||||||
|
"uuid",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -556,7 +556,7 @@ impl fmt::Display for RadiusAuthToken {
|
||||||
pub struct UnixGroupToken {
|
pub struct UnixGroupToken {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub spn: String,
|
pub spn: String,
|
||||||
pub uuid: String,
|
pub uuid: Uuid,
|
||||||
pub gidnumber: u32,
|
pub gidnumber: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,7 +580,7 @@ pub struct UnixUserToken {
|
||||||
pub spn: String,
|
pub spn: String,
|
||||||
pub displayname: String,
|
pub displayname: String,
|
||||||
pub gidnumber: u32,
|
pub gidnumber: u32,
|
||||||
pub uuid: String,
|
pub uuid: Uuid,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub shell: Option<String>,
|
pub shell: Option<String>,
|
||||||
pub groups: Vec<UnixGroupToken>,
|
pub groups: Vec<UnixGroupToken>,
|
||||||
|
|
|
@ -150,7 +150,7 @@ impl UnixUserAccount {
|
||||||
spn: self.spn.clone(),
|
spn: self.spn.clone(),
|
||||||
displayname: self.displayname.clone(),
|
displayname: self.displayname.clone(),
|
||||||
gidnumber: self.gidnumber,
|
gidnumber: self.gidnumber,
|
||||||
uuid: self.uuid.as_hyphenated().to_string(),
|
uuid: self.uuid,
|
||||||
shell: self.shell.clone(),
|
shell: self.shell.clone(),
|
||||||
groups,
|
groups,
|
||||||
sshkeys: self.sshkeys.clone(),
|
sshkeys: self.sshkeys.clone(),
|
||||||
|
@ -449,7 +449,7 @@ impl UnixGroup {
|
||||||
Ok(UnixGroupToken {
|
Ok(UnixGroupToken {
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
spn: self.spn.clone(),
|
spn: self.spn.clone(),
|
||||||
uuid: self.uuid.as_hyphenated().to_string(),
|
uuid: self.uuid,
|
||||||
gidnumber: self.gidnumber,
|
gidnumber: self.gidnumber,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -563,7 +563,7 @@ async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// get the account by uuid
|
// get the account by uuid
|
||||||
let r3 = rsclient
|
let r3 = rsclient
|
||||||
.idm_account_unix_token_get(r.uuid.as_str())
|
.idm_account_unix_token_get(&r.uuid.hyphenated().to_string())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -590,7 +590,7 @@ async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// get the group by uuid
|
// get the group by uuid
|
||||||
let r3 = rsclient
|
let r3 = rsclient
|
||||||
.idm_group_unix_token_get(r.uuid.as_str())
|
.idm_group_unix_token_get(&r.uuid.hyphenated().to_string())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ tokio = { workspace = true, features = ["rt", "fs", "macros", "sync", "time", "n
|
||||||
tokio-util = { workspace = true, features = ["codec"] }
|
tokio-util = { workspace = true, features = ["codec"] }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tss-esapi = { workspace = true, optional = true }
|
tss-esapi = { workspace = true, optional = true }
|
||||||
reqwest = { workspace = true, default-features = false }
|
uuid = { workspace = true }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_family = "windows"))'.dependencies]
|
[target.'cfg(not(target_family = "windows"))'.dependencies]
|
||||||
|
|
|
@ -5,6 +5,7 @@ use bytes::{BufMut, BytesMut};
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
// use tokio::runtime::Builder;
|
// use tokio::runtime::Builder;
|
||||||
|
use tokio::time::{self, Duration};
|
||||||
use tokio_util::codec::Framed;
|
use tokio_util::codec::Framed;
|
||||||
use tokio_util::codec::{Decoder, Encoder};
|
use tokio_util::codec::{Decoder, Encoder};
|
||||||
|
|
||||||
|
@ -48,8 +49,10 @@ impl ClientCodec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a call to kanidm_unixd via a unix socket at `path`
|
async fn call_daemon_inner(
|
||||||
pub async fn call_daemon(path: &str, req: ClientRequest) -> Result<ClientResponse, Box<dyn Error>> {
|
path: &str,
|
||||||
|
req: ClientRequest,
|
||||||
|
) -> Result<ClientResponse, Box<dyn Error>> {
|
||||||
trace!(?path, ?req);
|
trace!(?path, ?req);
|
||||||
let stream = UnixStream::connect(path).await?;
|
let stream = UnixStream::connect(path).await?;
|
||||||
trace!("connected");
|
trace!("connected");
|
||||||
|
@ -71,3 +74,23 @@ pub async fn call_daemon(path: &str, req: ClientRequest) -> Result<ClientRespons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes a call to kanidm_unixd via a unix socket at `path`
|
||||||
|
pub async fn call_daemon(
|
||||||
|
path: &str,
|
||||||
|
req: ClientRequest,
|
||||||
|
timeout: u64,
|
||||||
|
) -> Result<ClientResponse, Box<dyn Error>> {
|
||||||
|
let sleep = time::sleep(Duration::from_millis(timeout));
|
||||||
|
tokio::pin!(sleep);
|
||||||
|
|
||||||
|
tokio::select! {
|
||||||
|
_ = &mut sleep => {
|
||||||
|
error!("Timed out making request to kanidm_unixd");
|
||||||
|
Err(Box::new(IoError::new(ErrorKind::Other, "timeout")))
|
||||||
|
}
|
||||||
|
res = call_daemon_inner(path, req) => {
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ use kanidm_client::KanidmClientBuilder;
|
||||||
use kanidm_proto::constants::DEFAULT_CLIENT_CONFIG_PATH;
|
use kanidm_proto::constants::DEFAULT_CLIENT_CONFIG_PATH;
|
||||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||||
use kanidm_unix_common::db::Db;
|
use kanidm_unix_common::db::Db;
|
||||||
|
use kanidm_unix_common::idprovider::kanidm::KanidmProvider;
|
||||||
use kanidm_unix_common::resolver::Resolver;
|
use kanidm_unix_common::resolver::Resolver;
|
||||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||||
use kanidm_unix_common::unix_passwd::{parse_etc_group, parse_etc_passwd};
|
use kanidm_unix_common::unix_passwd::{parse_etc_group, parse_etc_passwd};
|
||||||
|
@ -182,7 +183,7 @@ async fn handle_task_client(
|
||||||
|
|
||||||
async fn handle_client(
|
async fn handle_client(
|
||||||
sock: UnixStream,
|
sock: UnixStream,
|
||||||
cachelayer: Arc<Resolver>,
|
cachelayer: Arc<Resolver<KanidmProvider>>,
|
||||||
task_channel_tx: &Sender<AsyncTaskRequest>,
|
task_channel_tx: &Sender<AsyncTaskRequest>,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
debug!("Accepted connection");
|
debug!("Accepted connection");
|
||||||
|
@ -372,7 +373,9 @@ async fn handle_client(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_etc_passwd_group(cachelayer: &Resolver) -> Result<(), Box<dyn Error>> {
|
async fn process_etc_passwd_group(
|
||||||
|
cachelayer: &Resolver<KanidmProvider>,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let mut file = File::open("/etc/passwd").await?;
|
let mut file = File::open("/etc/passwd").await?;
|
||||||
let mut contents = vec![];
|
let mut contents = vec![];
|
||||||
file.read_to_end(&mut contents).await?;
|
file.read_to_end(&mut contents).await?;
|
||||||
|
@ -661,6 +664,8 @@ async fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let idprovider = KanidmProvider::new(rsclient);
|
||||||
|
|
||||||
let db = match Db::new(cfg.db_path.as_str(), &cfg.tpm_policy) {
|
let db = match Db::new(cfg.db_path.as_str(), &cfg.tpm_policy) {
|
||||||
Ok(db) => db,
|
Ok(db) => db,
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
|
@ -671,8 +676,8 @@ async fn main() -> ExitCode {
|
||||||
|
|
||||||
let cl_inner = match Resolver::new(
|
let cl_inner = match Resolver::new(
|
||||||
db,
|
db,
|
||||||
|
idprovider,
|
||||||
cfg.cache_timeout,
|
cfg.cache_timeout,
|
||||||
rsclient,
|
|
||||||
cfg.pam_allowed_login_groups.clone(),
|
cfg.pam_allowed_login_groups.clone(),
|
||||||
cfg.default_shell.clone(),
|
cfg.default_shell.clone(),
|
||||||
cfg.home_prefix.clone(),
|
cfg.home_prefix.clone(),
|
||||||
|
|
|
@ -2,17 +2,16 @@ use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::idprovider::interface::{GroupToken, Id, UserToken};
|
||||||
use crate::unix_config::TpmPolicy;
|
use crate::unix_config::TpmPolicy;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use kanidm_lib_crypto::CryptoPolicy;
|
use kanidm_lib_crypto::CryptoPolicy;
|
||||||
use kanidm_lib_crypto::DbPasswordV1;
|
use kanidm_lib_crypto::DbPasswordV1;
|
||||||
use kanidm_lib_crypto::Password;
|
use kanidm_lib_crypto::Password;
|
||||||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
|
||||||
use libc::umask;
|
use libc::umask;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use tokio::sync::{Mutex, MutexGuard};
|
use tokio::sync::{Mutex, MutexGuard};
|
||||||
|
use uuid::Uuid;
|
||||||
use crate::resolver::Id;
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Cache {
|
pub trait Cache {
|
||||||
|
@ -43,27 +42,27 @@ pub trait CacheTxn {
|
||||||
|
|
||||||
fn clear(&self) -> Result<(), CacheError>;
|
fn clear(&self) -> Result<(), CacheError>;
|
||||||
|
|
||||||
fn get_account(&self, account_id: &Id) -> Result<Option<(UnixUserToken, u64)>, CacheError>;
|
fn get_account(&self, account_id: &Id) -> Result<Option<(UserToken, u64)>, CacheError>;
|
||||||
|
|
||||||
fn get_accounts(&self) -> Result<Vec<UnixUserToken>, CacheError>;
|
fn get_accounts(&self) -> Result<Vec<UserToken>, CacheError>;
|
||||||
|
|
||||||
fn update_account(&self, account: &UnixUserToken, expire: u64) -> Result<(), CacheError>;
|
fn update_account(&self, account: &UserToken, expire: u64) -> Result<(), CacheError>;
|
||||||
|
|
||||||
fn delete_account(&self, a_uuid: &str) -> Result<(), CacheError>;
|
fn delete_account(&self, a_uuid: Uuid) -> Result<(), CacheError>;
|
||||||
|
|
||||||
fn update_account_password(&self, a_uuid: &str, cred: &str) -> Result<(), CacheError>;
|
fn update_account_password(&self, a_uuid: Uuid, cred: &str) -> Result<(), CacheError>;
|
||||||
|
|
||||||
fn check_account_password(&self, a_uuid: &str, cred: &str) -> Result<bool, CacheError>;
|
fn check_account_password(&self, a_uuid: Uuid, cred: &str) -> Result<bool, CacheError>;
|
||||||
|
|
||||||
fn get_group(&self, grp_id: &Id) -> Result<Option<(UnixGroupToken, u64)>, CacheError>;
|
fn get_group(&self, grp_id: &Id) -> Result<Option<(GroupToken, u64)>, CacheError>;
|
||||||
|
|
||||||
fn get_group_members(&self, g_uuid: &str) -> Result<Vec<UnixUserToken>, CacheError>;
|
fn get_group_members(&self, g_uuid: Uuid) -> Result<Vec<UserToken>, CacheError>;
|
||||||
|
|
||||||
fn get_groups(&self) -> Result<Vec<UnixGroupToken>, CacheError>;
|
fn get_groups(&self) -> Result<Vec<GroupToken>, CacheError>;
|
||||||
|
|
||||||
fn update_group(&self, grp: &UnixGroupToken, expire: u64) -> Result<(), CacheError>;
|
fn update_group(&self, grp: &GroupToken, expire: u64) -> Result<(), CacheError>;
|
||||||
|
|
||||||
fn delete_group(&self, g_uuid: &str) -> Result<(), CacheError>;
|
fn delete_group(&self, g_uuid: Uuid) -> Result<(), CacheError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Db {
|
pub struct Db {
|
||||||
|
@ -347,7 +346,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_account(&self, account_id: &Id) -> Result<Option<(UnixUserToken, u64)>, CacheError> {
|
fn get_account(&self, account_id: &Id) -> Result<Option<(UserToken, u64)>, CacheError> {
|
||||||
let data = match account_id {
|
let data = match account_id {
|
||||||
Id::Name(n) => self.get_account_data_name(n.as_str()),
|
Id::Name(n) => self.get_account_data_name(n.as_str()),
|
||||||
Id::Gid(g) => self.get_account_data_gid(*g),
|
Id::Gid(g) => self.get_account_data_gid(*g),
|
||||||
|
@ -381,7 +380,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_accounts(&self) -> Result<Vec<UnixUserToken>, CacheError> {
|
fn get_accounts(&self) -> Result<Vec<UserToken>, CacheError> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare("SELECT token FROM account_t")
|
.prepare("SELECT token FROM account_t")
|
||||||
|
@ -410,7 +409,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_account(&self, account: &UnixUserToken, expire: u64) -> Result<(), CacheError> {
|
fn update_account(&self, account: &UserToken, expire: u64) -> Result<(), CacheError> {
|
||||||
let data = serde_json::to_vec(account).map_err(|e| {
|
let data = serde_json::to_vec(account).map_err(|e| {
|
||||||
error!("update_account json error -> {:?}", e);
|
error!("update_account json error -> {:?}", e);
|
||||||
CacheError::SerdeJson
|
CacheError::SerdeJson
|
||||||
|
@ -423,11 +422,12 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
// This is needed because sqlites 'insert or replace into', will null the password field
|
// This is needed because sqlites 'insert or replace into', will null the password field
|
||||||
// if present, and upsert MUST match the exact conflicting column, so that means we have
|
// if present, and upsert MUST match the exact conflicting column, so that means we have
|
||||||
// to manually manage the update or insert :( :(
|
// to manually manage the update or insert :( :(
|
||||||
|
let account_uuid = account.uuid.as_hyphenated().to_string();
|
||||||
|
|
||||||
// Find anything conflicting and purge it.
|
// Find anything conflicting and purge it.
|
||||||
self.conn.execute("DELETE FROM account_t WHERE NOT uuid = :uuid AND (name = :name OR spn = :spn OR gidnumber = :gidnumber)",
|
self.conn.execute("DELETE FROM account_t WHERE NOT uuid = :uuid AND (name = :name OR spn = :spn OR gidnumber = :gidnumber)",
|
||||||
named_params!{
|
named_params!{
|
||||||
":uuid": &account.uuid,
|
":uuid": &account_uuid,
|
||||||
":name": &account.name,
|
":name": &account.name,
|
||||||
":spn": &account.spn,
|
":spn": &account.spn,
|
||||||
":gidnumber": &account.gidnumber,
|
":gidnumber": &account.gidnumber,
|
||||||
|
@ -441,7 +441,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
let updated = self.conn.execute(
|
let updated = self.conn.execute(
|
||||||
"UPDATE account_t SET name=:name, spn=:spn, gidnumber=:gidnumber, token=:token, expiry=:expiry WHERE uuid = :uuid",
|
"UPDATE account_t SET name=:name, spn=:spn, gidnumber=:gidnumber, token=:token, expiry=:expiry WHERE uuid = :uuid",
|
||||||
named_params!{
|
named_params!{
|
||||||
":uuid": &account.uuid,
|
":uuid": &account_uuid,
|
||||||
":name": &account.name,
|
":name": &account.name,
|
||||||
":spn": &account.spn,
|
":spn": &account.spn,
|
||||||
":gidnumber": &account.gidnumber,
|
":gidnumber": &account.gidnumber,
|
||||||
|
@ -461,7 +461,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
stmt.execute(named_params! {
|
stmt.execute(named_params! {
|
||||||
":uuid": &account.uuid,
|
":uuid": &account_uuid,
|
||||||
":name": &account.name,
|
":name": &account.name,
|
||||||
":spn": &account.spn,
|
":spn": &account.spn,
|
||||||
":gidnumber": &account.gidnumber,
|
":gidnumber": &account.gidnumber,
|
||||||
|
@ -482,7 +482,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.prepare("DELETE FROM memberof_t WHERE a_uuid = :a_uuid")
|
.prepare("DELETE FROM memberof_t WHERE a_uuid = :a_uuid")
|
||||||
.map_err(|e| self.sqlite_error("prepare", &e))?;
|
.map_err(|e| self.sqlite_error("prepare", &e))?;
|
||||||
|
|
||||||
stmt.execute([&account.uuid])
|
stmt.execute([&account_uuid])
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
debug!("delete memberships -> {:?}", r);
|
debug!("delete memberships -> {:?}", r);
|
||||||
})
|
})
|
||||||
|
@ -495,8 +495,8 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
// Now for each group, add the relation.
|
// Now for each group, add the relation.
|
||||||
account.groups.iter().try_for_each(|g| {
|
account.groups.iter().try_for_each(|g| {
|
||||||
stmt.execute(named_params! {
|
stmt.execute(named_params! {
|
||||||
":a_uuid": &account.uuid,
|
":a_uuid": &account_uuid,
|
||||||
":g_uuid": &g.uuid,
|
":g_uuid": &g.uuid.as_hyphenated().to_string(),
|
||||||
})
|
})
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
debug!("insert membership -> {:?}", r);
|
debug!("insert membership -> {:?}", r);
|
||||||
|
@ -505,11 +505,13 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_account(&self, a_uuid: &str) -> Result<(), CacheError> {
|
fn delete_account(&self, a_uuid: Uuid) -> Result<(), CacheError> {
|
||||||
|
let account_uuid = a_uuid.as_hyphenated().to_string();
|
||||||
|
|
||||||
self.conn
|
self.conn
|
||||||
.execute(
|
.execute(
|
||||||
"DELETE FROM memberof_t WHERE a_uuid = :a_uuid",
|
"DELETE FROM memberof_t WHERE a_uuid = :a_uuid",
|
||||||
params![a_uuid],
|
params![&account_uuid],
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| self.sqlite_error("account_t memberof_t cascade delete", &e))?;
|
.map_err(|e| self.sqlite_error("account_t memberof_t cascade delete", &e))?;
|
||||||
|
@ -517,13 +519,13 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
self.conn
|
self.conn
|
||||||
.execute(
|
.execute(
|
||||||
"DELETE FROM account_t WHERE uuid = :a_uuid",
|
"DELETE FROM account_t WHERE uuid = :a_uuid",
|
||||||
params![a_uuid],
|
params![&account_uuid],
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| self.sqlite_error("account_t delete", &e))
|
.map_err(|e| self.sqlite_error("account_t delete", &e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_account_password(&self, a_uuid: &str, cred: &str) -> Result<(), CacheError> {
|
fn update_account_password(&self, a_uuid: Uuid, cred: &str) -> Result<(), CacheError> {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
let pw = if let Some(tcti_str) = self.require_tpm {
|
let pw = if let Some(tcti_str) = self.require_tpm {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
@ -552,7 +554,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.execute(
|
.execute(
|
||||||
"UPDATE account_t SET password = :data WHERE uuid = :a_uuid",
|
"UPDATE account_t SET password = :data WHERE uuid = :a_uuid",
|
||||||
named_params! {
|
named_params! {
|
||||||
":a_uuid": &a_uuid,
|
":a_uuid": &a_uuid.as_hyphenated().to_string(),
|
||||||
":data": &data,
|
":data": &data,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -560,7 +562,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_account_password(&self, a_uuid: &str, cred: &str) -> Result<bool, CacheError> {
|
fn check_account_password(&self, a_uuid: Uuid, cred: &str) -> Result<bool, CacheError> {
|
||||||
#[cfg(not(feature = "tpm"))]
|
#[cfg(not(feature = "tpm"))]
|
||||||
if self.require_tpm.is_some() {
|
if self.require_tpm.is_some() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
@ -573,7 +575,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
|
|
||||||
// Makes tuple (token, expiry)
|
// Makes tuple (token, expiry)
|
||||||
let data_iter = stmt
|
let data_iter = stmt
|
||||||
.query_map([a_uuid], |row| row.get(0))
|
.query_map([a_uuid.as_hyphenated().to_string()], |row| row.get(0))
|
||||||
.map_err(|e| self.sqlite_error("query_map", &e))?;
|
.map_err(|e| self.sqlite_error("query_map", &e))?;
|
||||||
let data: Result<Vec<Vec<u8>>, _> = data_iter
|
let data: Result<Vec<Vec<u8>>, _> = data_iter
|
||||||
.map(|v| v.map_err(|e| self.sqlite_error("map", &e)))
|
.map(|v| v.map_err(|e| self.sqlite_error("map", &e)))
|
||||||
|
@ -622,7 +624,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_group(&self, grp_id: &Id) -> Result<Option<(UnixGroupToken, u64)>, CacheError> {
|
fn get_group(&self, grp_id: &Id) -> Result<Option<(GroupToken, u64)>, CacheError> {
|
||||||
let data = match grp_id {
|
let data = match grp_id {
|
||||||
Id::Name(n) => self.get_group_data_name(n.as_str()),
|
Id::Name(n) => self.get_group_data_name(n.as_str()),
|
||||||
Id::Gid(g) => self.get_group_data_gid(*g),
|
Id::Gid(g) => self.get_group_data_gid(*g),
|
||||||
|
@ -656,7 +658,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_group_members(&self, g_uuid: &str) -> Result<Vec<UnixUserToken>, CacheError> {
|
fn get_group_members(&self, g_uuid: Uuid) -> Result<Vec<UserToken>, CacheError> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare("SELECT account_t.token FROM (account_t, memberof_t) WHERE account_t.uuid = memberof_t.a_uuid AND memberof_t.g_uuid = :g_uuid")
|
.prepare("SELECT account_t.token FROM (account_t, memberof_t) WHERE account_t.uuid = memberof_t.a_uuid AND memberof_t.g_uuid = :g_uuid")
|
||||||
|
@ -665,7 +667,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let data_iter = stmt
|
let data_iter = stmt
|
||||||
.query_map([g_uuid], |row| row.get(0))
|
.query_map([g_uuid.as_hyphenated().to_string()], |row| row.get(0))
|
||||||
.map_err(|e| self.sqlite_error("query_map", &e))?;
|
.map_err(|e| self.sqlite_error("query_map", &e))?;
|
||||||
let data: Result<Vec<Vec<u8>>, _> = data_iter
|
let data: Result<Vec<Vec<u8>>, _> = data_iter
|
||||||
.map(|v| v.map_err(|e| self.sqlite_error("map", &e)))
|
.map(|v| v.map_err(|e| self.sqlite_error("map", &e)))
|
||||||
|
@ -685,7 +687,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_groups(&self) -> Result<Vec<UnixGroupToken>, CacheError> {
|
fn get_groups(&self) -> Result<Vec<GroupToken>, CacheError> {
|
||||||
let mut stmt = self
|
let mut stmt = self
|
||||||
.conn
|
.conn
|
||||||
.prepare("SELECT token FROM group_t")
|
.prepare("SELECT token FROM group_t")
|
||||||
|
@ -714,7 +716,7 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_group(&self, grp: &UnixGroupToken, expire: u64) -> Result<(), CacheError> {
|
fn update_group(&self, grp: &GroupToken, expire: u64) -> Result<(), CacheError> {
|
||||||
let data = serde_json::to_vec(grp).map_err(|e| {
|
let data = serde_json::to_vec(grp).map_err(|e| {
|
||||||
error!("json error -> {:?}", e);
|
error!("json error -> {:?}", e);
|
||||||
CacheError::SerdeJson
|
CacheError::SerdeJson
|
||||||
|
@ -730,8 +732,9 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
self.sqlite_error("prepare", &e)
|
self.sqlite_error("prepare", &e)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// We have to to-str uuid as the sqlite impl makes it a blob which breaks our selects in get.
|
||||||
stmt.execute(named_params! {
|
stmt.execute(named_params! {
|
||||||
":uuid": &grp.uuid,
|
":uuid": &grp.uuid.as_hyphenated().to_string(),
|
||||||
":name": &grp.name,
|
":name": &grp.name,
|
||||||
":spn": &grp.spn,
|
":spn": &grp.spn,
|
||||||
":gidnumber": &grp.gidnumber,
|
":gidnumber": &grp.gidnumber,
|
||||||
|
@ -744,13 +747,17 @@ impl<'a> CacheTxn for DbTxn<'a> {
|
||||||
.map_err(|e| self.sqlite_error("execute", &e))
|
.map_err(|e| self.sqlite_error("execute", &e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_group(&self, g_uuid: &str) -> Result<(), CacheError> {
|
fn delete_group(&self, g_uuid: Uuid) -> Result<(), CacheError> {
|
||||||
|
let group_uuid = g_uuid.as_hyphenated().to_string();
|
||||||
self.conn
|
self.conn
|
||||||
.execute("DELETE FROM memberof_t WHERE g_uuid = :g_uuid", [g_uuid])
|
.execute(
|
||||||
|
"DELETE FROM memberof_t WHERE g_uuid = :g_uuid",
|
||||||
|
[&group_uuid],
|
||||||
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| self.sqlite_error("group_t memberof_t cascade delete", &e))?;
|
.map_err(|e| self.sqlite_error("group_t memberof_t cascade delete", &e))?;
|
||||||
self.conn
|
self.conn
|
||||||
.execute("DELETE FROM group_t WHERE uuid = :g_uuid", [g_uuid])
|
.execute("DELETE FROM group_t WHERE uuid = :g_uuid", [&group_uuid])
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| self.sqlite_error("group_t delete", &e))
|
.map_err(|e| self.sqlite_error("group_t delete", &e))
|
||||||
}
|
}
|
||||||
|
@ -1129,11 +1136,9 @@ pub(crate) mod tpm {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use kanidm_proto::v1::{UnixGroupToken, UnixUserToken};
|
|
||||||
// use std::assert_matches::assert_matches;
|
// use std::assert_matches::assert_matches;
|
||||||
|
|
||||||
use super::{Cache, CacheTxn, Db};
|
use super::{Cache, CacheTxn, Db};
|
||||||
use crate::resolver::Id;
|
use crate::idprovider::interface::{GroupToken, Id, UserToken};
|
||||||
use crate::unix_config::TpmPolicy;
|
use crate::unix_config::TpmPolicy;
|
||||||
|
|
||||||
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
|
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
|
||||||
|
@ -1146,12 +1151,12 @@ mod tests {
|
||||||
let dbtxn = db.write().await;
|
let dbtxn = db.write().await;
|
||||||
assert!(dbtxn.migrate().is_ok());
|
assert!(dbtxn.migrate().is_ok());
|
||||||
|
|
||||||
let mut ut1 = UnixUserToken {
|
let mut ut1 = UserToken {
|
||||||
name: "testuser".to_string(),
|
name: "testuser".to_string(),
|
||||||
spn: "testuser@example.com".to_string(),
|
spn: "testuser@example.com".to_string(),
|
||||||
displayname: "Test User".to_string(),
|
displayname: "Test User".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"),
|
||||||
shell: None,
|
shell: None,
|
||||||
groups: Vec::new(),
|
groups: Vec::new(),
|
||||||
sshkeys: vec!["key-a".to_string()],
|
sshkeys: vec!["key-a".to_string()],
|
||||||
|
@ -1230,11 +1235,11 @@ mod tests {
|
||||||
let dbtxn = db.write().await;
|
let dbtxn = db.write().await;
|
||||||
assert!(dbtxn.migrate().is_ok());
|
assert!(dbtxn.migrate().is_ok());
|
||||||
|
|
||||||
let mut gt1 = UnixGroupToken {
|
let mut gt1 = GroupToken {
|
||||||
name: "testgroup".to_string(),
|
name: "testgroup".to_string(),
|
||||||
spn: "testgroup@example.com".to_string(),
|
spn: "testgroup@example.com".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let id_name = Id::Name("testgroup".to_string());
|
let id_name = Id::Name("testgroup".to_string());
|
||||||
|
@ -1305,26 +1310,26 @@ mod tests {
|
||||||
let dbtxn = db.write().await;
|
let dbtxn = db.write().await;
|
||||||
assert!(dbtxn.migrate().is_ok());
|
assert!(dbtxn.migrate().is_ok());
|
||||||
|
|
||||||
let gt1 = UnixGroupToken {
|
let gt1 = GroupToken {
|
||||||
name: "testuser".to_string(),
|
name: "testuser".to_string(),
|
||||||
spn: "testuser@example.com".to_string(),
|
spn: "testuser@example.com".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let gt2 = UnixGroupToken {
|
let gt2 = GroupToken {
|
||||||
name: "testgroup".to_string(),
|
name: "testgroup".to_string(),
|
||||||
spn: "testgroup@example.com".to_string(),
|
spn: "testgroup@example.com".to_string(),
|
||||||
gidnumber: 2001,
|
gidnumber: 2001,
|
||||||
uuid: "b500be97-8552-42a5-aca0-668bc5625705".to_string(),
|
uuid: uuid::uuid!("b500be97-8552-42a5-aca0-668bc5625705"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ut1 = UnixUserToken {
|
let mut ut1 = UserToken {
|
||||||
name: "testuser".to_string(),
|
name: "testuser".to_string(),
|
||||||
spn: "testuser@example.com".to_string(),
|
spn: "testuser@example.com".to_string(),
|
||||||
displayname: "Test User".to_string(),
|
displayname: "Test User".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"),
|
||||||
shell: None,
|
shell: None,
|
||||||
groups: vec![gt1.clone(), gt2],
|
groups: vec![gt1.clone(), gt2],
|
||||||
sshkeys: vec!["key-a".to_string()],
|
sshkeys: vec!["key-a".to_string()],
|
||||||
|
@ -1341,10 +1346,10 @@ mod tests {
|
||||||
|
|
||||||
// Now, get the memberships of the two groups.
|
// Now, get the memberships of the two groups.
|
||||||
let m1 = dbtxn
|
let m1 = dbtxn
|
||||||
.get_group_members("0302b99c-f0f6-41ab-9492-852692b0fd16")
|
.get_group_members(uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let m2 = dbtxn
|
let m2 = dbtxn
|
||||||
.get_group_members("b500be97-8552-42a5-aca0-668bc5625705")
|
.get_group_members(uuid::uuid!("b500be97-8552-42a5-aca0-668bc5625705"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(m1[0].name == "testuser");
|
assert!(m1[0].name == "testuser");
|
||||||
assert!(m2[0].name == "testuser");
|
assert!(m2[0].name == "testuser");
|
||||||
|
@ -1355,10 +1360,10 @@ mod tests {
|
||||||
|
|
||||||
// Check that the memberships have updated correctly.
|
// Check that the memberships have updated correctly.
|
||||||
let m1 = dbtxn
|
let m1 = dbtxn
|
||||||
.get_group_members("0302b99c-f0f6-41ab-9492-852692b0fd16")
|
.get_group_members(uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let m2 = dbtxn
|
let m2 = dbtxn
|
||||||
.get_group_members("b500be97-8552-42a5-aca0-668bc5625705")
|
.get_group_members(uuid::uuid!("b500be97-8552-42a5-aca0-668bc5625705"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(m1[0].name == "testuser");
|
assert!(m1[0].name == "testuser");
|
||||||
assert!(m2.is_empty());
|
assert!(m2.is_empty());
|
||||||
|
@ -1381,13 +1386,13 @@ mod tests {
|
||||||
let dbtxn = db.write().await;
|
let dbtxn = db.write().await;
|
||||||
assert!(dbtxn.migrate().is_ok());
|
assert!(dbtxn.migrate().is_ok());
|
||||||
|
|
||||||
let uuid1 = "0302b99c-f0f6-41ab-9492-852692b0fd16";
|
let uuid1 = uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16");
|
||||||
let mut ut1 = UnixUserToken {
|
let mut ut1 = UserToken {
|
||||||
name: "testuser".to_string(),
|
name: "testuser".to_string(),
|
||||||
spn: "testuser@example.com".to_string(),
|
spn: "testuser@example.com".to_string(),
|
||||||
displayname: "Test User".to_string(),
|
displayname: "Test User".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid1,
|
||||||
shell: None,
|
shell: None,
|
||||||
groups: Vec::new(),
|
groups: Vec::new(),
|
||||||
sshkeys: vec!["key-a".to_string()],
|
sshkeys: vec!["key-a".to_string()],
|
||||||
|
@ -1451,18 +1456,18 @@ mod tests {
|
||||||
let dbtxn = db.write().await;
|
let dbtxn = db.write().await;
|
||||||
assert!(dbtxn.migrate().is_ok());
|
assert!(dbtxn.migrate().is_ok());
|
||||||
|
|
||||||
let mut gt1 = UnixGroupToken {
|
let mut gt1 = GroupToken {
|
||||||
name: "testgroup".to_string(),
|
name: "testgroup".to_string(),
|
||||||
spn: "testgroup@example.com".to_string(),
|
spn: "testgroup@example.com".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let gt2 = UnixGroupToken {
|
let gt2 = GroupToken {
|
||||||
name: "testgroup".to_string(),
|
name: "testgroup".to_string(),
|
||||||
spn: "testgroup@example.com".to_string(),
|
spn: "testgroup@example.com".to_string(),
|
||||||
gidnumber: 2001,
|
gidnumber: 2001,
|
||||||
uuid: "799123b2-3802-4b19-b0b8-1ffae2aa9a4b".to_string(),
|
uuid: uuid::uuid!("799123b2-3802-4b19-b0b8-1ffae2aa9a4b"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let id_name = Id::Name("testgroup".to_string());
|
let id_name = Id::Name("testgroup".to_string());
|
||||||
|
@ -1475,7 +1480,7 @@ mod tests {
|
||||||
// test adding a group
|
// test adding a group
|
||||||
dbtxn.update_group(>1, 0).unwrap();
|
dbtxn.update_group(>1, 0).unwrap();
|
||||||
let r0 = dbtxn.get_group(&id_name).unwrap();
|
let r0 = dbtxn.get_group(&id_name).unwrap();
|
||||||
assert!(r0.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16");
|
assert!(r0.unwrap().0.uuid == uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"));
|
||||||
|
|
||||||
// Do the "rename" of gt1 which is what would allow gt2 to be valid.
|
// Do the "rename" of gt1 which is what would allow gt2 to be valid.
|
||||||
gt1.name = "testgroup2".to_string();
|
gt1.name = "testgroup2".to_string();
|
||||||
|
@ -1483,7 +1488,7 @@ mod tests {
|
||||||
// Now, add gt2 which dups on gt1 name/spn.
|
// Now, add gt2 which dups on gt1 name/spn.
|
||||||
dbtxn.update_group(>2, 0).unwrap();
|
dbtxn.update_group(>2, 0).unwrap();
|
||||||
let r2 = dbtxn.get_group(&id_name).unwrap();
|
let r2 = dbtxn.get_group(&id_name).unwrap();
|
||||||
assert!(r2.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b");
|
assert!(r2.unwrap().0.uuid == uuid::uuid!("799123b2-3802-4b19-b0b8-1ffae2aa9a4b"));
|
||||||
let r3 = dbtxn.get_group(&id_name2).unwrap();
|
let r3 = dbtxn.get_group(&id_name2).unwrap();
|
||||||
assert!(r3.is_none());
|
assert!(r3.is_none());
|
||||||
|
|
||||||
|
@ -1492,9 +1497,9 @@ mod tests {
|
||||||
|
|
||||||
// Both now coexist
|
// Both now coexist
|
||||||
let r4 = dbtxn.get_group(&id_name).unwrap();
|
let r4 = dbtxn.get_group(&id_name).unwrap();
|
||||||
assert!(r4.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b");
|
assert!(r4.unwrap().0.uuid == uuid::uuid!("799123b2-3802-4b19-b0b8-1ffae2aa9a4b"));
|
||||||
let r5 = dbtxn.get_group(&id_name2).unwrap();
|
let r5 = dbtxn.get_group(&id_name2).unwrap();
|
||||||
assert!(r5.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16");
|
assert!(r5.unwrap().0.uuid == uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"));
|
||||||
|
|
||||||
assert!(dbtxn.commit().is_ok());
|
assert!(dbtxn.commit().is_ok());
|
||||||
}
|
}
|
||||||
|
@ -1506,24 +1511,24 @@ mod tests {
|
||||||
let dbtxn = db.write().await;
|
let dbtxn = db.write().await;
|
||||||
assert!(dbtxn.migrate().is_ok());
|
assert!(dbtxn.migrate().is_ok());
|
||||||
|
|
||||||
let mut ut1 = UnixUserToken {
|
let mut ut1 = UserToken {
|
||||||
name: "testuser".to_string(),
|
name: "testuser".to_string(),
|
||||||
spn: "testuser@example.com".to_string(),
|
spn: "testuser@example.com".to_string(),
|
||||||
displayname: "Test User".to_string(),
|
displayname: "Test User".to_string(),
|
||||||
gidnumber: 2000,
|
gidnumber: 2000,
|
||||||
uuid: "0302b99c-f0f6-41ab-9492-852692b0fd16".to_string(),
|
uuid: uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"),
|
||||||
shell: None,
|
shell: None,
|
||||||
groups: Vec::new(),
|
groups: Vec::new(),
|
||||||
sshkeys: vec!["key-a".to_string()],
|
sshkeys: vec!["key-a".to_string()],
|
||||||
valid: true,
|
valid: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let ut2 = UnixUserToken {
|
let ut2 = UserToken {
|
||||||
name: "testuser".to_string(),
|
name: "testuser".to_string(),
|
||||||
spn: "testuser@example.com".to_string(),
|
spn: "testuser@example.com".to_string(),
|
||||||
displayname: "Test User".to_string(),
|
displayname: "Test User".to_string(),
|
||||||
gidnumber: 2001,
|
gidnumber: 2001,
|
||||||
uuid: "799123b2-3802-4b19-b0b8-1ffae2aa9a4b".to_string(),
|
uuid: uuid::uuid!("799123b2-3802-4b19-b0b8-1ffae2aa9a4b"),
|
||||||
shell: None,
|
shell: None,
|
||||||
groups: Vec::new(),
|
groups: Vec::new(),
|
||||||
sshkeys: vec!["key-a".to_string()],
|
sshkeys: vec!["key-a".to_string()],
|
||||||
|
@ -1540,7 +1545,7 @@ mod tests {
|
||||||
// test adding an account
|
// test adding an account
|
||||||
dbtxn.update_account(&ut1, 0).unwrap();
|
dbtxn.update_account(&ut1, 0).unwrap();
|
||||||
let r0 = dbtxn.get_account(&id_name).unwrap();
|
let r0 = dbtxn.get_account(&id_name).unwrap();
|
||||||
assert!(r0.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16");
|
assert!(r0.unwrap().0.uuid == uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"));
|
||||||
|
|
||||||
// Do the "rename" of gt1 which is what would allow gt2 to be valid.
|
// Do the "rename" of gt1 which is what would allow gt2 to be valid.
|
||||||
ut1.name = "testuser2".to_string();
|
ut1.name = "testuser2".to_string();
|
||||||
|
@ -1548,7 +1553,7 @@ mod tests {
|
||||||
// Now, add gt2 which dups on gt1 name/spn.
|
// Now, add gt2 which dups on gt1 name/spn.
|
||||||
dbtxn.update_account(&ut2, 0).unwrap();
|
dbtxn.update_account(&ut2, 0).unwrap();
|
||||||
let r2 = dbtxn.get_account(&id_name).unwrap();
|
let r2 = dbtxn.get_account(&id_name).unwrap();
|
||||||
assert!(r2.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b");
|
assert!(r2.unwrap().0.uuid == uuid::uuid!("799123b2-3802-4b19-b0b8-1ffae2aa9a4b"));
|
||||||
let r3 = dbtxn.get_account(&id_name2).unwrap();
|
let r3 = dbtxn.get_account(&id_name2).unwrap();
|
||||||
assert!(r3.is_none());
|
assert!(r3.is_none());
|
||||||
|
|
||||||
|
@ -1557,9 +1562,9 @@ mod tests {
|
||||||
|
|
||||||
// Both now coexist
|
// Both now coexist
|
||||||
let r4 = dbtxn.get_account(&id_name).unwrap();
|
let r4 = dbtxn.get_account(&id_name).unwrap();
|
||||||
assert!(r4.unwrap().0.uuid == "799123b2-3802-4b19-b0b8-1ffae2aa9a4b");
|
assert!(r4.unwrap().0.uuid == uuid::uuid!("799123b2-3802-4b19-b0b8-1ffae2aa9a4b"));
|
||||||
let r5 = dbtxn.get_account(&id_name2).unwrap();
|
let r5 = dbtxn.get_account(&id_name2).unwrap();
|
||||||
assert!(r5.unwrap().0.uuid == "0302b99c-f0f6-41ab-9492-852692b0fd16");
|
assert!(r5.unwrap().0.uuid == uuid::uuid!("0302b99c-f0f6-41ab-9492-852692b0fd16"));
|
||||||
|
|
||||||
assert!(dbtxn.commit().is_ok());
|
assert!(dbtxn.commit().is_ok());
|
||||||
}
|
}
|
||||||
|
|
67
unix_integration/src/idprovider/interface.rs
Normal file
67
unix_integration/src/idprovider/interface.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// Errors that the IdProvider may return. These drive the resolver state machine
|
||||||
|
/// and should be carefully selected to match your expected errors.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum IdpError {
|
||||||
|
/// An error occurred in the underlying communication to the Idp. A timeout or
|
||||||
|
/// or other communication issue exists. The resolver will take this provider
|
||||||
|
/// offline.
|
||||||
|
Transport,
|
||||||
|
/// The provider is online but the provider module is not current authorised with
|
||||||
|
/// the idp. After returning this error the operation will be retried after a
|
||||||
|
/// successful authentication.
|
||||||
|
ProviderUnauthorised,
|
||||||
|
/// The provider made an invalid request to the idp, and the result is not able to
|
||||||
|
/// be used by the resolver.
|
||||||
|
BadRequest,
|
||||||
|
/// The idp has indicated that the requested resource does not exist and should
|
||||||
|
/// be considered deleted, removed, or not present.
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Id {
|
||||||
|
Name(String),
|
||||||
|
Gid(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct GroupToken {
|
||||||
|
pub name: String,
|
||||||
|
pub spn: String,
|
||||||
|
pub uuid: Uuid,
|
||||||
|
pub gidnumber: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct UserToken {
|
||||||
|
pub name: String,
|
||||||
|
pub spn: String,
|
||||||
|
pub uuid: Uuid,
|
||||||
|
pub gidnumber: u32,
|
||||||
|
pub displayname: String,
|
||||||
|
pub shell: Option<String>,
|
||||||
|
pub groups: Vec<GroupToken>,
|
||||||
|
// Could there be a better type here?
|
||||||
|
pub sshkeys: Vec<String>,
|
||||||
|
// Defaults to false.
|
||||||
|
pub valid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait IdProvider {
|
||||||
|
async fn provider_authenticate(&self) -> Result<(), IdpError>;
|
||||||
|
|
||||||
|
async fn unix_user_get(&self, id: &Id) -> Result<UserToken, IdpError>;
|
||||||
|
|
||||||
|
async fn unix_user_authenticate(
|
||||||
|
&self,
|
||||||
|
id: &Id,
|
||||||
|
cred: &str,
|
||||||
|
) -> Result<Option<UserToken>, IdpError>;
|
||||||
|
|
||||||
|
async fn unix_group_get(&self, id: &Id) -> Result<GroupToken, IdpError>;
|
||||||
|
}
|
264
unix_integration/src/idprovider/kanidm.rs
Normal file
264
unix_integration/src/idprovider/kanidm.rs
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use kanidm_client::{ClientError, KanidmClient, StatusCode};
|
||||||
|
use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use super::interface::{GroupToken, Id, IdProvider, IdpError, UserToken};
|
||||||
|
|
||||||
|
pub struct KanidmProvider {
|
||||||
|
client: RwLock<KanidmClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KanidmProvider {
|
||||||
|
pub fn new(client: KanidmClient) -> Self {
|
||||||
|
KanidmProvider {
|
||||||
|
client: RwLock::new(client),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnixUserToken> for UserToken {
|
||||||
|
fn from(value: UnixUserToken) -> UserToken {
|
||||||
|
let UnixUserToken {
|
||||||
|
name,
|
||||||
|
spn,
|
||||||
|
displayname,
|
||||||
|
gidnumber,
|
||||||
|
uuid,
|
||||||
|
shell,
|
||||||
|
groups,
|
||||||
|
sshkeys,
|
||||||
|
valid,
|
||||||
|
} = value;
|
||||||
|
|
||||||
|
let groups = groups
|
||||||
|
.into_iter()
|
||||||
|
.map(GroupToken::from)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
UserToken {
|
||||||
|
name,
|
||||||
|
spn,
|
||||||
|
uuid,
|
||||||
|
gidnumber,
|
||||||
|
displayname,
|
||||||
|
shell,
|
||||||
|
groups,
|
||||||
|
sshkeys,
|
||||||
|
valid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnixGroupToken> for GroupToken {
|
||||||
|
fn from(value: UnixGroupToken) -> GroupToken {
|
||||||
|
let UnixGroupToken {
|
||||||
|
name,
|
||||||
|
spn,
|
||||||
|
uuid,
|
||||||
|
gidnumber,
|
||||||
|
} = value;
|
||||||
|
|
||||||
|
GroupToken {
|
||||||
|
name,
|
||||||
|
spn,
|
||||||
|
uuid,
|
||||||
|
gidnumber,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl IdProvider for KanidmProvider {
|
||||||
|
// Needs .read on all types except re-auth.
|
||||||
|
|
||||||
|
async fn provider_authenticate(&self) -> Result<(), IdpError> {
|
||||||
|
match self.client.write().await.auth_anonymous().await {
|
||||||
|
Ok(_uat) => Ok(()),
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "Provider authentication failed");
|
||||||
|
Err(IdpError::ProviderUnauthorised)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unix_user_get(&self, id: &Id) -> Result<UserToken, IdpError> {
|
||||||
|
match self
|
||||||
|
.client
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.idm_account_unix_token_get(id.to_string().as_str())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(tok) => Ok(UserToken::from(tok)),
|
||||||
|
Err(ClientError::Transport(err)) => {
|
||||||
|
error!(?err);
|
||||||
|
Err(IdpError::Transport)
|
||||||
|
}
|
||||||
|
Err(ClientError::Http(StatusCode::UNAUTHORIZED, reason, opid)) => {
|
||||||
|
match reason {
|
||||||
|
Some(OperationError::NotAuthenticated) => warn!(
|
||||||
|
"session not authenticated - attempting reauthentication - eventid {}",
|
||||||
|
opid
|
||||||
|
),
|
||||||
|
Some(OperationError::SessionExpired) => warn!(
|
||||||
|
"session expired - attempting reauthentication - eventid {}",
|
||||||
|
opid
|
||||||
|
),
|
||||||
|
e => error!(
|
||||||
|
"authentication error {:?}, moving to offline - eventid {}",
|
||||||
|
e, opid
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Err(IdpError::ProviderUnauthorised)
|
||||||
|
}
|
||||||
|
Err(ClientError::Http(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Some(OperationError::NoMatchingEntries),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
| Err(ClientError::Http(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
Some(OperationError::NoMatchingEntries),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
| Err(ClientError::Http(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Some(OperationError::InvalidAccountState(_)),
|
||||||
|
opid,
|
||||||
|
)) => {
|
||||||
|
debug!(
|
||||||
|
?opid,
|
||||||
|
"entry has been removed or is no longer a valid posix account"
|
||||||
|
);
|
||||||
|
Err(IdpError::NotFound)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "client error");
|
||||||
|
Err(IdpError::BadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unix_user_authenticate(
|
||||||
|
&self,
|
||||||
|
id: &Id,
|
||||||
|
cred: &str,
|
||||||
|
) -> Result<Option<UserToken>, IdpError> {
|
||||||
|
match self
|
||||||
|
.client
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.idm_account_unix_cred_verify(id.to_string().as_str(), cred)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Some(n_tok)) => Ok(Some(UserToken::from(n_tok))),
|
||||||
|
Ok(None) => Ok(None),
|
||||||
|
Err(ClientError::Transport(err)) => {
|
||||||
|
error!(?err);
|
||||||
|
Err(IdpError::Transport)
|
||||||
|
}
|
||||||
|
Err(ClientError::Http(StatusCode::UNAUTHORIZED, reason, opid)) => {
|
||||||
|
match reason {
|
||||||
|
Some(OperationError::NotAuthenticated) => warn!(
|
||||||
|
"session not authenticated - attempting reauthentication - eventid {}",
|
||||||
|
opid
|
||||||
|
),
|
||||||
|
Some(OperationError::SessionExpired) => warn!(
|
||||||
|
"session expired - attempting reauthentication - eventid {}",
|
||||||
|
opid
|
||||||
|
),
|
||||||
|
e => error!(
|
||||||
|
"authentication error {:?}, moving to offline - eventid {}",
|
||||||
|
e, opid
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Err(IdpError::ProviderUnauthorised)
|
||||||
|
}
|
||||||
|
Err(ClientError::Http(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Some(OperationError::NoMatchingEntries),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
| Err(ClientError::Http(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
Some(OperationError::NoMatchingEntries),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
| Err(ClientError::Http(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Some(OperationError::InvalidAccountState(_)),
|
||||||
|
opid,
|
||||||
|
)) => {
|
||||||
|
error!(
|
||||||
|
"unknown account or is not a valid posix account - eventid {}",
|
||||||
|
opid
|
||||||
|
);
|
||||||
|
Err(IdpError::NotFound)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "client error");
|
||||||
|
// Some other unknown processing error?
|
||||||
|
Err(IdpError::BadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn unix_group_get(&self, id: &Id) -> Result<GroupToken, IdpError> {
|
||||||
|
match self
|
||||||
|
.client
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.idm_group_unix_token_get(id.to_string().as_str())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(tok) => Ok(GroupToken::from(tok)),
|
||||||
|
Err(ClientError::Transport(err)) => {
|
||||||
|
error!(?err);
|
||||||
|
Err(IdpError::Transport)
|
||||||
|
}
|
||||||
|
Err(ClientError::Http(StatusCode::UNAUTHORIZED, reason, opid)) => {
|
||||||
|
match reason {
|
||||||
|
Some(OperationError::NotAuthenticated) => warn!(
|
||||||
|
"session not authenticated - attempting reauthentication - eventid {}",
|
||||||
|
opid
|
||||||
|
),
|
||||||
|
Some(OperationError::SessionExpired) => warn!(
|
||||||
|
"session expired - attempting reauthentication - eventid {}",
|
||||||
|
opid
|
||||||
|
),
|
||||||
|
e => error!(
|
||||||
|
"authentication error {:?}, moving to offline - eventid {}",
|
||||||
|
e, opid
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Err(IdpError::ProviderUnauthorised)
|
||||||
|
}
|
||||||
|
Err(ClientError::Http(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Some(OperationError::NoMatchingEntries),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
| Err(ClientError::Http(
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
Some(OperationError::NoMatchingEntries),
|
||||||
|
opid,
|
||||||
|
))
|
||||||
|
| Err(ClientError::Http(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
Some(OperationError::InvalidAccountState(_)),
|
||||||
|
opid,
|
||||||
|
)) => {
|
||||||
|
debug!(
|
||||||
|
?opid,
|
||||||
|
"entry has been removed or is no longer a valid posix group"
|
||||||
|
);
|
||||||
|
Err(IdpError::NotFound)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(?err, "client error");
|
||||||
|
Err(IdpError::BadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
unix_integration/src/idprovider/mod.rs
Normal file
2
unix_integration/src/idprovider/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod interface;
|
||||||
|
pub mod kanidm;
|
|
@ -26,6 +26,8 @@ pub mod constants;
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
pub mod db;
|
pub mod db;
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
|
pub mod idprovider;
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
#[cfg(all(target_family = "unix", feature = "selinux"))]
|
||||||
pub mod selinux_util;
|
pub mod selinux_util;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// use async_trait::async_trait;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
@ -6,26 +7,18 @@ use std::path::Path;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use kanidm_client::{ClientError, KanidmClient};
|
|
||||||
use kanidm_proto::v1::{OperationError, UnixGroupToken, UnixUserToken};
|
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use reqwest::StatusCode;
|
use tokio::sync::Mutex;
|
||||||
use tokio::sync::{Mutex, RwLock};
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::db::{Cache, CacheTxn, Db};
|
use crate::db::{Cache, CacheTxn, Db};
|
||||||
|
use crate::idprovider::interface::{GroupToken, Id, IdProvider, IdpError, UserToken};
|
||||||
use crate::unix_config::{HomeAttr, UidAttr};
|
use crate::unix_config::{HomeAttr, UidAttr};
|
||||||
use crate::unix_proto::{HomeDirectoryInfo, NssGroup, NssUser};
|
use crate::unix_proto::{HomeDirectoryInfo, NssGroup, NssUser};
|
||||||
|
|
||||||
// use crate::unix_passwd::{EtcUser, EtcGroup};
|
// use crate::unix_passwd::{EtcUser, EtcGroup};
|
||||||
|
|
||||||
const NXCACHE_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(2048) };
|
const NXCACHE_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(128) };
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Id {
|
|
||||||
Name(String),
|
|
||||||
Gid(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum CacheState {
|
enum CacheState {
|
||||||
|
@ -35,9 +28,14 @@ enum CacheState {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Resolver {
|
pub struct Resolver<I>
|
||||||
|
where
|
||||||
|
I: IdProvider,
|
||||||
|
{
|
||||||
|
// Generic / modular types.
|
||||||
db: Db,
|
db: Db,
|
||||||
client: RwLock<KanidmClient>,
|
client: I,
|
||||||
|
// Types to update still.
|
||||||
state: Mutex<CacheState>,
|
state: Mutex<CacheState>,
|
||||||
pam_allow_groups: BTreeSet<String>,
|
pam_allow_groups: BTreeSet<String>,
|
||||||
timeout_seconds: u64,
|
timeout_seconds: u64,
|
||||||
|
@ -61,18 +59,16 @@ impl ToString for Id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resolver {
|
impl<I> Resolver<I>
|
||||||
|
where
|
||||||
|
I: IdProvider,
|
||||||
|
{
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
db: Db,
|
db: Db,
|
||||||
// need db path
|
client: I,
|
||||||
// path: &str,
|
|
||||||
// tpm_policy: &TpmPolicy,
|
|
||||||
|
|
||||||
// cache timeout
|
// cache timeout
|
||||||
timeout_seconds: u64,
|
timeout_seconds: u64,
|
||||||
//
|
|
||||||
client: KanidmClient,
|
|
||||||
pam_allow_groups: Vec<String>,
|
pam_allow_groups: Vec<String>,
|
||||||
default_shell: String,
|
default_shell: String,
|
||||||
home_prefix: String,
|
home_prefix: String,
|
||||||
|
@ -97,7 +93,7 @@ impl Resolver {
|
||||||
// being valid from "now".
|
// being valid from "now".
|
||||||
Ok(Resolver {
|
Ok(Resolver {
|
||||||
db,
|
db,
|
||||||
client: RwLock::new(client),
|
client,
|
||||||
state: Mutex::new(CacheState::OfflineNextCheck(SystemTime::now())),
|
state: Mutex::new(CacheState::OfflineNextCheck(SystemTime::now())),
|
||||||
timeout_seconds,
|
timeout_seconds,
|
||||||
pam_allow_groups: pam_allow_groups.into_iter().collect(),
|
pam_allow_groups: pam_allow_groups.into_iter().collect(),
|
||||||
|
@ -150,12 +146,12 @@ impl Resolver {
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_cached_usertokens(&self) -> Result<Vec<UnixUserToken>, ()> {
|
async fn get_cached_usertokens(&self) -> Result<Vec<UserToken>, ()> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
dbtxn.get_accounts().map_err(|_| ())
|
dbtxn.get_accounts().map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_cached_grouptokens(&self) -> Result<Vec<UnixGroupToken>, ()> {
|
async fn get_cached_grouptokens(&self) -> Result<Vec<GroupToken>, ()> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
dbtxn.get_groups().map_err(|_| ())
|
dbtxn.get_groups().map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
@ -193,10 +189,7 @@ impl Resolver {
|
||||||
nxset_txn.contains(&Id::Gid(idnumber)) || nxset_txn.contains(&Id::Name(name.to_string()))
|
nxset_txn.contains(&Id::Gid(idnumber)) || nxset_txn.contains(&Id::Name(name.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_cached_usertoken(
|
async fn get_cached_usertoken(&self, account_id: &Id) -> Result<(bool, Option<UserToken>), ()> {
|
||||||
&self,
|
|
||||||
account_id: &Id,
|
|
||||||
) -> Result<(bool, Option<UnixUserToken>), ()> {
|
|
||||||
// Account_id could be:
|
// Account_id could be:
|
||||||
// * gidnumber
|
// * gidnumber
|
||||||
// * name
|
// * name
|
||||||
|
@ -244,10 +237,7 @@ impl Resolver {
|
||||||
} // end match r
|
} // end match r
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_cached_grouptoken(
|
async fn get_cached_grouptoken(&self, grp_id: &Id) -> Result<(bool, Option<GroupToken>), ()> {
|
||||||
&self,
|
|
||||||
grp_id: &Id,
|
|
||||||
) -> Result<(bool, Option<UnixGroupToken>), ()> {
|
|
||||||
// grp_id could be:
|
// grp_id could be:
|
||||||
// * gidnumber
|
// * gidnumber
|
||||||
// * name
|
// * name
|
||||||
|
@ -295,7 +285,7 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_cache_usertoken(&self, token: &mut UnixUserToken) -> Result<(), ()> {
|
async fn set_cache_usertoken(&self, token: &mut UserToken) -> Result<(), ()> {
|
||||||
// Set an expiry
|
// Set an expiry
|
||||||
let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
|
let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
|
||||||
let offset = ex_time
|
let offset = ex_time
|
||||||
|
@ -350,7 +340,7 @@ impl Resolver {
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_cache_grouptoken(&self, token: &UnixGroupToken) -> Result<(), ()> {
|
async fn set_cache_grouptoken(&self, token: &GroupToken) -> Result<(), ()> {
|
||||||
// Set an expiry
|
// Set an expiry
|
||||||
let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
|
let ex_time = SystemTime::now() + Duration::from_secs(self.timeout_seconds);
|
||||||
let offset = ex_time
|
let offset = ex_time
|
||||||
|
@ -366,7 +356,7 @@ impl Resolver {
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_cache_usertoken(&self, a_uuid: &str) -> Result<(), ()> {
|
async fn delete_cache_usertoken(&self, a_uuid: Uuid) -> Result<(), ()> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
dbtxn
|
dbtxn
|
||||||
.delete_account(a_uuid)
|
.delete_account(a_uuid)
|
||||||
|
@ -374,7 +364,7 @@ impl Resolver {
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_cache_grouptoken(&self, g_uuid: &str) -> Result<(), ()> {
|
async fn delete_cache_grouptoken(&self, g_uuid: Uuid) -> Result<(), ()> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
dbtxn
|
dbtxn
|
||||||
.delete_group(g_uuid)
|
.delete_group(g_uuid)
|
||||||
|
@ -382,7 +372,7 @@ impl Resolver {
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_cache_userpassword(&self, a_uuid: &str, cred: &str) -> Result<(), ()> {
|
async fn set_cache_userpassword(&self, a_uuid: Uuid, cred: &str) -> Result<(), ()> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
dbtxn
|
dbtxn
|
||||||
.update_account_password(a_uuid, cred)
|
.update_account_password(a_uuid, cred)
|
||||||
|
@ -390,7 +380,7 @@ impl Resolver {
|
||||||
.map_err(|_| ())
|
.map_err(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_cache_userpassword(&self, a_uuid: &str, cred: &str) -> Result<bool, ()> {
|
async fn check_cache_userpassword(&self, a_uuid: Uuid, cred: &str) -> Result<bool, ()> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
dbtxn
|
dbtxn
|
||||||
.check_account_password(a_uuid, cred)
|
.check_account_password(a_uuid, cred)
|
||||||
|
@ -401,19 +391,13 @@ impl Resolver {
|
||||||
async fn refresh_usertoken(
|
async fn refresh_usertoken(
|
||||||
&self,
|
&self,
|
||||||
account_id: &Id,
|
account_id: &Id,
|
||||||
token: Option<UnixUserToken>,
|
token: Option<UserToken>,
|
||||||
) -> Result<Option<UnixUserToken>, ()> {
|
) -> Result<Option<UserToken>, ()> {
|
||||||
match self
|
match self.client.unix_user_get(account_id).await {
|
||||||
.client
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.idm_account_unix_token_get(account_id.to_string().as_str())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(mut n_tok) => {
|
Ok(mut n_tok) => {
|
||||||
if self.check_nxset(&n_tok.name, n_tok.gidnumber).await {
|
if self.check_nxset(&n_tok.name, n_tok.gidnumber).await {
|
||||||
// Refuse to release the token, it's in the denied set.
|
// Refuse to release the token, it's in the denied set.
|
||||||
self.delete_cache_usertoken(&n_tok.uuid).await?;
|
self.delete_cache_usertoken(n_tok.uuid).await?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
// We have the token!
|
// We have the token!
|
||||||
|
@ -421,85 +405,49 @@ impl Resolver {
|
||||||
Ok(Some(n_tok))
|
Ok(Some(n_tok))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(IdpError::Transport) => {
|
||||||
match e {
|
error!("transport error, moving to offline");
|
||||||
ClientError::Transport(er) => {
|
|
||||||
error!("transport error, moving to offline -> {:?}", er);
|
|
||||||
// Something went wrong, mark offline.
|
// Something went wrong, mark offline.
|
||||||
let time = SystemTime::now().add(Duration::from_secs(15));
|
let time = SystemTime::now().add(Duration::from_secs(15));
|
||||||
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
||||||
.await;
|
.await;
|
||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
ClientError::Http(StatusCode::UNAUTHORIZED, reason, opid) => {
|
Err(IdpError::ProviderUnauthorised) => {
|
||||||
match reason {
|
|
||||||
Some(OperationError::NotAuthenticated) =>
|
|
||||||
warn!("session not authenticated - attempting reauthentication - eventid {}", opid),
|
|
||||||
Some(OperationError::SessionExpired) =>
|
|
||||||
warn!("session expired - attempting reauthentication - eventid {}", opid),
|
|
||||||
e =>
|
|
||||||
error!(
|
|
||||||
"authentication error {:?}, moving to offline - eventid {}", e, opid
|
|
||||||
),
|
|
||||||
};
|
|
||||||
// Something went wrong, mark offline to force a re-auth ASAP.
|
// Something went wrong, mark offline to force a re-auth ASAP.
|
||||||
let time = SystemTime::now().sub(Duration::from_secs(1));
|
let time = SystemTime::now().sub(Duration::from_secs(1));
|
||||||
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
||||||
.await;
|
.await;
|
||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
ClientError::Http(
|
Err(IdpError::NotFound) => {
|
||||||
StatusCode::BAD_REQUEST,
|
// We were able to contact the server but the entry has been removed, or
|
||||||
Some(OperationError::NoMatchingEntries),
|
|
||||||
opid,
|
|
||||||
)
|
|
||||||
| ClientError::Http(
|
|
||||||
StatusCode::NOT_FOUND,
|
|
||||||
Some(OperationError::NoMatchingEntries),
|
|
||||||
opid,
|
|
||||||
)
|
|
||||||
| ClientError::Http(
|
|
||||||
StatusCode::BAD_REQUEST,
|
|
||||||
Some(OperationError::InvalidAccountState(_)),
|
|
||||||
opid,
|
|
||||||
) => {
|
|
||||||
// We wele able to contact the server but the entry has been removed, or
|
|
||||||
// is not longer a valid posix account.
|
// is not longer a valid posix account.
|
||||||
debug!("entry has been removed or is no longer a valid posix account, clearing from cache ... - eventid {}", opid);
|
|
||||||
if let Some(tok) = token {
|
if let Some(tok) = token {
|
||||||
self.delete_cache_usertoken(&tok.uuid).await?;
|
self.delete_cache_usertoken(tok.uuid).await?;
|
||||||
};
|
};
|
||||||
// Cache the NX here.
|
// Cache the NX here.
|
||||||
self.set_nxcache(account_id).await;
|
self.set_nxcache(account_id).await;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
er => {
|
Err(IdpError::BadRequest) => {
|
||||||
error!("client error -> {:?}", er);
|
|
||||||
// Some other transient error, continue with the token.
|
// Some other transient error, continue with the token.
|
||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn refresh_grouptoken(
|
async fn refresh_grouptoken(
|
||||||
&self,
|
&self,
|
||||||
grp_id: &Id,
|
grp_id: &Id,
|
||||||
token: Option<UnixGroupToken>,
|
token: Option<GroupToken>,
|
||||||
) -> Result<Option<UnixGroupToken>, ()> {
|
) -> Result<Option<GroupToken>, ()> {
|
||||||
match self
|
match self.client.unix_group_get(grp_id).await {
|
||||||
.client
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.idm_group_unix_token_get(grp_id.to_string().as_str())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(n_tok) => {
|
Ok(n_tok) => {
|
||||||
if self.check_nxset(&n_tok.name, n_tok.gidnumber).await {
|
if self.check_nxset(&n_tok.name, n_tok.gidnumber).await {
|
||||||
// Refuse to release the token, it's in the denied set.
|
// Refuse to release the token, it's in the denied set.
|
||||||
self.delete_cache_grouptoken(&n_tok.uuid).await?;
|
self.delete_cache_grouptoken(n_tok.uuid).await?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
// We have the token!
|
// We have the token!
|
||||||
|
@ -507,66 +455,37 @@ impl Resolver {
|
||||||
Ok(Some(n_tok))
|
Ok(Some(n_tok))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(IdpError::Transport) => {
|
||||||
match e {
|
error!("transport error, moving to offline");
|
||||||
ClientError::Transport(er) => {
|
|
||||||
error!("transport error, moving to offline -> {:?}", er);
|
|
||||||
// Something went wrong, mark offline.
|
// Something went wrong, mark offline.
|
||||||
let time = SystemTime::now().add(Duration::from_secs(15));
|
let time = SystemTime::now().add(Duration::from_secs(15));
|
||||||
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
||||||
.await;
|
.await;
|
||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
ClientError::Http(
|
Err(IdpError::ProviderUnauthorised) => {
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
Some(OperationError::NotAuthenticated),
|
|
||||||
opid,
|
|
||||||
) => {
|
|
||||||
error!(
|
|
||||||
"transport unauthenticated, moving to offline - eventid {}",
|
|
||||||
opid
|
|
||||||
);
|
|
||||||
// Something went wrong, mark offline.
|
// Something went wrong, mark offline.
|
||||||
let time = SystemTime::now().add(Duration::from_secs(15));
|
let time = SystemTime::now().add(Duration::from_secs(15));
|
||||||
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
||||||
.await;
|
.await;
|
||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
ClientError::Http(
|
Err(IdpError::NotFound) => {
|
||||||
StatusCode::BAD_REQUEST,
|
|
||||||
Some(OperationError::NoMatchingEntries),
|
|
||||||
opid,
|
|
||||||
)
|
|
||||||
| ClientError::Http(
|
|
||||||
StatusCode::NOT_FOUND,
|
|
||||||
Some(OperationError::NoMatchingEntries),
|
|
||||||
opid,
|
|
||||||
)
|
|
||||||
| ClientError::Http(
|
|
||||||
StatusCode::BAD_REQUEST,
|
|
||||||
Some(OperationError::InvalidAccountState(_)),
|
|
||||||
opid,
|
|
||||||
) => {
|
|
||||||
debug!("entry has been removed or is no longer a valid posix group, clearing from cache ... - eventid {}", opid);
|
|
||||||
if let Some(tok) = token {
|
if let Some(tok) = token {
|
||||||
self.delete_cache_grouptoken(&tok.uuid).await?;
|
self.delete_cache_grouptoken(tok.uuid).await?;
|
||||||
};
|
};
|
||||||
// Cache the NX here.
|
// Cache the NX here.
|
||||||
self.set_nxcache(grp_id).await;
|
self.set_nxcache(grp_id).await;
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
er => {
|
Err(IdpError::BadRequest) => {
|
||||||
error!("client error -> {:?}", er);
|
|
||||||
// Some other transient error, continue with the token.
|
// Some other transient error, continue with the token.
|
||||||
Ok(token)
|
Ok(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_usertoken(&self, account_id: Id) -> Result<Option<UnixUserToken>, ()> {
|
async fn get_usertoken(&self, account_id: Id) -> Result<Option<UserToken>, ()> {
|
||||||
debug!("get_usertoken");
|
debug!("get_usertoken");
|
||||||
// get the item from the cache
|
// get the item from the cache
|
||||||
let (expired, item) = self.get_cached_usertoken(&account_id).await.map_err(|e| {
|
let (expired, item) = self.get_cached_usertoken(&account_id).await.map_err(|e| {
|
||||||
|
@ -618,7 +537,7 @@ impl Resolver {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_grouptoken(&self, grp_id: Id) -> Result<Option<UnixGroupToken>, ()> {
|
async fn get_grouptoken(&self, grp_id: Id) -> Result<Option<GroupToken>, ()> {
|
||||||
debug!("get_grouptoken");
|
debug!("get_grouptoken");
|
||||||
let (expired, item) = self.get_cached_grouptoken(&grp_id).await.map_err(|e| {
|
let (expired, item) = self.get_cached_grouptoken(&grp_id).await.map_err(|e| {
|
||||||
debug!("get_grouptoken error -> {:?}", e);
|
debug!("get_grouptoken error -> {:?}", e);
|
||||||
|
@ -665,11 +584,11 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_groupmembers(&self, uuid: &str) -> Vec<String> {
|
async fn get_groupmembers(&self, g_uuid: Uuid) -> Vec<String> {
|
||||||
let dbtxn = self.db.write().await;
|
let dbtxn = self.db.write().await;
|
||||||
|
|
||||||
dbtxn
|
dbtxn
|
||||||
.get_group_members(uuid)
|
.get_group_members(g_uuid)
|
||||||
.unwrap_or_else(|_| Vec::new())
|
.unwrap_or_else(|_| Vec::new())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ut| self.token_uidattr(&ut))
|
.map(|ut| self.token_uidattr(&ut))
|
||||||
|
@ -692,37 +611,37 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn token_homedirectory_alias(&self, token: &UnixUserToken) -> Option<String> {
|
fn token_homedirectory_alias(&self, token: &UserToken) -> Option<String> {
|
||||||
self.home_alias.map(|t| match t {
|
self.home_alias.map(|t| match t {
|
||||||
// If we have an alias. use it.
|
// If we have an alias. use it.
|
||||||
HomeAttr::Uuid => token.uuid.as_str().to_string(),
|
HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
|
||||||
HomeAttr::Spn => token.spn.as_str().to_string(),
|
HomeAttr::Spn => token.spn.as_str().to_string(),
|
||||||
HomeAttr::Name => token.name.as_str().to_string(),
|
HomeAttr::Name => token.name.as_str().to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn token_homedirectory_attr(&self, token: &UnixUserToken) -> String {
|
fn token_homedirectory_attr(&self, token: &UserToken) -> String {
|
||||||
match self.home_attr {
|
match self.home_attr {
|
||||||
HomeAttr::Uuid => token.uuid.as_str().to_string(),
|
HomeAttr::Uuid => token.uuid.hyphenated().to_string(),
|
||||||
HomeAttr::Spn => token.spn.as_str().to_string(),
|
HomeAttr::Spn => token.spn.as_str().to_string(),
|
||||||
HomeAttr::Name => token.name.as_str().to_string(),
|
HomeAttr::Name => token.name.as_str().to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn token_homedirectory(&self, token: &UnixUserToken) -> String {
|
fn token_homedirectory(&self, token: &UserToken) -> String {
|
||||||
self.token_homedirectory_alias(token)
|
self.token_homedirectory_alias(token)
|
||||||
.unwrap_or_else(|| self.token_homedirectory_attr(token))
|
.unwrap_or_else(|| self.token_homedirectory_attr(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn token_abs_homedirectory(&self, token: &UnixUserToken) -> String {
|
fn token_abs_homedirectory(&self, token: &UserToken) -> String {
|
||||||
format!("{}{}", self.home_prefix, self.token_homedirectory(token))
|
format!("{}{}", self.home_prefix, self.token_homedirectory(token))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn token_uidattr(&self, token: &UnixUserToken) -> String {
|
fn token_uidattr(&self, token: &UserToken) -> String {
|
||||||
match self.uid_attr_map {
|
match self.uid_attr_map {
|
||||||
UidAttr::Spn => token.spn.as_str(),
|
UidAttr::Spn => token.spn.as_str(),
|
||||||
UidAttr::Name => token.name.as_str(),
|
UidAttr::Name => token.name.as_str(),
|
||||||
|
@ -764,7 +683,7 @@ impl Resolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn token_gidattr(&self, token: &UnixGroupToken) -> String {
|
fn token_gidattr(&self, token: &GroupToken) -> String {
|
||||||
match self.gid_attr_map {
|
match self.gid_attr_map {
|
||||||
UidAttr::Spn => token.spn.as_str(),
|
UidAttr::Spn => token.spn.as_str(),
|
||||||
UidAttr::Name => token.name.as_str(),
|
UidAttr::Name => token.name.as_str(),
|
||||||
|
@ -776,7 +695,7 @@ impl Resolver {
|
||||||
let l = self.get_cached_grouptokens().await?;
|
let l = self.get_cached_grouptokens().await?;
|
||||||
let mut r: Vec<_> = Vec::with_capacity(l.len());
|
let mut r: Vec<_> = Vec::with_capacity(l.len());
|
||||||
for tok in l.into_iter() {
|
for tok in l.into_iter() {
|
||||||
let members = self.get_groupmembers(&tok.uuid).await;
|
let members = self.get_groupmembers(tok.uuid).await;
|
||||||
r.push(NssGroup {
|
r.push(NssGroup {
|
||||||
name: self.token_gidattr(&tok),
|
name: self.token_gidattr(&tok),
|
||||||
gid: tok.gidnumber,
|
gid: tok.gidnumber,
|
||||||
|
@ -791,7 +710,7 @@ impl Resolver {
|
||||||
// Get members set.
|
// Get members set.
|
||||||
match token {
|
match token {
|
||||||
Some(tok) => {
|
Some(tok) => {
|
||||||
let members = self.get_groupmembers(&tok.uuid).await;
|
let members = self.get_groupmembers(tok.uuid).await;
|
||||||
Ok(Some(NssGroup {
|
Ok(Some(NssGroup {
|
||||||
name: self.token_gidattr(&tok),
|
name: self.token_gidattr(&tok),
|
||||||
gid: tok.gidnumber,
|
gid: tok.gidnumber,
|
||||||
|
@ -812,28 +731,22 @@ impl Resolver {
|
||||||
|
|
||||||
async fn online_account_authenticate(
|
async fn online_account_authenticate(
|
||||||
&self,
|
&self,
|
||||||
token: &Option<UnixUserToken>,
|
token: &Option<UserToken>,
|
||||||
account_id: &str,
|
account_id: &Id,
|
||||||
cred: &str,
|
cred: &str,
|
||||||
) -> Result<Option<bool>, ()> {
|
) -> Result<Option<bool>, ()> {
|
||||||
debug!("Attempt online password check");
|
debug!("Attempt online password check");
|
||||||
// We are online, attempt the pw to the server.
|
// We are online, attempt the pw to the server.
|
||||||
match self
|
match self.client.unix_user_authenticate(account_id, cred).await {
|
||||||
.client
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.idm_account_unix_cred_verify(account_id, cred)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(Some(mut n_tok)) => {
|
Ok(Some(mut n_tok)) => {
|
||||||
if self.check_nxset(&n_tok.name, n_tok.gidnumber).await {
|
if self.check_nxset(&n_tok.name, n_tok.gidnumber).await {
|
||||||
// Refuse to release the token, it's in the denied set.
|
// Refuse to release the token, it's in the denied set.
|
||||||
self.delete_cache_usertoken(&n_tok.uuid).await?;
|
self.delete_cache_usertoken(n_tok.uuid).await?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
debug!("online password check success.");
|
debug!("online password check success.");
|
||||||
self.set_cache_usertoken(&mut n_tok).await?;
|
self.set_cache_usertoken(&mut n_tok).await?;
|
||||||
self.set_cache_userpassword(&n_tok.uuid, cred).await?;
|
self.set_cache_userpassword(n_tok.uuid, cred).await?;
|
||||||
Ok(Some(true))
|
Ok(Some(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -842,80 +755,46 @@ impl Resolver {
|
||||||
// PW failed the check.
|
// PW failed the check.
|
||||||
Ok(Some(false))
|
Ok(Some(false))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(IdpError::Transport) => {
|
||||||
match e {
|
error!("transport error, moving to offline");
|
||||||
ClientError::Transport(er) => {
|
|
||||||
error!("transport error, moving to offline -> {:?}", er);
|
|
||||||
// Something went wrong, mark offline.
|
// Something went wrong, mark offline.
|
||||||
let time = SystemTime::now().add(Duration::from_secs(15));
|
let time = SystemTime::now().add(Duration::from_secs(15));
|
||||||
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
||||||
.await;
|
.await;
|
||||||
match token.as_ref() {
|
match token.as_ref() {
|
||||||
Some(t) => self.check_cache_userpassword(&t.uuid, cred).await.map(Some),
|
Some(t) => self.check_cache_userpassword(t.uuid, cred).await.map(Some),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientError::Http(StatusCode::UNAUTHORIZED, reason, opid) => {
|
|
||||||
match reason {
|
Err(IdpError::ProviderUnauthorised) => {
|
||||||
Some(OperationError::NotAuthenticated) =>
|
|
||||||
warn!("session not authenticated - attempting reauthentication - eventid {}", opid),
|
|
||||||
Some(OperationError::SessionExpired) =>
|
|
||||||
warn!("session expired - attempting reauthentication - eventid {}", opid),
|
|
||||||
e =>
|
|
||||||
error!(
|
|
||||||
"authentication error {:?}, moving to offline - eventid {}", e, opid
|
|
||||||
),
|
|
||||||
};
|
|
||||||
// Something went wrong, mark offline to force a re-auth ASAP.
|
// Something went wrong, mark offline to force a re-auth ASAP.
|
||||||
let time = SystemTime::now().sub(Duration::from_secs(1));
|
let time = SystemTime::now().sub(Duration::from_secs(1));
|
||||||
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
self.set_cachestate(CacheState::OfflineNextCheck(time))
|
||||||
.await;
|
.await;
|
||||||
match token.as_ref() {
|
match token.as_ref() {
|
||||||
Some(t) => self.check_cache_userpassword(&t.uuid, cred).await.map(Some),
|
Some(t) => self.check_cache_userpassword(t.uuid, cred).await.map(Some),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientError::Http(
|
Err(IdpError::NotFound) => Ok(None),
|
||||||
StatusCode::BAD_REQUEST,
|
Err(IdpError::BadRequest) => {
|
||||||
Some(OperationError::NoMatchingEntries),
|
|
||||||
opid,
|
|
||||||
)
|
|
||||||
| ClientError::Http(
|
|
||||||
StatusCode::NOT_FOUND,
|
|
||||||
Some(OperationError::NoMatchingEntries),
|
|
||||||
opid,
|
|
||||||
)
|
|
||||||
| ClientError::Http(
|
|
||||||
StatusCode::BAD_REQUEST,
|
|
||||||
Some(OperationError::InvalidAccountState(_)),
|
|
||||||
opid,
|
|
||||||
) => {
|
|
||||||
error!(
|
|
||||||
"unknown account or is not a valid posix account - eventid {}",
|
|
||||||
opid
|
|
||||||
);
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
er => {
|
|
||||||
error!("client error -> {:?}", er);
|
|
||||||
// Some other unknown processing error?
|
// Some other unknown processing error?
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn offline_account_authenticate(
|
async fn offline_account_authenticate(
|
||||||
&self,
|
&self,
|
||||||
token: &Option<UnixUserToken>,
|
token: &Option<UserToken>,
|
||||||
cred: &str,
|
cred: &str,
|
||||||
) -> Result<Option<bool>, ()> {
|
) -> Result<Option<bool>, ()> {
|
||||||
debug!("Attempt offline password check");
|
debug!("Attempt offline password check");
|
||||||
match token.as_ref() {
|
match token.as_ref() {
|
||||||
Some(t) => {
|
Some(t) => {
|
||||||
if t.valid {
|
if t.valid {
|
||||||
self.check_cache_userpassword(&t.uuid, cred).await.map(Some)
|
self.check_cache_userpassword(t.uuid, cred).await.map(Some)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(false))
|
Ok(Some(false))
|
||||||
}
|
}
|
||||||
|
@ -942,7 +821,7 @@ impl Resolver {
|
||||||
let user_set: BTreeSet<_> = tok
|
let user_set: BTreeSet<_> = tok
|
||||||
.groups
|
.groups
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|g| [g.name.clone(), g.spn.clone(), g.uuid.clone()])
|
.flat_map(|g| [g.name.clone(), g.uuid.hyphenated().to_string()])
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -963,22 +842,18 @@ impl Resolver {
|
||||||
account_id: &str,
|
account_id: &str,
|
||||||
cred: &str,
|
cred: &str,
|
||||||
) -> Result<Option<bool>, ()> {
|
) -> Result<Option<bool>, ()> {
|
||||||
|
let id = Id::Name(account_id.to_string());
|
||||||
|
|
||||||
let state = self.get_cachestate().await;
|
let state = self.get_cachestate().await;
|
||||||
let (_expired, token) = self
|
let (_expired, token) = self.get_cached_usertoken(&id).await?;
|
||||||
.get_cached_usertoken(&Id::Name(account_id.to_string()))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
match state {
|
match state {
|
||||||
CacheState::Online => {
|
CacheState::Online => self.online_account_authenticate(&token, &id, cred).await,
|
||||||
self.online_account_authenticate(&token, account_id, cred)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
CacheState::OfflineNextCheck(_time) => {
|
CacheState::OfflineNextCheck(_time) => {
|
||||||
// Always attempt to go online to attempt the authentication.
|
// Always attempt to go online to attempt the authentication.
|
||||||
if self.test_connection().await {
|
if self.test_connection().await {
|
||||||
// Brought ourselves online, lets check.
|
// Brought ourselves online, lets check.
|
||||||
self.online_account_authenticate(&token, account_id, cred)
|
self.online_account_authenticate(&token, &id, cred).await
|
||||||
.await
|
|
||||||
} else {
|
} else {
|
||||||
// We are offline, check from the cache if possible.
|
// We are offline, check from the cache if possible.
|
||||||
self.offline_account_authenticate(&token, cred).await
|
self.offline_account_authenticate(&token, cred).await
|
||||||
|
@ -1014,8 +889,8 @@ impl Resolver {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
CacheState::OfflineNextCheck(_time) => {
|
CacheState::OfflineNextCheck(_time) => {
|
||||||
match self.client.write().await.auth_anonymous().await {
|
match self.client.provider_authenticate().await {
|
||||||
Ok(_uat) => {
|
Ok(()) => {
|
||||||
debug!("OfflineNextCheck -> authenticated");
|
debug!("OfflineNextCheck -> authenticated");
|
||||||
self.set_cachestate(CacheState::Online).await;
|
self.set_cachestate(CacheState::Online).await;
|
||||||
true
|
true
|
||||||
|
|
|
@ -65,7 +65,7 @@ async fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
let req = ClientRequest::SshKey(opt.account_id);
|
let req = ClientRequest::SshKey(opt.account_id);
|
||||||
|
|
||||||
match call_daemon(cfg.sock_path.as_str(), req).await {
|
match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await {
|
||||||
Ok(r) => match r {
|
Ok(r) => match r {
|
||||||
ClientResponse::SshKeys(sk) => sk.iter().for_each(|k| {
|
ClientResponse::SshKeys(sk) => sk.iter().for_each(|k| {
|
||||||
println!("{}", k);
|
println!("{}", k);
|
||||||
|
|
|
@ -17,7 +17,6 @@ use std::process::ExitCode;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use kanidm_unix_common::client::call_daemon;
|
use kanidm_unix_common::client::call_daemon;
|
||||||
use kanidm_unix_common::client_sync::call_daemon_blocking;
|
|
||||||
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
use kanidm_unix_common::constants::DEFAULT_CONFIG_PATH;
|
||||||
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
use kanidm_unix_common::unix_config::KanidmUnixdConfig;
|
||||||
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse};
|
use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse};
|
||||||
|
@ -70,7 +69,7 @@ async fn main() -> ExitCode {
|
||||||
let req = ClientRequest::PamAuthenticate(account_id.clone(), password);
|
let req = ClientRequest::PamAuthenticate(account_id.clone(), password);
|
||||||
let sereq = ClientRequest::PamAccountAllowed(account_id);
|
let sereq = ClientRequest::PamAccountAllowed(account_id);
|
||||||
|
|
||||||
match call_daemon(cfg.sock_path.as_str(), req).await {
|
match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await {
|
||||||
Ok(r) => match r {
|
Ok(r) => match r {
|
||||||
ClientResponse::PamStatus(Some(true)) => {
|
ClientResponse::PamStatus(Some(true)) => {
|
||||||
println!("auth success!");
|
println!("auth success!");
|
||||||
|
@ -91,7 +90,7 @@ async fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match call_daemon(cfg.sock_path.as_str(), sereq).await {
|
match call_daemon(cfg.sock_path.as_str(), sereq, cfg.unix_sock_timeout).await {
|
||||||
Ok(r) => match r {
|
Ok(r) => match r {
|
||||||
ClientResponse::PamStatus(Some(true)) => {
|
ClientResponse::PamStatus(Some(true)) => {
|
||||||
println!("account success!");
|
println!("account success!");
|
||||||
|
@ -133,7 +132,7 @@ async fn main() -> ExitCode {
|
||||||
|
|
||||||
let req = ClientRequest::ClearCache;
|
let req = ClientRequest::ClearCache;
|
||||||
|
|
||||||
match call_daemon(cfg.sock_path.as_str(), req).await {
|
match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await {
|
||||||
Ok(r) => match r {
|
Ok(r) => match r {
|
||||||
ClientResponse::Ok => info!("success"),
|
ClientResponse::Ok => info!("success"),
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -162,7 +161,7 @@ async fn main() -> ExitCode {
|
||||||
|
|
||||||
let req = ClientRequest::InvalidateCache;
|
let req = ClientRequest::InvalidateCache;
|
||||||
|
|
||||||
match call_daemon(cfg.sock_path.as_str(), req).await {
|
match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await {
|
||||||
Ok(r) => match r {
|
Ok(r) => match r {
|
||||||
ClientResponse::Ok => info!("success"),
|
ClientResponse::Ok => info!("success"),
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -198,7 +197,7 @@ async fn main() -> ExitCode {
|
||||||
cfg.sock_path
|
cfg.sock_path
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
match call_daemon_blocking(cfg.sock_path.as_str(), &req, cfg.unix_sock_timeout) {
|
match call_daemon(cfg.sock_path.as_str(), req, cfg.unix_sock_timeout).await {
|
||||||
Ok(r) => match r {
|
Ok(r) => match r {
|
||||||
ClientResponse::Ok => println!("working!"),
|
ClientResponse::Ok => println!("working!"),
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -11,7 +11,9 @@ use kanidm_unix_common::constants::{
|
||||||
DEFAULT_SHELL, DEFAULT_UID_ATTR_MAP,
|
DEFAULT_SHELL, DEFAULT_UID_ATTR_MAP,
|
||||||
};
|
};
|
||||||
use kanidm_unix_common::db::Db;
|
use kanidm_unix_common::db::Db;
|
||||||
use kanidm_unix_common::resolver::{Id, Resolver};
|
use kanidm_unix_common::idprovider::interface::Id;
|
||||||
|
use kanidm_unix_common::idprovider::kanidm::KanidmProvider;
|
||||||
|
use kanidm_unix_common::resolver::Resolver;
|
||||||
use kanidm_unix_common::unix_config::TpmPolicy;
|
use kanidm_unix_common::unix_config::TpmPolicy;
|
||||||
use kanidmd_core::config::{Configuration, IntegrationTestConfig, ServerRole};
|
use kanidmd_core::config::{Configuration, IntegrationTestConfig, ServerRole};
|
||||||
use kanidmd_core::create_server_core;
|
use kanidmd_core::create_server_core;
|
||||||
|
@ -42,7 +44,7 @@ where
|
||||||
Box::new(move |n| Box::pin(f(n)))
|
Box::new(move |n| Box::pin(f(n)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_test(fix_fn: Fixture) -> (Resolver, KanidmClient) {
|
async fn setup_test(fix_fn: Fixture) -> (Resolver<KanidmProvider>, KanidmClient) {
|
||||||
sketching::test_init();
|
sketching::test_init();
|
||||||
|
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
|
@ -100,6 +102,8 @@ async fn setup_test(fix_fn: Fixture) -> (Resolver, KanidmClient) {
|
||||||
.build()
|
.build()
|
||||||
.expect("Failed to build client");
|
.expect("Failed to build client");
|
||||||
|
|
||||||
|
let idprovider = KanidmProvider::new(rsclient);
|
||||||
|
|
||||||
let db = Db::new(
|
let db = Db::new(
|
||||||
"", // The sqlite db path, this is in memory.
|
"", // The sqlite db path, this is in memory.
|
||||||
&TpmPolicy::default(),
|
&TpmPolicy::default(),
|
||||||
|
@ -108,8 +112,8 @@ async fn setup_test(fix_fn: Fixture) -> (Resolver, KanidmClient) {
|
||||||
|
|
||||||
let cachelayer = Resolver::new(
|
let cachelayer = Resolver::new(
|
||||||
db,
|
db,
|
||||||
|
idprovider,
|
||||||
300,
|
300,
|
||||||
rsclient,
|
|
||||||
vec!["allowed_group".to_string()],
|
vec!["allowed_group".to_string()],
|
||||||
DEFAULT_SHELL.to_string(),
|
DEFAULT_SHELL.to_string(),
|
||||||
DEFAULT_HOME_PREFIX.to_string(),
|
DEFAULT_HOME_PREFIX.to_string(),
|
||||||
|
|
Loading…
Reference in a new issue