use crate::entry::{Entry, EntryCommitted, EntrySealed};
use kanidm_proto::v1::OperationError;
use kanidm_proto::v1::UserAuthToken;
use crate::audit::AuditScope;
use crate::constants::UUID_ANONYMOUS;
use crate::credential::totp::TOTP;
use crate::credential::Credential;
use crate::idm::claim::Claim;
use crate::idm::group::Group;
use crate::modify::{ModifyInvalid, ModifyList};
use crate::server::{QueryServerReadTransaction, QueryServerWriteTransaction};
use crate::value::{PartialValue, Value};
use uuid::Uuid;
lazy_static! {
static ref PVCLASS_ACCOUNT: PartialValue = PartialValue::new_class("account");
}
macro_rules! try_from_entry {
($value:expr, $groups:expr) => {{
if !$value.attribute_value_pres("class", &PVCLASS_ACCOUNT) {
return Err(OperationError::InvalidAccountState(
"Missing class: account".to_string(),
));
}
let name = $value
.get_ava_single_str("name")
.map(|s| s.to_string())
.ok_or(OperationError::InvalidAccountState(
"Missing attribute: name".to_string(),
))?;
let displayname = $value
.get_ava_single_str("displayname")
.map(|s| s.to_string())
.ok_or(OperationError::InvalidAccountState(
"Missing attribute: displayname".to_string(),
))?;
let primary = $value
.get_ava_single_credential("primary_credential")
.map(|v| v.clone());
let spn = $value
.get_ava_single("spn")
.map(|s| {
debug_assert!(s.is_spn());
s.to_proto_string_clone()
})
.ok_or(OperationError::InvalidAccountState(
"Missing attribute: spn".to_string(),
))?;
let groups = $groups;
let uuid = $value.get_uuid().clone();
Ok(Account {
uuid: uuid,
name: name,
displayname: displayname,
groups: groups,
primary: primary,
spn: spn,
})
}};
}
#[derive(Debug, Clone)]
pub(crate) struct Account {
pub name: String,
pub displayname: String,
pub uuid: Uuid,
pub groups: Vec<Group>,
pub primary: Option<Credential>,
pub spn: String,
}
impl Account {
pub(crate) fn try_from_entry_ro(
au: &mut AuditScope,
value: Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerReadTransaction,
) -> Result<Self, OperationError> {
lperf_trace_segment!(au, "idm::account::try_from_entry_ro", || {
let groups = Group::try_from_account_entry_ro(au, &value, qs)?;
try_from_entry!(value, groups)
})
}
pub(crate) fn try_from_entry_rw(
au: &mut AuditScope,
value: Entry<EntrySealed, EntryCommitted>,
qs: &mut QueryServerWriteTransaction,
) -> Result<Self, OperationError> {
let groups = Group::try_from_account_entry_rw(au, &value, qs)?;
try_from_entry!(value, groups)
}
#[cfg(test)]
pub(crate) fn try_from_entry_no_groups(
value: Entry<EntrySealed, EntryCommitted>,
) -> Result<Self, OperationError> {
try_from_entry!(value, vec![])
}
pub(crate) fn to_userauthtoken(&self, claims: Vec<Claim>) -> Option<UserAuthToken> {
Some(UserAuthToken {
name: self.name.clone(),
spn: self.spn.clone(),
displayname: self.name.clone(),
uuid: self.uuid.to_hyphenated_ref().to_string(),
groups: self.groups.iter().map(|g| g.to_proto()).collect(),
claims: claims.iter().map(|c| c.to_proto()).collect(),
})
}
pub fn is_anonymous(&self) -> bool {
self.uuid == *UUID_ANONYMOUS
}
pub(crate) fn gen_password_mod(
&self,
cleartext: &str,
appid: &Option<String>,
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
match appid {
Some(_) => Err(OperationError::InvalidState),
None => {
match &self.primary {
Some(primary) => {
let ncred = primary.set_password(cleartext);
let vcred = Value::new_credential("primary", ncred);
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
}
None => {
let ncred = Credential::new_password_only(cleartext);
let vcred = Value::new_credential("primary", ncred);
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
}
}
} }
}
pub(crate) fn gen_totp_mod(
&self,
token: TOTP,
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
match &self.primary {
Some(primary) => {
let ncred = primary.update_totp(token);
let vcred = Value::new_credential("primary", ncred);
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
}
None => {
Err(OperationError::InvalidState)
}
}
}
pub(crate) fn regenerate_radius_secret_mod(
&self,
cleartext: &str,
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
let vcred = Value::new_radius_str(cleartext);
Ok(ModifyList::new_purge_and_set("radius_secret", vcred))
}
}
#[cfg(test)]
mod tests {
use crate::constants::JSON_ANONYMOUS_V1;
#[test]
fn test_idm_account_from_anonymous() {
let anon_e = entry_str_to_account!(JSON_ANONYMOUS_V1);
debug!("{:?}", anon_e);
}
#[test]
fn test_idm_account_from_real() {
}
#[test]
fn test_idm_account_set_credential() {
}
}