mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
64 120 session claims (#462)
This commit is contained in:
parent
033b977906
commit
807af81184
|
@ -901,9 +901,8 @@ impl KanidmAsyncClient {
|
|||
pub async fn idm_account_primary_credential_generate_totp(
|
||||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<(Uuid, TotpSecret), ClientError> {
|
||||
let r = SetCredentialRequest::TotpGenerate(label.to_string());
|
||||
let r = SetCredentialRequest::TotpGenerate;
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
|
|
|
@ -605,11 +605,10 @@ impl KanidmClient {
|
|||
pub fn idm_account_primary_credential_generate_totp(
|
||||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<(Uuid, TotpSecret), ClientError> {
|
||||
tokio_block_on(
|
||||
self.asclient
|
||||
.idm_account_primary_credential_generate_totp(id, label),
|
||||
.idm_account_primary_credential_generate_totp(id),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ static DEFAULT_NOT_HP_GROUP_NAMES: [&str; 2] =
|
|||
["idm_account_unix_extend_priv", "idm_group_unix_extend_priv"];
|
||||
|
||||
fn create_user(rsclient: &KanidmClient, id: &str, group_name: &str) -> () {
|
||||
rsclient.idm_account_create(id, "Deeeeemo").unwrap();
|
||||
rsclient.idm_account_create(id, id).unwrap();
|
||||
|
||||
// Create group and add to user to test read attr: member_of
|
||||
let _ = match rsclient.idm_group_get(&group_name).unwrap() {
|
||||
|
|
|
@ -756,7 +756,7 @@ fn test_server_rest_totp_auth_lifecycle() {
|
|||
.idm_account_primary_credential_set_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
||||
.is_ok());
|
||||
let (sessionid, tok) = rsclient
|
||||
.idm_account_primary_credential_generate_totp("demo_account", "demo")
|
||||
.idm_account_primary_credential_generate_totp("demo_account")
|
||||
.unwrap();
|
||||
|
||||
let r_tok: Totp = tok.into();
|
||||
|
|
|
@ -150,18 +150,33 @@ pub struct Application {
|
|||
}
|
||||
*/
|
||||
|
||||
// The currently authenticated user, and any required metadata for them
|
||||
// to properly authorise them. This is similar in nature to oauth and the krb
|
||||
// PAC/PAD structures. Currently we only use this internally, but we should
|
||||
// consider making it "parseable" by the client so they can have per-session
|
||||
// group/authorisation data.
|
||||
//
|
||||
// This structure and how it works will *very much* change over time from this
|
||||
// point onward!
|
||||
//
|
||||
// It's likely that this must have a relationship to the server's user structure
|
||||
// and to the Entry so that filters or access controls can be applied.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum AuthType {
|
||||
Anonymous,
|
||||
UnixPassword,
|
||||
Password,
|
||||
GeneratedPassword,
|
||||
Webauthn,
|
||||
PasswordMfa,
|
||||
// PasswordWebauthn,
|
||||
// WebauthnVerified,
|
||||
// PasswordWebauthnVerified,
|
||||
}
|
||||
|
||||
/// The currently authenticated user, and any required metadata for them
|
||||
/// to properly authorise them. This is similar in nature to oauth and the krb
|
||||
/// PAC/PAD structures. Currently we only use this internally, but we should
|
||||
/// consider making it "parseable" by the client so they can have per-session
|
||||
/// group/authorisation data.
|
||||
///
|
||||
/// This structure and how it works will *very much* change over time from this
|
||||
/// point onward!
|
||||
///
|
||||
/// It's likely that this must have a relationship to the server's user structure
|
||||
/// and to the Entry so that filters or access controls can be applied.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub struct UserAuthToken {
|
||||
pub session_id: Uuid,
|
||||
// When this data should be considered invalid. Interpretation
|
||||
|
@ -175,6 +190,7 @@ pub struct UserAuthToken {
|
|||
// pub application: Option<Application>,
|
||||
pub groups: Vec<Group>,
|
||||
pub claims: Vec<Claim>,
|
||||
pub auth_type: AuthType,
|
||||
// Should we allow supplemental ava's to be added on request?
|
||||
pub lim_uidx: bool,
|
||||
pub lim_rmax: usize,
|
||||
|
@ -649,7 +665,7 @@ pub struct AuthResponse {
|
|||
pub enum SetCredentialRequest {
|
||||
Password(String),
|
||||
GeneratePassword,
|
||||
TotpGenerate(String),
|
||||
TotpGenerate,
|
||||
TotpVerify(Uuid, u32),
|
||||
TotpRemove,
|
||||
// Start the rego.
|
||||
|
|
|
@ -146,7 +146,6 @@ impl AccountOpt {
|
|||
let client = acsopt.copt.to_client();
|
||||
let (session, tok) = match client.idm_account_primary_credential_generate_totp(
|
||||
acsopt.aopts.account_id.as_str(),
|
||||
acsopt.tag.as_str(),
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
|
|
|
@ -158,24 +158,29 @@ pub struct AccountCreateOpt {
|
|||
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub enum AccountCredential {
|
||||
/// Set this accounts password
|
||||
#[structopt(name = "set_password")]
|
||||
SetPassword(AccountCredentialSet),
|
||||
#[structopt(name = "generate_password")]
|
||||
GeneratePassword(AccountCredentialSet),
|
||||
/// Register a new webauthn device to this account.
|
||||
#[structopt(name = "register_webauthn")]
|
||||
RegisterWebauthn(AccountNamedTagOpt),
|
||||
/// Remove a webauthn device from this account
|
||||
#[structopt(name = "remove_webauthn")]
|
||||
RemoveWebauthn(AccountNamedTagOpt),
|
||||
/// Set the TOTP credential of the account. If a TOTP already exists, on a successful
|
||||
/// registration, this will replace it.
|
||||
#[structopt(name = "set_totp")]
|
||||
RegisterTotp(AccountNamedTagOpt),
|
||||
#[structopt(name = "register_totp")]
|
||||
RegisterTotp(AccountNamedOpt),
|
||||
/// Remove TOTP from the account. If no TOTP exists, no action is taken.
|
||||
#[structopt(name = "remove_totp")]
|
||||
RemoveTotp(AccountNamedOpt),
|
||||
/// Show the status of the accounts credentials.
|
||||
#[structopt(name = "status")]
|
||||
Status(AccountNamedOpt),
|
||||
/// Reset the accounts credentials, removing all TOTP, Webauthn, Passwords,
|
||||
/// and generate a new strong random password.
|
||||
#[structopt(name = "reset_credential")]
|
||||
GeneratePassword(AccountCredentialSet),
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
|
|
|
@ -82,7 +82,7 @@ impl QueryServerReadV1 {
|
|||
let res = lperf_op_segment!(&mut audit, "actors::v1_read::handle<SearchMessage>", || {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -185,7 +185,7 @@ impl QueryServerReadV1 {
|
|||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| {
|
||||
idms_prox_read
|
||||
.process_uat_to_identity(&mut audit, &uat)
|
||||
.process_uat_to_identity(&mut audit, &uat, ct)
|
||||
.map(|i| (uat, i))
|
||||
})
|
||||
.map_err(|e| {
|
||||
|
@ -252,7 +252,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -305,7 +305,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -356,7 +356,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -429,7 +429,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -486,7 +486,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -545,7 +545,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -600,7 +600,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -673,7 +673,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -749,7 +749,7 @@ impl QueryServerReadV1 {
|
|||
// resolve the id
|
||||
let ident = idm_auth
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idm_auth.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idm_auth.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -804,7 +804,7 @@ impl QueryServerReadV1 {
|
|||
|| {
|
||||
let ident = idms_prox_read
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_read.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
|
|
@ -73,7 +73,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -126,7 +126,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -180,7 +180,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -234,7 +234,7 @@ impl QueryServerWriteV1 {
|
|||
let ct = duration_from_epoch_now();
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(&mut audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -287,7 +287,7 @@ impl QueryServerWriteV1 {
|
|||
let ct = duration_from_epoch_now();
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -339,7 +339,7 @@ impl QueryServerWriteV1 {
|
|||
let ct = duration_from_epoch_now();
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -387,7 +387,7 @@ impl QueryServerWriteV1 {
|
|||
let ct = duration_from_epoch_now();
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -425,7 +425,6 @@ impl QueryServerWriteV1 {
|
|||
&self,
|
||||
uat: Option<String>,
|
||||
uuid_or_name: String,
|
||||
appid: Option<String>,
|
||||
sac: SetCredentialRequest,
|
||||
eventid: Uuid,
|
||||
) -> Result<SetCredentialResponse, OperationError> {
|
||||
|
@ -443,7 +442,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -471,7 +470,6 @@ impl QueryServerWriteV1 {
|
|||
ident,
|
||||
target_uuid,
|
||||
cleartext,
|
||||
appid,
|
||||
)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(
|
||||
|
@ -492,7 +490,6 @@ impl QueryServerWriteV1 {
|
|||
// &idms_prox_write.qs_write,
|
||||
ident,
|
||||
target_uuid,
|
||||
appid,
|
||||
)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(
|
||||
|
@ -507,13 +504,12 @@ impl QueryServerWriteV1 {
|
|||
.and_then(|r| idms_prox_write.commit(&mut audit).map(|_| r))
|
||||
.map(SetCredentialResponse::Token)
|
||||
}
|
||||
SetCredentialRequest::TotpGenerate(label) => {
|
||||
SetCredentialRequest::TotpGenerate => {
|
||||
let gte = GenerateTotpEvent::from_parts(
|
||||
&mut audit,
|
||||
// &idms_prox_write.qs_write,
|
||||
ident,
|
||||
target_uuid,
|
||||
label,
|
||||
)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(
|
||||
|
@ -655,7 +651,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -700,7 +696,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -758,7 +754,7 @@ impl QueryServerWriteV1 {
|
|||
let ct = duration_from_epoch_now();
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -819,7 +815,7 @@ impl QueryServerWriteV1 {
|
|||
let ct = duration_from_epoch_now();
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
@ -1123,7 +1119,7 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let ident = idms_prox_write
|
||||
.validate_and_parse_uat(&mut audit, uat.as_deref(), ct)
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat))
|
||||
.and_then(|uat| idms_prox_write.process_uat_to_identity(&mut audit, &uat, ct))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
|
|
@ -388,10 +388,7 @@ async fn json_rest_event_delete_id_attr(
|
|||
}
|
||||
}
|
||||
|
||||
async fn json_rest_event_credential_put(
|
||||
mut req: tide::Request<AppState>,
|
||||
appid: Option<String>,
|
||||
) -> tide::Result {
|
||||
async fn json_rest_event_credential_put(mut req: tide::Request<AppState>) -> tide::Result {
|
||||
let uat = req.get_current_uat();
|
||||
let uuid_or_name = req.get_url_param("id")?;
|
||||
let sac: SetCredentialRequest = req.body_json().await?;
|
||||
|
@ -400,7 +397,7 @@ async fn json_rest_event_credential_put(
|
|||
let res = req
|
||||
.state()
|
||||
.qe_w_ref
|
||||
.handle_credentialset(uat, uuid_or_name, appid, sac, eventid)
|
||||
.handle_credentialset(uat, uuid_or_name, sac, eventid)
|
||||
.await;
|
||||
to_tide_response(res, hvalue)
|
||||
}
|
||||
|
@ -541,7 +538,7 @@ pub async fn account_id_delete(req: tide::Request<AppState>) -> tide::Result {
|
|||
}
|
||||
|
||||
pub async fn account_put_id_credential_primary(req: tide::Request<AppState>) -> tide::Result {
|
||||
json_rest_event_credential_put(req, None).await
|
||||
json_rest_event_credential_put(req).await
|
||||
}
|
||||
|
||||
pub async fn account_get_id_credential_status(req: tide::Request<AppState>) -> tide::Result {
|
||||
|
|
|
@ -390,7 +390,7 @@ pub fn verify_server_core(config: &Configuration) {
|
|||
// Now add IDM server verifications?
|
||||
}
|
||||
|
||||
pub fn recover_account_core(config: &Configuration, name: &str, password: &str) {
|
||||
pub fn recover_account_core(config: &Configuration, name: &str) {
|
||||
let mut audit = AuditScope::new("recover_account", uuid::Uuid::new_v4(), config.log_level);
|
||||
|
||||
let schema = match Schema::new(&mut audit) {
|
||||
|
@ -421,11 +421,11 @@ pub fn recover_account_core(config: &Configuration, name: &str, password: &str)
|
|||
|
||||
// Run the password change.
|
||||
let mut idms_prox_write = task::block_on(idms.proxy_write_async(duration_from_epoch_now()));
|
||||
match idms_prox_write.recover_account(&mut audit, &name, &password) {
|
||||
Ok(_) => match idms_prox_write.commit(&mut audit) {
|
||||
match idms_prox_write.recover_account(&mut audit, &name, None) {
|
||||
Ok(new_pw) => match idms_prox_write.commit(&mut audit) {
|
||||
Ok(()) => {
|
||||
audit.write_log();
|
||||
info!("Password reset!");
|
||||
info!("Password reset to -> {}", new_pw);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("A critical error during commit occured {:?}", e);
|
||||
|
@ -515,7 +515,7 @@ pub async fn create_server_core(config: Configuration) -> Result<(), ()> {
|
|||
Some(itc) => {
|
||||
let mut idms_prox_write =
|
||||
task::block_on(idms.proxy_write_async(duration_from_epoch_now()));
|
||||
match idms_prox_write.recover_account(&mut audit, "admin", &itc.admin_password) {
|
||||
match idms_prox_write.recover_account(&mut audit, "admin", Some(&itc.admin_password)) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
audit.write_log();
|
||||
|
|
|
@ -341,6 +341,13 @@ impl Credential {
|
|||
Password::new(policy, cleartext).map(Self::new_from_password)
|
||||
}
|
||||
|
||||
pub fn new_generatedpassword_only(
|
||||
policy: &CryptoPolicy,
|
||||
cleartext: &str,
|
||||
) -> Result<Self, OperationError> {
|
||||
Password::new(policy, cleartext).map(Self::new_from_generatedpassword)
|
||||
}
|
||||
|
||||
pub fn new_webauthn_only(label: String, cred: WebauthnCredential) -> Self {
|
||||
let mut webauthn_map = Map::new();
|
||||
webauthn_map.insert(label, cred);
|
||||
|
@ -593,8 +600,9 @@ impl Credential {
|
|||
|
||||
pub(crate) fn update_password(&self, pw: Password) -> Self {
|
||||
let type_ = match &self.type_ {
|
||||
CredentialType::Password(_) => CredentialType::Password(pw),
|
||||
CredentialType::GeneratedPassword(_) => CredentialType::GeneratedPassword(pw),
|
||||
CredentialType::Password(_) | CredentialType::GeneratedPassword(_) => {
|
||||
CredentialType::Password(pw)
|
||||
}
|
||||
CredentialType::PasswordMfa(_, totp, wan) => {
|
||||
CredentialType::PasswordMfa(pw, totp.clone(), wan.clone())
|
||||
}
|
||||
|
@ -646,6 +654,14 @@ impl Credential {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_from_generatedpassword(pw: Password) -> Self {
|
||||
Credential {
|
||||
type_: CredentialType::GeneratedPassword(pw),
|
||||
claims: Vec::new(),
|
||||
uuid: Uuid::new_v4(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_from_password(pw: Password) -> Self {
|
||||
Credential {
|
||||
type_: CredentialType::Password(pw),
|
||||
|
|
|
@ -60,7 +60,6 @@ impl TotpAlgo {
|
|||
/// https://tools.ietf.org/html/rfc6238 which relies on https://tools.ietf.org/html/rfc4226
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Totp {
|
||||
label: String,
|
||||
secret: Vec<u8>,
|
||||
pub(crate) step: u64,
|
||||
algo: TotpAlgo,
|
||||
|
@ -76,7 +75,6 @@ impl TryFrom<DbTotpV1> for Totp {
|
|||
DbTotpAlgoV1::S512 => TotpAlgo::Sha512,
|
||||
};
|
||||
Ok(Totp {
|
||||
label: value.l,
|
||||
secret: value.k,
|
||||
step: value.s,
|
||||
algo,
|
||||
|
@ -87,7 +85,6 @@ impl TryFrom<DbTotpV1> for Totp {
|
|||
impl From<ProtoTotp> for Totp {
|
||||
fn from(value: ProtoTotp) -> Self {
|
||||
Totp {
|
||||
label: "test_token".to_string(),
|
||||
secret: value.secret,
|
||||
algo: match value.algo {
|
||||
ProtoTotpAlgo::Sha1 => TotpAlgo::Sha1,
|
||||
|
@ -100,31 +97,21 @@ impl From<ProtoTotp> for Totp {
|
|||
}
|
||||
|
||||
impl Totp {
|
||||
pub fn new(label: String, secret: Vec<u8>, step: u64, algo: TotpAlgo) -> Self {
|
||||
Totp {
|
||||
label,
|
||||
secret,
|
||||
step,
|
||||
algo,
|
||||
}
|
||||
pub fn new(secret: Vec<u8>, step: u64, algo: TotpAlgo) -> Self {
|
||||
Totp { secret, step, algo }
|
||||
}
|
||||
|
||||
// Create a new token with secure key and algo.
|
||||
pub fn generate_secure(label: String, step: u64) -> Self {
|
||||
pub fn generate_secure(step: u64) -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
let secret: Vec<u8> = (0..SECRET_SIZE_BYTES).map(|_| rng.gen()).collect();
|
||||
let algo = TotpAlgo::Sha512;
|
||||
Totp {
|
||||
label,
|
||||
secret,
|
||||
step,
|
||||
algo,
|
||||
}
|
||||
Totp { secret, step, algo }
|
||||
}
|
||||
|
||||
pub(crate) fn to_dbtotpv1(&self) -> DbTotpV1 {
|
||||
DbTotpV1 {
|
||||
l: self.label.clone(),
|
||||
l: "totp".to_string(),
|
||||
k: self.secret.clone(),
|
||||
s: self.step,
|
||||
a: match self.algo {
|
||||
|
@ -204,16 +191,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hotp_basic() {
|
||||
let otp_sha1 = Totp::new("".to_string(), vec![0], 30, TotpAlgo::Sha1);
|
||||
let otp_sha1 = Totp::new(vec![0], 30, TotpAlgo::Sha1);
|
||||
assert!(otp_sha1.digest(0) == Ok(328482));
|
||||
let otp_sha256 = Totp::new("".to_string(), vec![0], 30, TotpAlgo::Sha256);
|
||||
let otp_sha256 = Totp::new(vec![0], 30, TotpAlgo::Sha256);
|
||||
assert!(otp_sha256.digest(0) == Ok(356306));
|
||||
let otp_sha512 = Totp::new("".to_string(), vec![0], 30, TotpAlgo::Sha512);
|
||||
let otp_sha512 = Totp::new(vec![0], 30, TotpAlgo::Sha512);
|
||||
assert!(otp_sha512.digest(0) == Ok(674061));
|
||||
}
|
||||
|
||||
fn do_test(key: Vec<u8>, algo: TotpAlgo, secs: u64, step: u64, expect: Result<u32, TotpError>) {
|
||||
let otp = Totp::new("".to_string(), key.clone(), step, algo.clone());
|
||||
let otp = Totp::new(key.clone(), step, algo.clone());
|
||||
let d = Duration::from_secs(secs);
|
||||
let r = otp.do_totp_duration_from_epoch(&d);
|
||||
debug!(
|
||||
|
@ -281,12 +268,7 @@ mod tests {
|
|||
fn totp_allow_one_previous() {
|
||||
let key = vec![0x00, 0xaa, 0xbb, 0xcc];
|
||||
let secs = 1585369780;
|
||||
let otp = Totp::new(
|
||||
"".to_string(),
|
||||
key.clone(),
|
||||
TOTP_DEFAULT_STEP,
|
||||
TotpAlgo::Sha512,
|
||||
);
|
||||
let otp = Totp::new(key.clone(), TOTP_DEFAULT_STEP, TotpAlgo::Sha512);
|
||||
let d = Duration::from_secs(secs);
|
||||
// Step
|
||||
assert!(otp.verify(952181, &d));
|
||||
|
|
|
@ -867,6 +867,10 @@ impl Entry<EntrySealed, EntryCommitted> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn insert_claim(&mut self, value: &str) {
|
||||
self.add_ava_int("claim", Value::new_iutf8(value));
|
||||
}
|
||||
|
||||
pub fn compare(&self, rhs: &Entry<EntrySealed, EntryCommitted>) -> bool {
|
||||
compare_attrs(&self.attrs, &rhs.attrs)
|
||||
}
|
||||
|
|
|
@ -133,4 +133,14 @@ impl Identity {
|
|||
pub fn get_event_origin_id(&self) -> IdentityId {
|
||||
IdentityId::from(&self.origin)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn has_claim(&self, claim: &str) -> bool {
|
||||
match &self.origin {
|
||||
IdentType::Internal => false,
|
||||
IdentType::User(u) => u
|
||||
.entry
|
||||
.attribute_equality("claim", &PartialValue::new_iutf8(claim)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,12 @@ use crate::prelude::*;
|
|||
|
||||
use kanidm_proto::v1::CredentialStatus;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::UserAuthToken;
|
||||
use kanidm_proto::v1::{AuthType, UserAuthToken};
|
||||
|
||||
use crate::constants::UUID_ANONYMOUS;
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::totp::Totp;
|
||||
use crate::credential::{softlock::CredSoftLockPolicy, Credential};
|
||||
use crate::idm::claim::Claim;
|
||||
use crate::idm::group::Group;
|
||||
use crate::modify::{ModifyInvalid, ModifyList};
|
||||
use crate::value::{PartialValue, Value};
|
||||
|
@ -155,12 +154,15 @@ impl Account {
|
|||
try_from_entry!(value, vec![])
|
||||
}
|
||||
|
||||
// Could this actually take a claims list and application instead?
|
||||
/// Given the session_id and other metadata, create a user authentication token
|
||||
/// that represents a users session. Since this metadata can vary from session
|
||||
/// to session, this userauthtoken may contain some data (claims) that may yield
|
||||
/// different privileges to the bearer.
|
||||
pub(crate) fn to_userauthtoken(
|
||||
&self,
|
||||
session_id: Uuid,
|
||||
claims: &[Claim],
|
||||
ct: Duration,
|
||||
auth_type: AuthType,
|
||||
) -> Option<UserAuthToken> {
|
||||
// This could consume self?
|
||||
// The cred handler provided is what authenticated this user, so we can use it to
|
||||
|
@ -179,7 +181,9 @@ impl Account {
|
|||
uuid: self.uuid,
|
||||
// application: None,
|
||||
groups: self.groups.iter().map(|g| g.to_proto()).collect(),
|
||||
claims: claims.iter().map(|c| c.to_proto()).collect(),
|
||||
// claims: claims.iter().map(|c| c.to_proto()).collect(),
|
||||
claims: Vec::new(),
|
||||
auth_type,
|
||||
// What's the best way to get access to these limits with regard to claims/other?
|
||||
lim_uidx: false,
|
||||
lim_rmax: 128,
|
||||
|
@ -188,19 +192,23 @@ impl Account {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn is_within_valid_time(&self, ct: Duration) -> bool {
|
||||
pub fn check_within_valid_time(
|
||||
ct: Duration,
|
||||
valid_from: Option<&OffsetDateTime>,
|
||||
expire: Option<&OffsetDateTime>,
|
||||
) -> bool {
|
||||
let cot = OffsetDateTime::unix_epoch() + ct;
|
||||
|
||||
let vmin = if let Some(vft) = &self.valid_from {
|
||||
let vmin = if let Some(vft) = valid_from {
|
||||
// If current time greater than strat time window
|
||||
vft < &cot
|
||||
vft <= &cot
|
||||
} else {
|
||||
// We have no time, not expired.
|
||||
true
|
||||
};
|
||||
let vmax = if let Some(ext) = &self.expire {
|
||||
let vmax = if let Some(ext) = expire {
|
||||
// If exp greater than ct then expired.
|
||||
&cot < ext
|
||||
&cot <= ext
|
||||
} else {
|
||||
// If not present, we are not expired
|
||||
true
|
||||
|
@ -209,6 +217,10 @@ impl Account {
|
|||
vmin && vmax
|
||||
}
|
||||
|
||||
pub fn is_within_valid_time(&self, ct: Duration) -> bool {
|
||||
Self::check_within_valid_time(ct, self.valid_from.as_ref(), self.expire.as_ref())
|
||||
}
|
||||
|
||||
pub fn primary_cred_uuid(&self) -> Uuid {
|
||||
match &self.primary {
|
||||
Some(cred) => cred.uuid,
|
||||
|
@ -226,12 +238,12 @@ impl Account {
|
|||
self.uuid == *UUID_ANONYMOUS
|
||||
}
|
||||
|
||||
pub(crate) fn gen_password_recover_mod(
|
||||
pub(crate) fn gen_generatedpassword_recover_mod(
|
||||
&self,
|
||||
cleartext: &str,
|
||||
crypto_policy: &CryptoPolicy,
|
||||
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
|
||||
let ncred = Credential::new_password_only(crypto_policy, cleartext)?;
|
||||
let ncred = Credential::new_generatedpassword_only(crypto_policy, cleartext)?;
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
|
||||
}
|
||||
|
@ -239,16 +251,8 @@ impl Account {
|
|||
pub(crate) fn gen_password_mod(
|
||||
&self,
|
||||
cleartext: &str,
|
||||
appid: &Option<String>,
|
||||
crypto_policy: &CryptoPolicy,
|
||||
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
|
||||
// What should this look like? Probablf an appid + stuff -> modify?
|
||||
// then the caller has to apply the modify under the requests event
|
||||
// for proper auth checks.
|
||||
match appid {
|
||||
Some(_) => Err(OperationError::InvalidState),
|
||||
None => {
|
||||
// TODO #59: Enforce PW policy. Can we allow this change?
|
||||
match &self.primary {
|
||||
// Change the cred
|
||||
Some(primary) => {
|
||||
|
@ -263,8 +267,6 @@ impl Account {
|
|||
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
|
||||
}
|
||||
}
|
||||
} // no appid
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_totp_mod(
|
||||
|
@ -355,19 +357,11 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check_credential_pw(
|
||||
&self,
|
||||
cleartext: &str,
|
||||
appid: &Option<String>,
|
||||
) -> Result<bool, OperationError> {
|
||||
match appid {
|
||||
Some(_) => Err(OperationError::InvalidState),
|
||||
None => self
|
||||
.primary
|
||||
pub(crate) fn check_credential_pw(&self, cleartext: &str) -> Result<bool, OperationError> {
|
||||
self.primary
|
||||
.as_ref()
|
||||
.ok_or(OperationError::InvalidState)
|
||||
.and_then(|cred| cred.password_ref().and_then(|pw| pw.verify(cleartext))),
|
||||
}
|
||||
.and_then(|cred| cred.password_ref().and_then(|pw| pw.verify(cleartext)))
|
||||
}
|
||||
|
||||
pub(crate) fn regenerate_radius_secret_mod(
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::idm::account::Account;
|
||||
use crate::idm::claim::Claim;
|
||||
use crate::idm::AuthState;
|
||||
use crate::prelude::*;
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech};
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech, AuthType};
|
||||
|
||||
use crate::credential::{totp::Totp, Credential, CredentialType, Password};
|
||||
|
||||
|
@ -35,7 +34,7 @@ const ACCOUNT_EXPIRED: &str = "account expired";
|
|||
const PW_BADLIST_MSG: &str = "password is in badlist";
|
||||
|
||||
enum CredState {
|
||||
Success(Vec<Claim>),
|
||||
Success(AuthType),
|
||||
Continue(Vec<AuthAllowed>),
|
||||
Denied(&'static str),
|
||||
}
|
||||
|
@ -67,7 +66,7 @@ struct CredWebauthn {
|
|||
enum CredHandler {
|
||||
Anonymous,
|
||||
// AppPassword (?)
|
||||
Password(Password),
|
||||
Password(Password, bool),
|
||||
PasswordMfa(Box<CredMfa>),
|
||||
Webauthn(CredWebauthn),
|
||||
// Webauthn + Password
|
||||
|
@ -81,9 +80,8 @@ impl CredHandler {
|
|||
webauthn: &Webauthn<WebauthnDomainConfig>,
|
||||
) -> Result<Self, ()> {
|
||||
match &c.type_ {
|
||||
CredentialType::Password(pw) | CredentialType::GeneratedPassword(pw) => {
|
||||
Ok(CredHandler::Password(pw.clone()))
|
||||
}
|
||||
CredentialType::Password(pw) => Ok(CredHandler::Password(pw.clone(), false)),
|
||||
CredentialType::GeneratedPassword(pw) => Ok(CredHandler::Password(pw.clone(), true)),
|
||||
CredentialType::PasswordMfa(pw, maybe_totp, maybe_wan) => {
|
||||
let wan = if !maybe_wan.is_empty() {
|
||||
webauthn
|
||||
|
@ -152,7 +150,6 @@ impl CredHandler {
|
|||
if let Err(_e) = async_tx.send(DelayedAction::PwUpgrade(PasswordUpgrade {
|
||||
target_uuid: who,
|
||||
existing_password: cleartext.to_string(),
|
||||
appid: None,
|
||||
})) {
|
||||
ladmin_warning!(au, "unable to queue delayed pwupgrade, continuing ... ");
|
||||
};
|
||||
|
@ -164,7 +161,7 @@ impl CredHandler {
|
|||
AuthCredential::Anonymous => {
|
||||
// For anonymous, no claims will ever be issued.
|
||||
lsecurity!(au, "Handler::Anonymous -> Result::Success");
|
||||
CredState::Success(Vec::new())
|
||||
CredState::Success(AuthType::Anonymous)
|
||||
}
|
||||
_ => {
|
||||
lsecurity!(
|
||||
|
@ -180,6 +177,7 @@ impl CredHandler {
|
|||
au: &mut AuditScope,
|
||||
cred: &AuthCredential,
|
||||
pw: &mut Password,
|
||||
generated: bool,
|
||||
who: Uuid,
|
||||
async_tx: &Sender<DelayedAction>,
|
||||
pw_badlist_set: Option<&HashSet<String>>,
|
||||
|
@ -198,7 +196,11 @@ impl CredHandler {
|
|||
_ => {
|
||||
lsecurity!(au, "Handler::Password -> Result::Success");
|
||||
Self::maybe_pw_upgrade(au, pw, who, cleartext.as_str(), async_tx);
|
||||
CredState::Success(Vec::new())
|
||||
if generated {
|
||||
CredState::Success(AuthType::GeneratedPassword)
|
||||
} else {
|
||||
CredState::Success(AuthType::Password)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -312,7 +314,7 @@ impl CredHandler {
|
|||
cleartext.as_str(),
|
||||
async_tx,
|
||||
);
|
||||
CredState::Success(Vec::new())
|
||||
CredState::Success(AuthType::PasswordMfa)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -377,7 +379,7 @@ impl CredHandler {
|
|||
ladmin_warning!(au, "unable to queue delayed webauthn counter increment, continuing ... ");
|
||||
};
|
||||
};
|
||||
CredState::Success(Vec::new())
|
||||
CredState::Success(AuthType::Webauthn)
|
||||
})
|
||||
.unwrap_or_else(|e| {
|
||||
wan_cred.state = CredVerifyState::Fail;
|
||||
|
@ -409,8 +411,8 @@ impl CredHandler {
|
|||
) -> CredState {
|
||||
match self {
|
||||
CredHandler::Anonymous => Self::validate_anonymous(au, cred),
|
||||
CredHandler::Password(ref mut pw) => {
|
||||
Self::validate_password(au, cred, pw, who, async_tx, pw_badlist_set)
|
||||
CredHandler::Password(ref mut pw, generated) => {
|
||||
Self::validate_password(au, cred, pw, *generated, who, async_tx, pw_badlist_set)
|
||||
}
|
||||
CredHandler::PasswordMfa(ref mut pw_mfa) => Self::validate_password_mfa(
|
||||
au,
|
||||
|
@ -431,7 +433,7 @@ impl CredHandler {
|
|||
pub fn next_auth_allowed(&self) -> Vec<AuthAllowed> {
|
||||
match &self {
|
||||
CredHandler::Anonymous => vec![AuthAllowed::Anonymous],
|
||||
CredHandler::Password(_) => vec![AuthAllowed::Password],
|
||||
CredHandler::Password(_, _) => vec![AuthAllowed::Password],
|
||||
CredHandler::PasswordMfa(ref pw_mfa) => pw_mfa
|
||||
.totp
|
||||
.iter()
|
||||
|
@ -450,7 +452,7 @@ impl CredHandler {
|
|||
fn can_proceed(&self, mech: &AuthMech) -> bool {
|
||||
match (self, mech) {
|
||||
(CredHandler::Anonymous, AuthMech::Anonymous)
|
||||
| (CredHandler::Password(_), AuthMech::Password)
|
||||
| (CredHandler::Password(_, _), AuthMech::Password)
|
||||
| (CredHandler::PasswordMfa(_), AuthMech::PasswordMfa)
|
||||
| (CredHandler::Webauthn(_), AuthMech::Webauthn) => true,
|
||||
(_, _) => false,
|
||||
|
@ -460,7 +462,7 @@ impl CredHandler {
|
|||
fn allows_mech(&self) -> AuthMech {
|
||||
match self {
|
||||
CredHandler::Anonymous => AuthMech::Anonymous,
|
||||
CredHandler::Password(_) => AuthMech::Password,
|
||||
CredHandler::Password(_, _) => AuthMech::Password,
|
||||
CredHandler::PasswordMfa(_) => AuthMech::PasswordMfa,
|
||||
CredHandler::Webauthn(_) => AuthMech::Webauthn,
|
||||
}
|
||||
|
@ -508,7 +510,6 @@ impl AuthSession {
|
|||
pub fn new(
|
||||
au: &mut AuditScope,
|
||||
account: Account,
|
||||
_appid: &Option<String>,
|
||||
webauthn: &Webauthn<WebauthnDomainConfig>,
|
||||
ct: Duration,
|
||||
) -> (Option<Self>, AuthState) {
|
||||
|
@ -657,11 +658,11 @@ impl AuthSession {
|
|||
webauthn,
|
||||
pw_badlist_set,
|
||||
) {
|
||||
CredState::Success(claims) => {
|
||||
CredState::Success(auth_type) => {
|
||||
lsecurity!(au, "Successful cred handling");
|
||||
let uat = self
|
||||
.account
|
||||
.to_userauthtoken(au.uuid, &claims, *time)
|
||||
.to_userauthtoken(au.uuid, *time, auth_type)
|
||||
.ok_or(OperationError::InvalidState)?;
|
||||
|
||||
// Now encrypt and prepare the token for return to the client.
|
||||
|
@ -790,7 +791,6 @@ mod tests {
|
|||
let (session, state) = AuthSession::new(
|
||||
&mut audit,
|
||||
anon_account,
|
||||
&None,
|
||||
&webauthn,
|
||||
duration_from_epoch_now(),
|
||||
);
|
||||
|
@ -823,35 +823,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
// Deprecated, will remove later.
|
||||
#[test]
|
||||
fn test_idm_authsession_missing_appid() {
|
||||
let webauthn = create_webauthn();
|
||||
let anon_account = entry_str_to_account!(JSON_ANONYMOUS_V1);
|
||||
let mut audit = AuditScope::new(
|
||||
"test_idm_authsession_missing_appid",
|
||||
uuid::Uuid::new_v4(),
|
||||
None,
|
||||
);
|
||||
|
||||
let (session, state) = AuthSession::new(
|
||||
&mut audit,
|
||||
anon_account,
|
||||
&Some("NonExistantAppID".to_string()),
|
||||
&webauthn,
|
||||
duration_from_epoch_now(),
|
||||
);
|
||||
|
||||
// We now ignore appids.
|
||||
assert!(session.is_some());
|
||||
|
||||
if let AuthState::Choose(_) = state {
|
||||
// Pass
|
||||
} else {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! start_password_session {
|
||||
(
|
||||
$audit:expr,
|
||||
|
@ -861,7 +832,6 @@ mod tests {
|
|||
let (session, state) = AuthSession::new(
|
||||
$audit,
|
||||
$account.clone(),
|
||||
&None,
|
||||
$webauthn,
|
||||
duration_from_epoch_now(),
|
||||
);
|
||||
|
@ -1008,7 +978,6 @@ mod tests {
|
|||
let (session, state) = AuthSession::new(
|
||||
$audit,
|
||||
$account.clone(),
|
||||
&None,
|
||||
$webauthn,
|
||||
duration_from_epoch_now(),
|
||||
);
|
||||
|
@ -1067,7 +1036,7 @@ mod tests {
|
|||
let ts = Duration::from_secs(12345);
|
||||
|
||||
// manually load in a cred
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure(TOTP_DEFAULT_STEP);
|
||||
|
||||
let totp_good = totp
|
||||
.do_totp_duration_from_epoch(&ts)
|
||||
|
@ -1234,7 +1203,7 @@ mod tests {
|
|||
let ts = Duration::from_secs(12345);
|
||||
|
||||
// manually load in a cred
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure(TOTP_DEFAULT_STEP);
|
||||
|
||||
let totp_good = totp
|
||||
.do_totp_duration_from_epoch(&ts)
|
||||
|
@ -1301,7 +1270,6 @@ mod tests {
|
|||
let (session, state) = AuthSession::new(
|
||||
$audit,
|
||||
$account.clone(),
|
||||
&None,
|
||||
$webauthn,
|
||||
duration_from_epoch_now(),
|
||||
);
|
||||
|
@ -1706,7 +1674,7 @@ mod tests {
|
|||
let (webauthn, mut wa, wan_cred) = setup_webauthn(account.name.as_str());
|
||||
let hs512 = create_hs512();
|
||||
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure(TOTP_DEFAULT_STEP);
|
||||
let totp_good = totp
|
||||
.do_totp_duration_from_epoch(&ts)
|
||||
.expect("failed to perform totp.");
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
use kanidm_proto::v1::Claim as ProtoClaim;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Claim {
|
||||
// For now, empty. Later we'll flesh this out to uuid + name?
|
||||
}
|
||||
|
||||
impl Claim {
|
||||
/*
|
||||
pub fn new() -> Self {
|
||||
Claim {
|
||||
// Fill this in!
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn to_proto(&self) -> ProtoClaim {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ pub(crate) enum DelayedAction {
|
|||
pub(crate) struct PasswordUpgrade {
|
||||
pub target_uuid: Uuid,
|
||||
pub existing_password: String,
|
||||
pub appid: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) struct UnixPasswordUpgrade {
|
||||
|
|
|
@ -9,16 +9,14 @@ pub struct PasswordChangeEvent {
|
|||
pub ident: Identity,
|
||||
pub target: Uuid,
|
||||
pub cleartext: String,
|
||||
pub appid: Option<String>,
|
||||
}
|
||||
|
||||
impl PasswordChangeEvent {
|
||||
pub fn new_internal(target: &Uuid, cleartext: &str, appid: Option<&str>) -> Self {
|
||||
pub fn new_internal(target: &Uuid, cleartext: &str) -> Self {
|
||||
PasswordChangeEvent {
|
||||
ident: Identity::from_internal(),
|
||||
target: *target,
|
||||
cleartext: cleartext.to_string(),
|
||||
appid: appid.map(|v| v.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +32,6 @@ impl PasswordChangeEvent {
|
|||
ident,
|
||||
target: u,
|
||||
cleartext,
|
||||
appid: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -44,13 +41,11 @@ impl PasswordChangeEvent {
|
|||
ident: Identity,
|
||||
target: Uuid,
|
||||
cleartext: String,
|
||||
appid: Option<String>,
|
||||
) -> Result<Self, OperationError> {
|
||||
Ok(PasswordChangeEvent {
|
||||
ident,
|
||||
target,
|
||||
cleartext,
|
||||
appid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +85,6 @@ impl UnixPasswordChangeEvent {
|
|||
pub struct GeneratePasswordEvent {
|
||||
pub ident: Identity,
|
||||
pub target: Uuid,
|
||||
pub appid: Option<String>,
|
||||
}
|
||||
|
||||
impl GeneratePasswordEvent {
|
||||
|
@ -99,13 +93,8 @@ impl GeneratePasswordEvent {
|
|||
// qs: &QueryServerWriteTransaction,
|
||||
ident: Identity,
|
||||
target: Uuid,
|
||||
appid: Option<String>,
|
||||
) -> Result<Self, OperationError> {
|
||||
Ok(GeneratePasswordEvent {
|
||||
ident,
|
||||
target,
|
||||
appid,
|
||||
})
|
||||
Ok(GeneratePasswordEvent { ident, target })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +237,6 @@ impl UnixUserAuthEvent {
|
|||
pub struct GenerateTotpEvent {
|
||||
pub ident: Identity,
|
||||
pub target: Uuid,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl GenerateTotpEvent {
|
||||
|
@ -257,24 +245,15 @@ impl GenerateTotpEvent {
|
|||
// qs: &QueryServerWriteTransaction,
|
||||
ident: Identity,
|
||||
target: Uuid,
|
||||
label: String,
|
||||
) -> Result<Self, OperationError> {
|
||||
Ok(GenerateTotpEvent {
|
||||
ident,
|
||||
target,
|
||||
label,
|
||||
})
|
||||
Ok(GenerateTotpEvent { ident, target })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new_internal(target: Uuid) -> Self {
|
||||
let ident = Identity::from_internal();
|
||||
|
||||
GenerateTotpEvent {
|
||||
ident,
|
||||
target,
|
||||
label: "internal_token".to_string(),
|
||||
}
|
||||
GenerateTotpEvent { ident, target }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,10 @@ impl MfaRegSession {
|
|||
pub fn totp_new(
|
||||
origin: IdentityId,
|
||||
account: Account,
|
||||
label: String,
|
||||
) -> Result<(Self, MfaRegNext), OperationError> {
|
||||
// Based on the req, init our session, and the return the next step.
|
||||
// Store the ID of the event that start's the attempt
|
||||
let token = Totp::generate_secure(label, TOTP_DEFAULT_STEP);
|
||||
let token = Totp::generate_secure(TOTP_DEFAULT_STEP);
|
||||
|
||||
let accountname = account.name.as_str();
|
||||
let issuer = account.spn.as_str();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
pub(crate) mod account;
|
||||
pub(crate) mod authsession;
|
||||
pub(crate) mod claim;
|
||||
pub(crate) mod delayed;
|
||||
pub(crate) mod event;
|
||||
pub(crate) mod group;
|
||||
|
|
|
@ -26,8 +26,8 @@ use crate::idm::delayed::{
|
|||
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::v1::{
|
||||
CredentialStatus, RadiusAuthToken, SetCredentialResponse, UnixGroupToken, UnixUserToken,
|
||||
UserAuthToken,
|
||||
AuthType, CredentialStatus, RadiusAuthToken, SetCredentialResponse, UnixGroupToken,
|
||||
UnixUserToken, UserAuthToken,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -354,6 +354,7 @@ pub trait IdmServerTransaction<'a> {
|
|||
&self,
|
||||
audit: &mut AuditScope,
|
||||
uat: &UserAuthToken,
|
||||
ct: Duration,
|
||||
) -> Result<Identity, OperationError> {
|
||||
// From a UAT, get the current identity and associated information.
|
||||
let entry = self
|
||||
|
@ -364,11 +365,52 @@ pub trait IdmServerTransaction<'a> {
|
|||
e
|
||||
})?;
|
||||
|
||||
// TODO #64: Now apply claims from the uat into the Entry
|
||||
// to allow filtering.
|
||||
|
||||
// TODO #59: If the account is expiredy, do not allow the event
|
||||
// #59: If the account is expired, do not allow the event
|
||||
// to proceed
|
||||
let valid = Account::check_within_valid_time(
|
||||
ct,
|
||||
entry.get_ava_single_datetime("account_valid_from").as_ref(),
|
||||
entry.get_ava_single_datetime("account_expire").as_ref(),
|
||||
);
|
||||
|
||||
if !valid {
|
||||
lsecurity!(
|
||||
audit,
|
||||
"Account has expired or is not yet valid, not allowing to proceed"
|
||||
);
|
||||
return Err(OperationError::SessionExpired);
|
||||
}
|
||||
|
||||
// #64: Now apply claims from the uat into the Entry
|
||||
// to allow filtering.
|
||||
/*
|
||||
entry.insert_claim(match &uat.auth_type {
|
||||
AuthType::Anonymous => "authtype_anonymous",
|
||||
AuthType::UnixPassword => "authtype_unixpassword",
|
||||
AuthType::Password => "authtype_password",
|
||||
AuthType::GeneratedPassword => "authtype_generatedpassword",
|
||||
AuthType::Webauthn => "authtype_webauthn",
|
||||
AuthType::PasswordMfa => "authtype_passwordmfa",
|
||||
});
|
||||
|
||||
match &uat.auth_type {
|
||||
AuthType::Anonymous | AuthType::UnixPassword | AuthType::Password => {}
|
||||
AuthType::GeneratedPassword | AuthType::Webauthn | AuthType::PasswordMfa => {
|
||||
entry.insert_claim("authlevel_strong")
|
||||
}
|
||||
};
|
||||
|
||||
match &uat.auth_type {
|
||||
AuthType::Anonymous => {}
|
||||
AuthType::UnixPassword
|
||||
| AuthType::Password
|
||||
| AuthType::GeneratedPassword
|
||||
| AuthType::Webauthn => entry.insert_claim("authclass_single"),
|
||||
AuthType::PasswordMfa => entry.insert_claim("authclass_mfa"),
|
||||
};
|
||||
*/
|
||||
|
||||
ltrace!(audit, "Applied claims -> {:?}", entry.get_ava_set("claim"));
|
||||
|
||||
let limits = Limits::from_uat(uat);
|
||||
Ok(Identity {
|
||||
|
@ -479,7 +521,7 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
};
|
||||
|
||||
let (auth_session, state) = if is_valid {
|
||||
AuthSession::new(au, account, &init.appid, self.webauthn, ct)
|
||||
AuthSession::new(au, account, self.webauthn, ct)
|
||||
} else {
|
||||
// it's softlocked, don't even bother.
|
||||
lsecurity!(au, "Account is softlocked.");
|
||||
|
@ -764,7 +806,7 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
Ok(Some(LdapBoundToken {
|
||||
uuid: *UUID_ANONYMOUS,
|
||||
effective_uat: account
|
||||
.to_userauthtoken(au.uuid, &[], ct)
|
||||
.to_userauthtoken(au.uuid, ct, AuthType::Anonymous)
|
||||
.ok_or(OperationError::InvalidState)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "Unable to generate effective_uat -> {:?}", e);
|
||||
|
@ -826,7 +868,7 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
spn: account.spn,
|
||||
uuid: account.uuid,
|
||||
effective_uat: anon_account
|
||||
.to_userauthtoken(au.uuid, &[], ct)
|
||||
.to_userauthtoken(au.uuid, ct, AuthType::UnixPassword)
|
||||
.ok_or(OperationError::InvalidState)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "Unable to generate effective_uat -> {:?}", e);
|
||||
|
@ -1074,7 +1116,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
// Get the modifications we *want* to perform.
|
||||
let modlist = account
|
||||
.gen_password_mod(pce.cleartext.as_str(), &pce.appid, self.crypto_policy)
|
||||
.gen_password_mod(pce.cleartext.as_str(), self.crypto_policy)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "Failed to generate password mod {:?}", e);
|
||||
e
|
||||
|
@ -1233,8 +1275,8 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
&mut self,
|
||||
au: &mut AuditScope,
|
||||
name: &str,
|
||||
cleartext: &str,
|
||||
) -> Result<(), OperationError> {
|
||||
cleartext: Option<&str>,
|
||||
) -> Result<String, OperationError> {
|
||||
// name to uuid
|
||||
let target = self.qs_write.name_to_uuid(au, name).map_err(|e| {
|
||||
ladmin_error!(au, "name to uuid failed {:?}", e);
|
||||
|
@ -1243,8 +1285,12 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
let account = self.target_to_account(au, &target)?;
|
||||
|
||||
let cleartext = cleartext
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(password_from_random);
|
||||
|
||||
let modlist = account
|
||||
.gen_password_recover_mod(cleartext, self.crypto_policy)
|
||||
.gen_generatedpassword_recover_mod(&cleartext, self.crypto_policy)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "Failed to generate password mod {:?}", e);
|
||||
e
|
||||
|
@ -1263,7 +1309,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
e
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
Ok(cleartext)
|
||||
}
|
||||
|
||||
pub fn generate_account_password(
|
||||
|
@ -1283,7 +1329,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
// it returns a modify
|
||||
let modlist = account
|
||||
.gen_password_mod(cleartext.as_str(), &gpe.appid, self.crypto_policy)
|
||||
.gen_generatedpassword_recover_mod(cleartext.as_str(), self.crypto_policy)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "Unable to generate password mod {:?}", e);
|
||||
e
|
||||
|
@ -1473,8 +1519,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let sessionid = uuid_from_duration(ct, self.sid);
|
||||
|
||||
let origin = (>e.ident.origin).into();
|
||||
let label = gte.label.clone();
|
||||
let (session, next) = MfaRegSession::totp_new(origin, account, label).map_err(|e| {
|
||||
let (session, next) = MfaRegSession::totp_new(origin, account).map_err(|e| {
|
||||
ladmin_error!(au, "Unable to start totp MfaRegSession {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -1585,16 +1630,12 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let account = self.target_to_account(au, &pwu.target_uuid)?;
|
||||
|
||||
// check, does the pw still match?
|
||||
let same = account.check_credential_pw(pwu.existing_password.as_str(), &pwu.appid)?;
|
||||
let same = account.check_credential_pw(pwu.existing_password.as_str())?;
|
||||
|
||||
// if yes, gen the pw mod and apply.
|
||||
if same {
|
||||
let modlist = account
|
||||
.gen_password_mod(
|
||||
pwu.existing_password.as_str(),
|
||||
&pwu.appid,
|
||||
self.crypto_policy,
|
||||
)
|
||||
.gen_password_mod(pwu.existing_password.as_str(), self.crypto_policy)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "Unable to generate password mod {:?}", e);
|
||||
e
|
||||
|
@ -1739,7 +1780,7 @@ mod tests {
|
|||
use crate::prelude::*;
|
||||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::SetCredentialResponse;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech};
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthMech, AuthType};
|
||||
|
||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||
// , IdmServerDelayed;
|
||||
|
@ -2184,7 +2225,7 @@ mod tests {
|
|||
idms: &IdmServer,
|
||||
_idms_delayed: &IdmServerDelayed,
|
||||
au: &mut AuditScope| {
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD, None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD);
|
||||
|
||||
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
|
||||
assert!(idms_prox_write.set_account_password(au, &pce).is_ok());
|
||||
|
@ -2199,7 +2240,7 @@ mod tests {
|
|||
idms: &IdmServer,
|
||||
_idms_delayed: &IdmServerDelayed,
|
||||
au: &mut AuditScope| {
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ANONYMOUS, TEST_PASSWORD, None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ANONYMOUS, TEST_PASSWORD);
|
||||
|
||||
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
|
||||
assert!(idms_prox_write.set_account_password(au, &pce).is_err());
|
||||
|
@ -2271,7 +2312,7 @@ mod tests {
|
|||
.expect("Failed to reset radius credential 1");
|
||||
|
||||
// Try and set that as the main account password, should fail.
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, r1.as_str(), None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, r1.as_str());
|
||||
let e = idms_prox_write.set_account_password(au, &pce);
|
||||
assert!(e.is_err());
|
||||
|
||||
|
@ -2316,17 +2357,17 @@ mod tests {
|
|||
// len check
|
||||
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
|
||||
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "password", None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "password");
|
||||
let e = idms_prox_write.set_account_password(au, &pce);
|
||||
assert!(e.is_err());
|
||||
|
||||
// zxcvbn check
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "password1234", None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "password1234");
|
||||
let e = idms_prox_write.set_account_password(au, &pce);
|
||||
assert!(e.is_err());
|
||||
|
||||
// Check the "name" checking works too (I think admin may hit a common pw rule first)
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "admin_nta", None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "admin_nta");
|
||||
let e = idms_prox_write.set_account_password(au, &pce);
|
||||
assert!(e.is_err());
|
||||
|
||||
|
@ -2334,7 +2375,6 @@ mod tests {
|
|||
let pce = PasswordChangeEvent::new_internal(
|
||||
&UUID_ADMIN,
|
||||
"demo_badlist_shohfie3aeci2oobur0aru9uushah6EiPi2woh4hohngoighaiRuepieN3ongoo1",
|
||||
None,
|
||||
);
|
||||
let e = idms_prox_write.set_account_password(au, &pce);
|
||||
assert!(e.is_err());
|
||||
|
@ -2352,7 +2392,7 @@ mod tests {
|
|||
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now());
|
||||
|
||||
// Check that the badlist password inserted is rejected.
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "bad@no3IBTyqHu$list", None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, "bad@no3IBTyqHu$list");
|
||||
let e = idms_prox_write.set_account_password(au, &pce);
|
||||
assert!(e.is_err());
|
||||
|
||||
|
@ -2585,7 +2625,7 @@ mod tests {
|
|||
idms_prox_write.expire_mfareg_sessions(expire.clone());
|
||||
|
||||
// Set a password.
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD, None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD);
|
||||
assert!(idms_prox_write.set_account_password(au, &pce).is_ok());
|
||||
|
||||
// == reg, but change the event source part way in the process (failure)
|
||||
|
@ -3354,7 +3394,7 @@ mod tests {
|
|||
_ => assert!(false),
|
||||
};
|
||||
// Reg a pw.
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD, None);
|
||||
let pce = PasswordChangeEvent::new_internal(&UUID_ADMIN, TEST_PASSWORD);
|
||||
assert!(idms_prox_write.set_account_password(au, &pce).is_ok());
|
||||
// Now remove, it will work.
|
||||
idms_prox_write
|
||||
|
@ -3394,4 +3434,110 @@ mod tests {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_idm_uat_claim_insertion() {
|
||||
run_idm_test!(|_qs: &QueryServer,
|
||||
idms: &IdmServer,
|
||||
_idms_delayed: &mut IdmServerDelayed,
|
||||
audit: &mut AuditScope| {
|
||||
let ct = Duration::from_secs(TEST_CURRENT_TIME);
|
||||
let mut idms_prox_write = idms.proxy_write(ct.clone());
|
||||
|
||||
// get an account.
|
||||
let account = idms_prox_write
|
||||
.target_to_account(audit, &UUID_ADMIN)
|
||||
.expect("account must exist");
|
||||
|
||||
// create some fake uats
|
||||
// process them and see what claims fall out :D
|
||||
let session_id = uuid::Uuid::new_v4();
|
||||
|
||||
// For the different auth types, check that we get the correct claims:
|
||||
|
||||
// == anonymous
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::Anonymous)
|
||||
.expect("Unable to create uat");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(audit, &uat, ct)
|
||||
.expect("Unable to process uat");
|
||||
|
||||
assert!(!ident.has_claim("authtype_anonymous"));
|
||||
// Does NOT have this
|
||||
assert!(!ident.has_claim("authlevel_strong"));
|
||||
assert!(!ident.has_claim("authclass_single"));
|
||||
assert!(!ident.has_claim("authclass_mfa"));
|
||||
|
||||
// == unixpassword
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::UnixPassword)
|
||||
.expect("Unable to create uat");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(audit, &uat, ct)
|
||||
.expect("Unable to process uat");
|
||||
|
||||
assert!(!ident.has_claim("authtype_unixpassword"));
|
||||
assert!(!ident.has_claim("authclass_single"));
|
||||
// Does NOT have this
|
||||
assert!(!ident.has_claim("authlevel_strong"));
|
||||
assert!(!ident.has_claim("authclass_mfa"));
|
||||
|
||||
// == password
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::Password)
|
||||
.expect("Unable to create uat");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(audit, &uat, ct)
|
||||
.expect("Unable to process uat");
|
||||
|
||||
assert!(!ident.has_claim("authtype_password"));
|
||||
assert!(!ident.has_claim("authclass_single"));
|
||||
// Does NOT have this
|
||||
assert!(!ident.has_claim("authlevel_strong"));
|
||||
assert!(!ident.has_claim("authclass_mfa"));
|
||||
|
||||
// == generatedpassword
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::GeneratedPassword)
|
||||
.expect("Unable to create uat");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(audit, &uat, ct)
|
||||
.expect("Unable to process uat");
|
||||
|
||||
assert!(!ident.has_claim("authtype_generatedpassword"));
|
||||
assert!(!ident.has_claim("authclass_single"));
|
||||
assert!(!ident.has_claim("authlevel_strong"));
|
||||
// Does NOT have this
|
||||
assert!(!ident.has_claim("authclass_mfa"));
|
||||
|
||||
// == webauthn
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::Webauthn)
|
||||
.expect("Unable to create uat");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(audit, &uat, ct)
|
||||
.expect("Unable to process uat");
|
||||
|
||||
assert!(!ident.has_claim("authtype_webauthn"));
|
||||
assert!(!ident.has_claim("authclass_single"));
|
||||
assert!(!ident.has_claim("authlevel_strong"));
|
||||
// Does NOT have this
|
||||
assert!(!ident.has_claim("authclass_mfa"));
|
||||
|
||||
// == passwordmfa
|
||||
let uat = account
|
||||
.to_userauthtoken(session_id, ct, AuthType::PasswordMfa)
|
||||
.expect("Unable to create uat");
|
||||
let ident = idms_prox_write
|
||||
.process_uat_to_identity(audit, &uat, ct)
|
||||
.expect("Unable to process uat");
|
||||
|
||||
assert!(!ident.has_claim("authtype_passwordmfa"));
|
||||
assert!(!ident.has_claim("authlevel_strong"));
|
||||
assert!(!ident.has_claim("authclass_mfa"));
|
||||
// Does NOT have this
|
||||
assert!(!ident.has_claim("authclass_single"));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ impl LdapServer {
|
|||
ladmin_info!(audit, "LDAP Search Request LDAP Attrs -> {:?}", l_attrs);
|
||||
ladmin_info!(audit, "LDAP Search Request Mapped Attrs -> {:?}", k_attrs);
|
||||
|
||||
// let ct = duration_from_epoch_now();
|
||||
let ct = duration_from_epoch_now();
|
||||
let idm_read = idms.proxy_read_async().await;
|
||||
lperf_segment!(audit, "ldap::do_search<core>", || {
|
||||
// Now start the txn - we need it for resolving filter components.
|
||||
|
@ -271,7 +271,7 @@ impl LdapServer {
|
|||
// ! Remember, searchEvent wraps to ignore hidden for us.
|
||||
let se = lperf_trace_segment!(audit, "ldap::do_search<core><prepare_se>", || {
|
||||
let ident = idm_read
|
||||
.process_uat_to_identity(audit, &uat.effective_uat)
|
||||
.process_uat_to_identity(audit, &uat.effective_uat, ct)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "Invalid identity: {:?}", e);
|
||||
e
|
||||
|
|
|
@ -234,7 +234,7 @@ mod tests {
|
|||
}"#,
|
||||
);
|
||||
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure(TOTP_DEFAULT_STEP);
|
||||
let p = CryptoPolicy::minimum();
|
||||
let c = Credential::new_password_only(&p, "password")
|
||||
.unwrap()
|
||||
|
|
|
@ -1080,12 +1080,14 @@ impl<'a> SchemaWriteTransaction<'a> {
|
|||
SchemaAttribute {
|
||||
name: AttrString::from("claim"),
|
||||
uuid: *UUID_SCHEMA_ATTR_CLAIM,
|
||||
description: String::from("The spn of a claim this entry holds"),
|
||||
description: String::from(
|
||||
"The string identifier of an extracted claim that can be filtered",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
phantom: true,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::SecurityPrincipalName,
|
||||
syntax: SyntaxType::Utf8StringInsensitive,
|
||||
},
|
||||
);
|
||||
self.attributes.insert(
|
||||
|
|
|
@ -273,14 +273,7 @@ async fn main() {
|
|||
}
|
||||
KanidmdOpt::RecoverAccount(raopt) => {
|
||||
eprintln!("Running account recovery ...");
|
||||
let password = match rpassword::prompt_password_stderr("new password: ") {
|
||||
Ok(pw) => pw,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to get password from prompt {:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
recover_account_core(&config, &raopt.name, &password);
|
||||
recover_account_core(&config, &raopt.name);
|
||||
}
|
||||
KanidmdOpt::Reindex(_copt) => {
|
||||
eprintln!("Running in reindex mode ...");
|
||||
|
|
Loading…
Reference in a new issue