Change config to thread-safe system, needed for a future config panel.

Improved some two factor methods.
This commit is contained in:
Daniel García
2019-01-25 18:23:51 +01:00
parent 86de0ca17b
commit a1dc47b826
19 changed files with 457 additions and 394 deletions

View File

@ -79,14 +79,14 @@ fn register(data: JsonUpcase<RegisterData>, conn: DbConn) -> EmptyResult {
}
user
} else if CONFIG.signups_allowed {
} else if CONFIG.signups_allowed() {
err!("Account with this email already exists")
} else {
err!("Registration not allowed")
}
}
None => {
if CONFIG.signups_allowed || Invitation::take(&data.Email, &conn) {
if CONFIG.signups_allowed() || Invitation::take(&data.Email, &conn) {
User::new(data.Email.clone())
} else {
err!("Registration not allowed")
@ -419,9 +419,9 @@ fn password_hint(data: JsonUpcase<PasswordHintData>, conn: DbConn) -> EmptyResul
None => return Ok(()),
};
if let Some(ref mail_config) = CONFIG.mail {
if let Some(ref mail_config) = CONFIG.mail() {
mail::send_password_hint(&data.Email, hint, mail_config)?;
} else if CONFIG.show_password_hint {
} else if CONFIG.show_password_hint() {
if let Some(hint) = hint {
err!(format!("Your password hint is: {}", &hint));
} else {

View File

@ -302,7 +302,7 @@ pub fn update_cipher_from_data(
cipher.fields = data.Fields.map(|f| f.to_string());
cipher.data = type_data.to_string();
cipher.password_history = data.PasswordHistory.map(|f| f.to_string());
cipher.save(&conn)?;
cipher.move_to_folder(data.FolderId, &headers.user.uuid, &conn)?;
@ -651,7 +651,7 @@ fn post_attachment(uuid: String, data: Data, content_type: &ContentType, headers
let boundary_pair = params.next().expect("No boundary provided");
let boundary = boundary_pair.1;
let base_path = Path::new(&CONFIG.attachments_folder).join(&cipher.uuid);
let base_path = Path::new(&CONFIG.attachments_folder()).join(&cipher.uuid);
let mut attachment_key = None;

View File

@ -486,17 +486,17 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
}
for email in data.Emails.iter() {
let mut user_org_status = match CONFIG.mail {
let mut user_org_status = match CONFIG.mail() {
Some(_) => UserOrgStatus::Invited as i32,
None => UserOrgStatus::Accepted as i32, // Automatically mark user as accepted if no email invites
};
let user = match User::find_by_mail(&email, &conn) {
None => {
if !CONFIG.invitations_allowed {
if !CONFIG.invitations_allowed() {
err!(format!("User email does not exist: {}", email))
}
if CONFIG.mail.is_none() {
if CONFIG.mail().is_none() {
let mut invitation = Invitation::new(email.clone());
invitation.save(&conn)?;
}
@ -535,7 +535,7 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
new_user.save(&conn)?;
if let Some(ref mail_config) = CONFIG.mail {
if let Some(ref mail_config) = CONFIG.mail() {
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
Some(org) => org.name,
None => err!("Error looking up organization"),
@ -558,11 +558,11 @@ fn send_invite(org_id: String, data: JsonUpcase<InviteData>, headers: AdminHeade
#[post("/organizations/<org_id>/users/<user_org>/reinvite")]
fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn: DbConn) -> EmptyResult {
if !CONFIG.invitations_allowed {
if !CONFIG.invitations_allowed() {
err!("Invitations are not allowed.")
}
if CONFIG.mail.is_none() {
if CONFIG.mail().is_none() {
err!("SMTP is not configured.")
}
@ -585,7 +585,7 @@ fn reinvite_user(org_id: String, user_org: String, headers: AdminHeaders, conn:
None => err!("Error looking up organization."),
};
if let Some(ref mail_config) = CONFIG.mail {
if let Some(ref mail_config) = CONFIG.mail() {
mail::send_invite(
&user.email,
&user.uuid,
@ -637,7 +637,7 @@ fn accept_invite(_org_id: String, _org_user_id: String, data: JsonUpcase<AcceptD
None => err!("Invited user not found"),
}
if let Some(ref mail_config) = CONFIG.mail {
if let Some(ref mail_config) = CONFIG.mail() {
let mut org_name = String::from("bitwarden_rs");
if let Some(org_id) = &claims.org_id {
org_name = match Organization::find_by_uuid(&org_id, &conn) {
@ -686,7 +686,7 @@ fn confirm_invite(
None => err!("Invalid key provided"),
};
if let Some(ref mail_config) = CONFIG.mail {
if let Some(ref mail_config) = CONFIG.mail() {
let org_name = match Organization::find_by_uuid(&org_id, &conn) {
Some(org) => org.name,
None => err!("Error looking up organization."),

View File

@ -179,7 +179,7 @@ fn activate_authenticator(data: JsonUpcase<EnableAuthenticatorData>, headers: He
Some(n) => n as u64,
None => err!("Malformed token"),
};
let mut user = headers.user;
if !user.check_valid_password(&password_hash) {
@ -236,13 +236,13 @@ use crate::CONFIG;
const U2F_VERSION: &str = "U2F_V2";
lazy_static! {
static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain);
static ref APP_ID: String = format!("{}/app-id.json", &CONFIG.domain());
static ref U2F: U2f = U2f::new(APP_ID.clone());
}
#[post("/two-factor/get-u2f", data = "<data>")]
fn generate_u2f(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
if !CONFIG.domain_set {
if !CONFIG.domain_set() {
err!("`DOMAIN` environment variable is not set. U2F disabled")
}
@ -286,6 +286,8 @@ fn generate_u2f_challenge(data: JsonUpcase<PasswordData>, headers: Headers, conn
#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
struct EnableU2FData {
Id: NumberOrString, // 1..5
Name: String,
MasterPasswordHash: String,
DeviceResponse: String,
}
@ -321,54 +323,52 @@ fn activate_u2f(data: JsonUpcase<EnableU2FData>, headers: Headers, conn: DbConn)
err!("Invalid password");
}
let tf_challenge =
TwoFactor::find_by_user_and_type(&user.uuid, TwoFactorType::U2fRegisterChallenge as i32, &conn);
let tf_type = TwoFactorType::U2fRegisterChallenge as i32;
let tf_challenge = match TwoFactor::find_by_user_and_type(&user.uuid, tf_type, &conn) {
Some(c) => c,
None => err!("Can't recover challenge"),
};
if let Some(tf_challenge) = tf_challenge {
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
let challenge: Challenge = serde_json::from_str(&tf_challenge.data)?;
tf_challenge.delete(&conn)?;
tf_challenge.delete(&conn)?;
let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
let response_copy: RegisterResponseCopy = serde_json::from_str(&data.DeviceResponse)?;
let error_code = response_copy
.error_code
.clone()
.map_or("0".into(), NumberOrString::into_string);
let error_code = response_copy
.error_code
.clone()
.map_or("0".into(), NumberOrString::into_string);
if error_code != "0" {
err!("Error registering U2F token")
}
let response = response_copy.into_response();
let registration = U2F.register_response(challenge.clone(), response)?;
// TODO: Allow more than one U2F device
let mut registrations = Vec::new();
registrations.push(registration);
let tf_registration = TwoFactor::new(
user.uuid.clone(),
TwoFactorType::U2f,
serde_json::to_string(&registrations).unwrap(),
);
tf_registration.save(&conn)?;
_generate_recover_code(&mut user, &conn);
Ok(Json(json!({
"Enabled": true,
"Challenge": {
"UserId": user.uuid,
"AppId": APP_ID.to_string(),
"Challenge": challenge,
"Version": U2F_VERSION,
},
"Object": "twoFactorU2f"
})))
} else {
err!("Can't recover challenge")
if error_code != "0" {
err!("Error registering U2F token")
}
let response = response_copy.into_response();
let registration = U2F.register_response(challenge.clone(), response)?;
// TODO: Allow more than one U2F device
let mut registrations = Vec::new();
registrations.push(registration);
let tf_registration = TwoFactor::new(
user.uuid.clone(),
TwoFactorType::U2f,
serde_json::to_string(&registrations).unwrap(),
);
tf_registration.save(&conn)?;
_generate_recover_code(&mut user, &conn);
Ok(Json(json!({
"Enabled": true,
"Challenge": {
"UserId": user.uuid,
"AppId": APP_ID.to_string(),
"Challenge": challenge,
"Version": U2F_VERSION,
},
"Object": "twoFactorU2f"
})))
}
#[put("/two-factor/u2f", data = "<data>")]
@ -493,29 +493,9 @@ use yubico::config::Config;
use yubico::Yubico;
fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
let mut yubikeys: Vec<String> = Vec::new();
let data_keys = [&data.Key1, &data.Key2, &data.Key3, &data.Key4, &data.Key5];
if data.Key1.is_some() {
yubikeys.push(data.Key1.as_ref().unwrap().to_owned());
}
if data.Key2.is_some() {
yubikeys.push(data.Key2.as_ref().unwrap().to_owned());
}
if data.Key3.is_some() {
yubikeys.push(data.Key3.as_ref().unwrap().to_owned());
}
if data.Key4.is_some() {
yubikeys.push(data.Key4.as_ref().unwrap().to_owned());
}
if data.Key5.is_some() {
yubikeys.push(data.Key5.as_ref().unwrap().to_owned());
}
yubikeys
data_keys.into_iter().filter_map(|e| e.as_ref().cloned()).collect()
}
fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
@ -529,17 +509,17 @@ fn jsonify_yubikeys(yubikeys: Vec<String>) -> serde_json::Value {
}
fn verify_yubikey_otp(otp: String) -> JsonResult {
if !CONFIG.yubico_cred_set {
if !CONFIG.yubico_cred_set() {
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
}
let yubico = Yubico::new();
let config = Config::default()
.set_client_id(CONFIG.yubico_client_id.to_owned())
.set_key(CONFIG.yubico_secret_key.to_owned());
.set_client_id(CONFIG.yubico_client_id())
.set_key(CONFIG.yubico_secret_key());
let result = match CONFIG.yubico_server {
Some(ref server) => yubico.verify(otp, config.set_api_hosts(vec![server.to_owned()])),
let result = match CONFIG.yubico_server() {
Some(server) => yubico.verify(otp, config.set_api_hosts(vec![server])),
None => yubico.verify(otp, config),
};
@ -551,7 +531,7 @@ fn verify_yubikey_otp(otp: String) -> JsonResult {
#[post("/two-factor/get-yubikey", data = "<data>")]
fn generate_yubikey(data: JsonUpcase<PasswordData>, headers: Headers, conn: DbConn) -> JsonResult {
if !CONFIG.yubico_cred_set {
if !CONFIG.yubico_cred_set() {
err!("`YUBICO_CLIENT_ID` or `YUBICO_SECRET_KEY` environment variable is not set. Yubikey OTP Disabled")
}
@ -637,7 +617,7 @@ fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn:
serde_json::to_string(&yubikey_metadata).unwrap(),
);
yubikey_registration.save(&conn)?;
_generate_recover_code(&mut user, &conn);
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);