mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-11-05 12:48:19 +02:00
Change API and structs to camelCase (#4386)
* Change API inputs/outputs and structs to camelCase * Fix fields and password history * Use convert_json_key_lcase_first * Make sends lowercase * Update admin and templates * Update org revoke * Fix sends expecting size to be a string on mobile * Convert two-factor providers to string
This commit is contained in:
@@ -3,10 +3,7 @@ use rocket::serde::json::Json;
|
||||
use rocket::Route;
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
core::log_user_event, core::two_factor::_generate_recover_code, EmptyResult, JsonResult, JsonUpcase,
|
||||
PasswordOrOtpData,
|
||||
},
|
||||
api::{core::log_user_event, core::two_factor::_generate_recover_code, EmptyResult, JsonResult, PasswordOrOtpData},
|
||||
auth::{ClientIp, Headers},
|
||||
crypto,
|
||||
db::{
|
||||
@@ -23,8 +20,8 @@ pub fn routes() -> Vec<Route> {
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-authenticator", data = "<data>")]
|
||||
async fn generate_authenticator(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
async fn generate_authenticator(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, false, &mut conn).await?;
|
||||
@@ -38,36 +35,32 @@ async fn generate_authenticator(data: JsonUpcase<PasswordOrOtpData>, headers: He
|
||||
};
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": enabled,
|
||||
"Key": key,
|
||||
"Object": "twoFactorAuthenticator"
|
||||
"enabled": enabled,
|
||||
"key": key,
|
||||
"object": "twoFactorAuthenticator"
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EnableAuthenticatorData {
|
||||
Key: String,
|
||||
Token: NumberOrString,
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
key: String,
|
||||
token: NumberOrString,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
}
|
||||
|
||||
#[post("/two-factor/authenticator", data = "<data>")]
|
||||
async fn activate_authenticator(
|
||||
data: JsonUpcase<EnableAuthenticatorData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let data: EnableAuthenticatorData = data.into_inner().data;
|
||||
let key = data.Key;
|
||||
let token = data.Token.into_string();
|
||||
async fn activate_authenticator(data: Json<EnableAuthenticatorData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableAuthenticatorData = data.into_inner();
|
||||
let key = data.key;
|
||||
let token = data.token.into_string();
|
||||
|
||||
let mut user = headers.user;
|
||||
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash,
|
||||
Otp: data.Otp,
|
||||
master_password_hash: data.master_password_hash,
|
||||
otp: data.otp,
|
||||
}
|
||||
.validate(&user, true, &mut conn)
|
||||
.await?;
|
||||
@@ -90,18 +83,14 @@ async fn activate_authenticator(
|
||||
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": true,
|
||||
"Key": key,
|
||||
"Object": "twoFactorAuthenticator"
|
||||
"enabled": true,
|
||||
"key": key,
|
||||
"object": "twoFactorAuthenticator"
|
||||
})))
|
||||
}
|
||||
|
||||
#[put("/two-factor/authenticator", data = "<data>")]
|
||||
async fn activate_authenticator_put(
|
||||
data: JsonUpcase<EnableAuthenticatorData>,
|
||||
headers: Headers,
|
||||
conn: DbConn,
|
||||
) -> JsonResult {
|
||||
async fn activate_authenticator_put(data: Json<EnableAuthenticatorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
activate_authenticator(data, headers, conn).await
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use rocket::Route;
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
core::log_user_event, core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult, JsonUpcase,
|
||||
core::log_user_event, core::two_factor::_generate_recover_code, ApiResult, EmptyResult, JsonResult,
|
||||
PasswordOrOtpData,
|
||||
},
|
||||
auth::Headers,
|
||||
@@ -92,8 +92,8 @@ impl DuoStatus {
|
||||
const DISABLED_MESSAGE_DEFAULT: &str = "<To use the global Duo keys, please leave these fields untouched>";
|
||||
|
||||
#[post("/two-factor/get-duo", data = "<data>")]
|
||||
async fn get_duo(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
async fn get_duo(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, false, &mut conn).await?;
|
||||
@@ -109,16 +109,16 @@ async fn get_duo(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn
|
||||
|
||||
let json = if let Some(data) = data {
|
||||
json!({
|
||||
"Enabled": enabled,
|
||||
"Host": data.host,
|
||||
"SecretKey": data.sk,
|
||||
"IntegrationKey": data.ik,
|
||||
"Object": "twoFactorDuo"
|
||||
"enabled": enabled,
|
||||
"host": data.host,
|
||||
"secretKey": data.sk,
|
||||
"integrationKey": data.ik,
|
||||
"object": "twoFactorDuo"
|
||||
})
|
||||
} else {
|
||||
json!({
|
||||
"Enabled": enabled,
|
||||
"Object": "twoFactorDuo"
|
||||
"enabled": enabled,
|
||||
"object": "twoFactorDuo"
|
||||
})
|
||||
};
|
||||
|
||||
@@ -126,21 +126,21 @@ async fn get_duo(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case, dead_code)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EnableDuoData {
|
||||
Host: String,
|
||||
SecretKey: String,
|
||||
IntegrationKey: String,
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
host: String,
|
||||
secret_key: String,
|
||||
integration_key: String,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
}
|
||||
|
||||
impl From<EnableDuoData> for DuoData {
|
||||
fn from(d: EnableDuoData) -> Self {
|
||||
Self {
|
||||
host: d.Host,
|
||||
ik: d.IntegrationKey,
|
||||
sk: d.SecretKey,
|
||||
host: d.host,
|
||||
ik: d.integration_key,
|
||||
sk: d.secret_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,17 +151,17 @@ fn check_duo_fields_custom(data: &EnableDuoData) -> bool {
|
||||
st.is_empty() || s == DISABLED_MESSAGE_DEFAULT
|
||||
}
|
||||
|
||||
!empty_or_default(&data.Host) && !empty_or_default(&data.SecretKey) && !empty_or_default(&data.IntegrationKey)
|
||||
!empty_or_default(&data.host) && !empty_or_default(&data.secret_key) && !empty_or_default(&data.integration_key)
|
||||
}
|
||||
|
||||
#[post("/two-factor/duo", data = "<data>")]
|
||||
async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableDuoData = data.into_inner().data;
|
||||
async fn activate_duo(data: Json<EnableDuoData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableDuoData = data.into_inner();
|
||||
let mut user = headers.user;
|
||||
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash.clone(),
|
||||
Otp: data.Otp.clone(),
|
||||
master_password_hash: data.master_password_hash.clone(),
|
||||
otp: data.otp.clone(),
|
||||
}
|
||||
.validate(&user, true, &mut conn)
|
||||
.await?;
|
||||
@@ -184,16 +184,16 @@ async fn activate_duo(data: JsonUpcase<EnableDuoData>, headers: Headers, mut con
|
||||
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": true,
|
||||
"Host": data.host,
|
||||
"SecretKey": data.sk,
|
||||
"IntegrationKey": data.ik,
|
||||
"Object": "twoFactorDuo"
|
||||
"enabled": true,
|
||||
"host": data.host,
|
||||
"secretKey": data.sk,
|
||||
"integrationKey": data.ik,
|
||||
"object": "twoFactorDuo"
|
||||
})))
|
||||
}
|
||||
|
||||
#[put("/two-factor/duo", data = "<data>")]
|
||||
async fn activate_duo_put(data: JsonUpcase<EnableDuoData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn activate_duo_put(data: Json<EnableDuoData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
activate_duo(data, headers, conn).await
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use rocket::Route;
|
||||
use crate::{
|
||||
api::{
|
||||
core::{log_user_event, two_factor::_generate_recover_code},
|
||||
EmptyResult, JsonResult, JsonUpcase, PasswordOrOtpData,
|
||||
EmptyResult, JsonResult, PasswordOrOtpData,
|
||||
},
|
||||
auth::Headers,
|
||||
crypto,
|
||||
@@ -22,28 +22,28 @@ pub fn routes() -> Vec<Route> {
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SendEmailLoginData {
|
||||
Email: String,
|
||||
MasterPasswordHash: String,
|
||||
email: String,
|
||||
master_password_hash: String,
|
||||
}
|
||||
|
||||
/// User is trying to login and wants to use email 2FA.
|
||||
/// Does not require Bearer token
|
||||
#[post("/two-factor/send-email-login", data = "<data>")] // JsonResult
|
||||
async fn send_email_login(data: JsonUpcase<SendEmailLoginData>, mut conn: DbConn) -> EmptyResult {
|
||||
let data: SendEmailLoginData = data.into_inner().data;
|
||||
async fn send_email_login(data: Json<SendEmailLoginData>, mut conn: DbConn) -> EmptyResult {
|
||||
let data: SendEmailLoginData = data.into_inner();
|
||||
|
||||
use crate::db::models::User;
|
||||
|
||||
// Get the user
|
||||
let user = match User::find_by_mail(&data.Email, &mut conn).await {
|
||||
let user = match User::find_by_mail(&data.email, &mut conn).await {
|
||||
Some(user) => user,
|
||||
None => err!("Username or password is incorrect. Try again."),
|
||||
};
|
||||
|
||||
// Check password
|
||||
if !user.check_valid_password(&data.MasterPasswordHash) {
|
||||
if !user.check_valid_password(&data.master_password_hash) {
|
||||
err!("Username or password is incorrect. Try again.")
|
||||
}
|
||||
|
||||
@@ -76,8 +76,8 @@ pub async fn send_token(user_uuid: &str, conn: &mut DbConn) -> EmptyResult {
|
||||
|
||||
/// When user clicks on Manage email 2FA show the user the related information
|
||||
#[post("/two-factor/get-email", data = "<data>")]
|
||||
async fn get_email(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
async fn get_email(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, false, &mut conn).await?;
|
||||
@@ -92,30 +92,30 @@ async fn get_email(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut co
|
||||
};
|
||||
|
||||
Ok(Json(json!({
|
||||
"Email": mfa_email,
|
||||
"Enabled": enabled,
|
||||
"Object": "twoFactorEmail"
|
||||
"email": mfa_email,
|
||||
"enabled": enabled,
|
||||
"object": "twoFactorEmail"
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SendEmailData {
|
||||
/// Email where 2FA codes will be sent to, can be different than user email account.
|
||||
Email: String,
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
email: String,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
}
|
||||
|
||||
/// Send a verification email to the specified email address to check whether it exists/belongs to user.
|
||||
#[post("/two-factor/send-email", data = "<data>")]
|
||||
async fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||
let data: SendEmailData = data.into_inner().data;
|
||||
async fn send_email(data: Json<SendEmailData>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||
let data: SendEmailData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash,
|
||||
Otp: data.Otp,
|
||||
master_password_hash: data.master_password_hash,
|
||||
otp: data.otp,
|
||||
}
|
||||
.validate(&user, false, &mut conn)
|
||||
.await?;
|
||||
@@ -131,7 +131,7 @@ async fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, mut conn:
|
||||
}
|
||||
|
||||
let generated_token = crypto::generate_email_token(CONFIG.email_token_size());
|
||||
let twofactor_data = EmailTokenData::new(data.Email, generated_token);
|
||||
let twofactor_data = EmailTokenData::new(data.email, generated_token);
|
||||
|
||||
// Uses EmailVerificationChallenge as type to show that it's not verified yet.
|
||||
let twofactor = TwoFactor::new(user.uuid, TwoFactorType::EmailVerificationChallenge, twofactor_data.to_json());
|
||||
@@ -143,24 +143,24 @@ async fn send_email(data: JsonUpcase<SendEmailData>, headers: Headers, mut conn:
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EmailData {
|
||||
Email: String,
|
||||
Token: String,
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
email: String,
|
||||
token: String,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
}
|
||||
|
||||
/// Verify email belongs to user and can be used for 2FA email codes.
|
||||
#[put("/two-factor/email", data = "<data>")]
|
||||
async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EmailData = data.into_inner().data;
|
||||
async fn email(data: Json<EmailData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EmailData = data.into_inner();
|
||||
let mut user = headers.user;
|
||||
|
||||
// This is the last step in the verification process, delete the otp directly afterwards
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash,
|
||||
Otp: data.Otp,
|
||||
master_password_hash: data.master_password_hash,
|
||||
otp: data.otp,
|
||||
}
|
||||
.validate(&user, true, &mut conn)
|
||||
.await?;
|
||||
@@ -176,7 +176,7 @@ async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn)
|
||||
_ => err!("No token available"),
|
||||
};
|
||||
|
||||
if !crypto::ct_eq(issued_token, data.Token) {
|
||||
if !crypto::ct_eq(issued_token, data.token) {
|
||||
err!("Token is invalid")
|
||||
}
|
||||
|
||||
@@ -190,9 +190,9 @@ async fn email(data: JsonUpcase<EmailData>, headers: Headers, mut conn: DbConn)
|
||||
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
|
||||
|
||||
Ok(Json(json!({
|
||||
"Email": email_data.email,
|
||||
"Enabled": "true",
|
||||
"Object": "twoFactorEmail"
|
||||
"email": email_data.email,
|
||||
"enabled": "true",
|
||||
"object": "twoFactorEmail"
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use serde_json::Value;
|
||||
use crate::{
|
||||
api::{
|
||||
core::{log_event, log_user_event},
|
||||
EmptyResult, JsonResult, JsonUpcase, PasswordOrOtpData,
|
||||
EmptyResult, JsonResult, PasswordOrOtpData,
|
||||
},
|
||||
auth::{ClientHeaders, Headers},
|
||||
crypto,
|
||||
@@ -50,52 +50,52 @@ async fn get_twofactor(headers: Headers, mut conn: DbConn) -> Json<Value> {
|
||||
let twofactors_json: Vec<Value> = twofactors.iter().map(TwoFactor::to_json_provider).collect();
|
||||
|
||||
Json(json!({
|
||||
"Data": twofactors_json,
|
||||
"Object": "list",
|
||||
"ContinuationToken": null,
|
||||
"data": twofactors_json,
|
||||
"object": "list",
|
||||
"continuationToken": null,
|
||||
}))
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-recover", data = "<data>")]
|
||||
async fn get_recover(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
async fn get_recover(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, true, &mut conn).await?;
|
||||
|
||||
Ok(Json(json!({
|
||||
"Code": user.totp_recover,
|
||||
"Object": "twoFactorRecover"
|
||||
"code": user.totp_recover,
|
||||
"object": "twoFactorRecover"
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct RecoverTwoFactor {
|
||||
MasterPasswordHash: String,
|
||||
Email: String,
|
||||
RecoveryCode: String,
|
||||
master_password_hash: String,
|
||||
email: String,
|
||||
recovery_code: String,
|
||||
}
|
||||
|
||||
#[post("/two-factor/recover", data = "<data>")]
|
||||
async fn recover(data: JsonUpcase<RecoverTwoFactor>, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult {
|
||||
let data: RecoverTwoFactor = data.into_inner().data;
|
||||
async fn recover(data: Json<RecoverTwoFactor>, client_headers: ClientHeaders, mut conn: DbConn) -> JsonResult {
|
||||
let data: RecoverTwoFactor = data.into_inner();
|
||||
|
||||
use crate::db::models::User;
|
||||
|
||||
// Get the user
|
||||
let mut user = match User::find_by_mail(&data.Email, &mut conn).await {
|
||||
let mut user = match User::find_by_mail(&data.email, &mut conn).await {
|
||||
Some(user) => user,
|
||||
None => err!("Username or password is incorrect. Try again."),
|
||||
};
|
||||
|
||||
// Check password
|
||||
if !user.check_valid_password(&data.MasterPasswordHash) {
|
||||
if !user.check_valid_password(&data.master_password_hash) {
|
||||
err!("Username or password is incorrect. Try again.")
|
||||
}
|
||||
|
||||
// Check if recovery code is correct
|
||||
if !user.check_valid_recovery_code(&data.RecoveryCode) {
|
||||
if !user.check_valid_recovery_code(&data.recovery_code) {
|
||||
err!("Recovery code is incorrect. Try again.")
|
||||
}
|
||||
|
||||
@@ -127,27 +127,27 @@ async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) {
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct DisableTwoFactorData {
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
Type: NumberOrString,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
r#type: NumberOrString,
|
||||
}
|
||||
|
||||
#[post("/two-factor/disable", data = "<data>")]
|
||||
async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: DisableTwoFactorData = data.into_inner().data;
|
||||
async fn disable_twofactor(data: Json<DisableTwoFactorData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: DisableTwoFactorData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
// Delete directly after a valid token has been provided
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash,
|
||||
Otp: data.Otp,
|
||||
master_password_hash: data.master_password_hash,
|
||||
otp: data.otp,
|
||||
}
|
||||
.validate(&user, true, &mut conn)
|
||||
.await?;
|
||||
|
||||
let type_ = data.Type.into_i32()?;
|
||||
let type_ = data.r#type.into_i32()?;
|
||||
|
||||
if let Some(twofactor) = TwoFactor::find_by_user_and_type(&user.uuid, type_, &mut conn).await {
|
||||
twofactor.delete(&mut conn).await?;
|
||||
@@ -160,14 +160,14 @@ async fn disable_twofactor(data: JsonUpcase<DisableTwoFactorData>, headers: Head
|
||||
}
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": false,
|
||||
"Type": type_,
|
||||
"Object": "twoFactorProvider"
|
||||
"enabled": false,
|
||||
"type": type_,
|
||||
"object": "twoFactorProvider"
|
||||
})))
|
||||
}
|
||||
|
||||
#[put("/two-factor/disable", data = "<data>")]
|
||||
async fn disable_twofactor_put(data: JsonUpcase<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn disable_twofactor_put(data: Json<DisableTwoFactorData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
disable_twofactor(data, headers, conn).await
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use rocket::Route;
|
||||
use rocket::{serde::json::Json, Route};
|
||||
|
||||
use crate::{
|
||||
api::{EmptyResult, JsonUpcase},
|
||||
api::EmptyResult,
|
||||
auth::Headers,
|
||||
crypto,
|
||||
db::{
|
||||
@@ -18,7 +18,7 @@ pub fn routes() -> Vec<Route> {
|
||||
}
|
||||
|
||||
/// Data stored in the TwoFactor table in the db
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ProtectedActionData {
|
||||
/// Token issued to validate the protected action
|
||||
pub token: String,
|
||||
@@ -82,23 +82,24 @@ async fn request_otp(headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct ProtectedActionVerify {
|
||||
OTP: String,
|
||||
#[serde(rename = "OTP", alias = "otp")]
|
||||
otp: String,
|
||||
}
|
||||
|
||||
#[post("/accounts/verify-otp", data = "<data>")]
|
||||
async fn verify_otp(data: JsonUpcase<ProtectedActionVerify>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||
async fn verify_otp(data: Json<ProtectedActionVerify>, headers: Headers, mut conn: DbConn) -> EmptyResult {
|
||||
if !CONFIG.mail_enabled() {
|
||||
err!("Email is disabled for this server. Either enable email or login using your master password instead of login via device.");
|
||||
}
|
||||
|
||||
let user = headers.user;
|
||||
let data: ProtectedActionVerify = data.into_inner().data;
|
||||
let data: ProtectedActionVerify = data.into_inner();
|
||||
|
||||
// Delete the token after one validation attempt
|
||||
// This endpoint only gets called for the vault export, and doesn't need a second attempt
|
||||
validate_protected_action_otp(&data.OTP, &user.uuid, true, &mut conn).await
|
||||
validate_protected_action_otp(&data.otp, &user.uuid, true, &mut conn).await
|
||||
}
|
||||
|
||||
pub async fn validate_protected_action_otp(
|
||||
|
||||
@@ -7,7 +7,7 @@ use webauthn_rs::{base64_data::Base64UrlSafeData, proto::*, AuthenticationState,
|
||||
use crate::{
|
||||
api::{
|
||||
core::{log_user_event, two_factor::_generate_recover_code},
|
||||
EmptyResult, JsonResult, JsonUpcase, PasswordOrOtpData,
|
||||
EmptyResult, JsonResult, PasswordOrOtpData,
|
||||
},
|
||||
auth::Headers,
|
||||
db::{
|
||||
@@ -96,20 +96,20 @@ pub struct WebauthnRegistration {
|
||||
impl WebauthnRegistration {
|
||||
fn to_json(&self) -> Value {
|
||||
json!({
|
||||
"Id": self.id,
|
||||
"Name": self.name,
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"migrated": self.migrated,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-webauthn", data = "<data>")]
|
||||
async fn get_webauthn(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
async fn get_webauthn(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
if !CONFIG.domain_set() {
|
||||
err!("`DOMAIN` environment variable is not set. Webauthn disabled")
|
||||
}
|
||||
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, false, &mut conn).await?;
|
||||
@@ -118,19 +118,15 @@ async fn get_webauthn(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut
|
||||
let registrations_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect();
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": enabled,
|
||||
"Keys": registrations_json,
|
||||
"Object": "twoFactorWebAuthn"
|
||||
"enabled": enabled,
|
||||
"keys": registrations_json,
|
||||
"object": "twoFactorWebAuthn"
|
||||
})))
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-webauthn-challenge", data = "<data>")]
|
||||
async fn generate_webauthn_challenge(
|
||||
data: JsonUpcase<PasswordOrOtpData>,
|
||||
headers: Headers,
|
||||
mut conn: DbConn,
|
||||
) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
async fn generate_webauthn_challenge(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, false, &mut conn).await?;
|
||||
@@ -161,102 +157,94 @@ async fn generate_webauthn_challenge(
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EnableWebauthnData {
|
||||
Id: NumberOrString, // 1..5
|
||||
Name: String,
|
||||
DeviceResponse: RegisterPublicKeyCredentialCopy,
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
id: NumberOrString, // 1..5
|
||||
name: String,
|
||||
device_response: RegisterPublicKeyCredentialCopy,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
}
|
||||
|
||||
// This is copied from RegisterPublicKeyCredential to change the Response objects casing
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct RegisterPublicKeyCredentialCopy {
|
||||
pub Id: String,
|
||||
pub RawId: Base64UrlSafeData,
|
||||
pub Response: AuthenticatorAttestationResponseRawCopy,
|
||||
pub Type: String,
|
||||
pub id: String,
|
||||
pub raw_id: Base64UrlSafeData,
|
||||
pub response: AuthenticatorAttestationResponseRawCopy,
|
||||
pub r#type: String,
|
||||
}
|
||||
|
||||
// This is copied from AuthenticatorAttestationResponseRaw to change clientDataJSON to clientDataJson
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthenticatorAttestationResponseRawCopy {
|
||||
pub AttestationObject: Base64UrlSafeData,
|
||||
pub ClientDataJson: Base64UrlSafeData,
|
||||
#[serde(rename = "AttestationObject", alias = "attestationObject")]
|
||||
pub attestation_object: Base64UrlSafeData,
|
||||
#[serde(rename = "clientDataJson", alias = "clientDataJSON")]
|
||||
pub client_data_json: Base64UrlSafeData,
|
||||
}
|
||||
|
||||
impl From<RegisterPublicKeyCredentialCopy> for RegisterPublicKeyCredential {
|
||||
fn from(r: RegisterPublicKeyCredentialCopy) -> Self {
|
||||
Self {
|
||||
id: r.Id,
|
||||
raw_id: r.RawId,
|
||||
id: r.id,
|
||||
raw_id: r.raw_id,
|
||||
response: AuthenticatorAttestationResponseRaw {
|
||||
attestation_object: r.Response.AttestationObject,
|
||||
client_data_json: r.Response.ClientDataJson,
|
||||
attestation_object: r.response.attestation_object,
|
||||
client_data_json: r.response.client_data_json,
|
||||
},
|
||||
type_: r.Type,
|
||||
type_: r.r#type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is copied from PublicKeyCredential to change the Response objects casing
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PublicKeyCredentialCopy {
|
||||
pub Id: String,
|
||||
pub RawId: Base64UrlSafeData,
|
||||
pub Response: AuthenticatorAssertionResponseRawCopy,
|
||||
pub Extensions: Option<AuthenticationExtensionsClientOutputsCopy>,
|
||||
pub Type: String,
|
||||
pub id: String,
|
||||
pub raw_id: Base64UrlSafeData,
|
||||
pub response: AuthenticatorAssertionResponseRawCopy,
|
||||
pub extensions: Option<AuthenticationExtensionsClientOutputs>,
|
||||
pub r#type: String,
|
||||
}
|
||||
|
||||
// This is copied from AuthenticatorAssertionResponseRaw to change clientDataJSON to clientDataJson
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthenticatorAssertionResponseRawCopy {
|
||||
pub AuthenticatorData: Base64UrlSafeData,
|
||||
pub ClientDataJson: Base64UrlSafeData,
|
||||
pub Signature: Base64UrlSafeData,
|
||||
pub UserHandle: Option<Base64UrlSafeData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct AuthenticationExtensionsClientOutputsCopy {
|
||||
#[serde(default)]
|
||||
pub Appid: bool,
|
||||
pub authenticator_data: Base64UrlSafeData,
|
||||
#[serde(rename = "clientDataJson", alias = "clientDataJSON")]
|
||||
pub client_data_json: Base64UrlSafeData,
|
||||
pub signature: Base64UrlSafeData,
|
||||
pub user_handle: Option<Base64UrlSafeData>,
|
||||
}
|
||||
|
||||
impl From<PublicKeyCredentialCopy> for PublicKeyCredential {
|
||||
fn from(r: PublicKeyCredentialCopy) -> Self {
|
||||
Self {
|
||||
id: r.Id,
|
||||
raw_id: r.RawId,
|
||||
id: r.id,
|
||||
raw_id: r.raw_id,
|
||||
response: AuthenticatorAssertionResponseRaw {
|
||||
authenticator_data: r.Response.AuthenticatorData,
|
||||
client_data_json: r.Response.ClientDataJson,
|
||||
signature: r.Response.Signature,
|
||||
user_handle: r.Response.UserHandle,
|
||||
authenticator_data: r.response.authenticator_data,
|
||||
client_data_json: r.response.client_data_json,
|
||||
signature: r.response.signature,
|
||||
user_handle: r.response.user_handle,
|
||||
},
|
||||
extensions: r.Extensions.map(|e| AuthenticationExtensionsClientOutputs {
|
||||
appid: e.Appid,
|
||||
}),
|
||||
type_: r.Type,
|
||||
extensions: r.extensions,
|
||||
type_: r.r#type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/two-factor/webauthn", data = "<data>")]
|
||||
async fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableWebauthnData = data.into_inner().data;
|
||||
async fn activate_webauthn(data: Json<EnableWebauthnData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableWebauthnData = data.into_inner();
|
||||
let mut user = headers.user;
|
||||
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash,
|
||||
Otp: data.Otp,
|
||||
master_password_hash: data.master_password_hash,
|
||||
otp: data.otp,
|
||||
}
|
||||
.validate(&user, true, &mut conn)
|
||||
.await?;
|
||||
@@ -274,13 +262,13 @@ async fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Header
|
||||
|
||||
// Verify the credentials with the saved state
|
||||
let (credential, _data) =
|
||||
WebauthnConfig::load().register_credential(&data.DeviceResponse.into(), &state, |_| Ok(false))?;
|
||||
WebauthnConfig::load().register_credential(&data.device_response.into(), &state, |_| Ok(false))?;
|
||||
|
||||
let mut registrations: Vec<_> = get_webauthn_registrations(&user.uuid, &mut conn).await?.1;
|
||||
// TODO: Check for repeated ID's
|
||||
registrations.push(WebauthnRegistration {
|
||||
id: data.Id.into_i32()?,
|
||||
name: data.Name,
|
||||
id: data.id.into_i32()?,
|
||||
name: data.name,
|
||||
migrated: false,
|
||||
|
||||
credential,
|
||||
@@ -296,28 +284,28 @@ async fn activate_webauthn(data: JsonUpcase<EnableWebauthnData>, headers: Header
|
||||
|
||||
let keys_json: Vec<Value> = registrations.iter().map(WebauthnRegistration::to_json).collect();
|
||||
Ok(Json(json!({
|
||||
"Enabled": true,
|
||||
"Keys": keys_json,
|
||||
"Object": "twoFactorU2f"
|
||||
"enabled": true,
|
||||
"keys": keys_json,
|
||||
"object": "twoFactorU2f"
|
||||
})))
|
||||
}
|
||||
|
||||
#[put("/two-factor/webauthn", data = "<data>")]
|
||||
async fn activate_webauthn_put(data: JsonUpcase<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn activate_webauthn_put(data: Json<EnableWebauthnData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
activate_webauthn(data, headers, conn).await
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct DeleteU2FData {
|
||||
Id: NumberOrString,
|
||||
MasterPasswordHash: String,
|
||||
id: NumberOrString,
|
||||
master_password_hash: String,
|
||||
}
|
||||
|
||||
#[delete("/two-factor/webauthn", data = "<data>")]
|
||||
async fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let id = data.data.Id.into_i32()?;
|
||||
if !headers.user.check_valid_password(&data.data.MasterPasswordHash) {
|
||||
async fn delete_webauthn(data: Json<DeleteU2FData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let id = data.id.into_i32()?;
|
||||
if !headers.user.check_valid_password(&data.master_password_hash) {
|
||||
err!("Invalid password");
|
||||
}
|
||||
|
||||
@@ -358,9 +346,9 @@ async fn delete_webauthn(data: JsonUpcase<DeleteU2FData>, headers: Headers, mut
|
||||
let keys_json: Vec<Value> = data.iter().map(WebauthnRegistration::to_json).collect();
|
||||
|
||||
Ok(Json(json!({
|
||||
"Enabled": true,
|
||||
"Keys": keys_json,
|
||||
"Object": "twoFactorU2f"
|
||||
"enabled": true,
|
||||
"keys": keys_json,
|
||||
"object": "twoFactorU2f"
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -413,8 +401,8 @@ pub async fn validate_webauthn_login(user_uuid: &str, response: &str, conn: &mut
|
||||
),
|
||||
};
|
||||
|
||||
let rsp: crate::util::UpCase<PublicKeyCredentialCopy> = serde_json::from_str(response)?;
|
||||
let rsp: PublicKeyCredential = rsp.data.into();
|
||||
let rsp: PublicKeyCredentialCopy = serde_json::from_str(response)?;
|
||||
let rsp: PublicKeyCredential = rsp.into();
|
||||
|
||||
let mut registrations = get_webauthn_registrations(user_uuid, conn).await?.1;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use yubico::{config::Config, verify_async};
|
||||
use crate::{
|
||||
api::{
|
||||
core::{log_user_event, two_factor::_generate_recover_code},
|
||||
EmptyResult, JsonResult, JsonUpcase, PasswordOrOtpData,
|
||||
EmptyResult, JsonResult, PasswordOrOtpData,
|
||||
},
|
||||
auth::Headers,
|
||||
db::{
|
||||
@@ -21,28 +21,30 @@ pub fn routes() -> Vec<Route> {
|
||||
routes![generate_yubikey, activate_yubikey, activate_yubikey_put,]
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EnableYubikeyData {
|
||||
Key1: Option<String>,
|
||||
Key2: Option<String>,
|
||||
Key3: Option<String>,
|
||||
Key4: Option<String>,
|
||||
Key5: Option<String>,
|
||||
Nfc: bool,
|
||||
MasterPasswordHash: Option<String>,
|
||||
Otp: Option<String>,
|
||||
key1: Option<String>,
|
||||
key2: Option<String>,
|
||||
key3: Option<String>,
|
||||
key4: Option<String>,
|
||||
key5: Option<String>,
|
||||
nfc: bool,
|
||||
master_password_hash: Option<String>,
|
||||
otp: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct YubikeyMetadata {
|
||||
Keys: Vec<String>,
|
||||
pub Nfc: bool,
|
||||
#[serde(rename = "keys", alias = "Keys")]
|
||||
keys: Vec<String>,
|
||||
#[serde(rename = "nfc", alias = "Nfc")]
|
||||
pub nfc: bool,
|
||||
}
|
||||
|
||||
fn parse_yubikeys(data: &EnableYubikeyData) -> Vec<String> {
|
||||
let data_keys = [&data.Key1, &data.Key2, &data.Key3, &data.Key4, &data.Key5];
|
||||
let data_keys = [&data.key1, &data.key2, &data.key3, &data.key4, &data.key5];
|
||||
|
||||
data_keys.iter().filter_map(|e| e.as_ref().cloned()).collect()
|
||||
}
|
||||
@@ -81,11 +83,11 @@ async fn verify_yubikey_otp(otp: String) -> EmptyResult {
|
||||
}
|
||||
|
||||
#[post("/two-factor/get-yubikey", data = "<data>")]
|
||||
async fn generate_yubikey(data: JsonUpcase<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
async fn generate_yubikey(data: Json<PasswordOrOtpData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
// Make sure the credentials are set
|
||||
get_yubico_credentials()?;
|
||||
|
||||
let data: PasswordOrOtpData = data.into_inner().data;
|
||||
let data: PasswordOrOtpData = data.into_inner();
|
||||
let user = headers.user;
|
||||
|
||||
data.validate(&user, false, &mut conn).await?;
|
||||
@@ -98,29 +100,29 @@ async fn generate_yubikey(data: JsonUpcase<PasswordOrOtpData>, headers: Headers,
|
||||
if let Some(r) = r {
|
||||
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(&r.data)?;
|
||||
|
||||
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
||||
let mut result = jsonify_yubikeys(yubikey_metadata.keys);
|
||||
|
||||
result["Enabled"] = Value::Bool(true);
|
||||
result["Nfc"] = Value::Bool(yubikey_metadata.Nfc);
|
||||
result["Object"] = Value::String("twoFactorU2f".to_owned());
|
||||
result["enabled"] = Value::Bool(true);
|
||||
result["nfc"] = Value::Bool(yubikey_metadata.nfc);
|
||||
result["object"] = Value::String("twoFactorU2f".to_owned());
|
||||
|
||||
Ok(Json(result))
|
||||
} else {
|
||||
Ok(Json(json!({
|
||||
"Enabled": false,
|
||||
"Object": "twoFactorU2f",
|
||||
"enabled": false,
|
||||
"object": "twoFactorU2f",
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/two-factor/yubikey", data = "<data>")]
|
||||
async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableYubikeyData = data.into_inner().data;
|
||||
async fn activate_yubikey(data: Json<EnableYubikeyData>, headers: Headers, mut conn: DbConn) -> JsonResult {
|
||||
let data: EnableYubikeyData = data.into_inner();
|
||||
let mut user = headers.user;
|
||||
|
||||
PasswordOrOtpData {
|
||||
MasterPasswordHash: data.MasterPasswordHash.clone(),
|
||||
Otp: data.Otp.clone(),
|
||||
master_password_hash: data.master_password_hash.clone(),
|
||||
otp: data.otp.clone(),
|
||||
}
|
||||
.validate(&user, true, &mut conn)
|
||||
.await?;
|
||||
@@ -136,8 +138,8 @@ async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers,
|
||||
|
||||
if yubikeys.is_empty() {
|
||||
return Ok(Json(json!({
|
||||
"Enabled": false,
|
||||
"Object": "twoFactorU2f",
|
||||
"enabled": false,
|
||||
"object": "twoFactorU2f",
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -154,8 +156,8 @@ async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers,
|
||||
let yubikey_ids: Vec<String> = yubikeys.into_iter().map(|x| (x[..12]).to_owned()).collect();
|
||||
|
||||
let yubikey_metadata = YubikeyMetadata {
|
||||
Keys: yubikey_ids,
|
||||
Nfc: data.Nfc,
|
||||
keys: yubikey_ids,
|
||||
nfc: data.nfc,
|
||||
};
|
||||
|
||||
yubikey_data.data = serde_json::to_string(&yubikey_metadata).unwrap();
|
||||
@@ -165,17 +167,17 @@ async fn activate_yubikey(data: JsonUpcase<EnableYubikeyData>, headers: Headers,
|
||||
|
||||
log_user_event(EventType::UserUpdated2fa as i32, &user.uuid, headers.device.atype, &headers.ip.ip, &mut conn).await;
|
||||
|
||||
let mut result = jsonify_yubikeys(yubikey_metadata.Keys);
|
||||
let mut result = jsonify_yubikeys(yubikey_metadata.keys);
|
||||
|
||||
result["Enabled"] = Value::Bool(true);
|
||||
result["Nfc"] = Value::Bool(yubikey_metadata.Nfc);
|
||||
result["Object"] = Value::String("twoFactorU2f".to_owned());
|
||||
result["enabled"] = Value::Bool(true);
|
||||
result["nfc"] = Value::Bool(yubikey_metadata.nfc);
|
||||
result["object"] = Value::String("twoFactorU2f".to_owned());
|
||||
|
||||
Ok(Json(result))
|
||||
}
|
||||
|
||||
#[put("/two-factor/yubikey", data = "<data>")]
|
||||
async fn activate_yubikey_put(data: JsonUpcase<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
async fn activate_yubikey_put(data: Json<EnableYubikeyData>, headers: Headers, conn: DbConn) -> JsonResult {
|
||||
activate_yubikey(data, headers, conn).await
|
||||
}
|
||||
|
||||
@@ -187,7 +189,7 @@ pub async fn validate_yubikey_login(response: &str, twofactor_data: &str) -> Emp
|
||||
let yubikey_metadata: YubikeyMetadata = serde_json::from_str(twofactor_data).expect("Can't parse Yubikey Metadata");
|
||||
let response_id = &response[..12];
|
||||
|
||||
if !yubikey_metadata.Keys.contains(&response_id.to_owned()) {
|
||||
if !yubikey_metadata.keys.contains(&response_id.to_owned()) {
|
||||
err!("Given Yubikey is not registered");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user