yo es un dumbass

This commit is contained in:
c0repwn3r 2023-04-03 13:28:12 -04:00
parent eb60011ebd
commit f7bd45dff3
Signed by: core
GPG Key ID: FDBF740DADDCEECF
14 changed files with 348 additions and 8 deletions

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="trifidapi@localhost" uuid="39c81b89-3fc4-493f-b203-7a00527cffe6">
<data-source source="LOCAL" name="trifid@localhost" uuid="39c81b89-3fc4-493f-b203-7a00527cffe6">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://localhost:5432/trifidapi</jdbc-url>
<jdbc-url>jdbc:postgresql://localhost:5432/trifid</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>

View File

@ -6,6 +6,8 @@
<sourceFolder url="file://$MODULE_DIR$/trifid-api/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/trifid-pki/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/dnapi-rs/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/trifid-api/trifid_api_entities/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/trifid-api/trifid_api_migration/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />

105
Cargo.lock generated
View File

@ -206,6 +206,16 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "ahash"
version = "0.7.6"
@ -669,6 +679,30 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "chacha20poly1305"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
"cipher",
"poly1305",
"zeroize",
]
[[package]]
name = "chrono"
version = "0.4.24"
@ -685,6 +719,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
"zeroize",
]
[[package]]
name = "clap"
version = "3.2.23"
@ -877,6 +922,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"typenum",
]
@ -1069,7 +1115,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"trifid-pki",
"trifid-pki 0.1.9",
"url",
]
@ -1602,6 +1648,15 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -1920,6 +1975,12 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.45"
@ -2139,6 +2200,17 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "poly1305"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@ -3128,7 +3200,7 @@ dependencies = [
"tar",
"tempfile",
"toml 0.7.3",
"trifid-pki",
"trifid-pki 0.1.9",
"url",
]
@ -3401,6 +3473,7 @@ version = "0.1.0"
dependencies = [
"actix-request-identifier",
"actix-web",
"chacha20poly1305",
"hex",
"log",
"once_cell",
@ -3410,6 +3483,7 @@ dependencies = [
"simple_logger",
"toml 0.7.3",
"totp-rs",
"trifid-pki 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"trifid_api_entities",
"trifid_api_migration",
]
@ -3430,6 +3504,23 @@ dependencies = [
"x25519-dalek",
]
[[package]]
name = "trifid-pki"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20f0b2d11a5219f67b42a0af7f07b10f1c6c1ea57f8cb9f1115d394176b64c6b"
dependencies = [
"ed25519-dalek",
"hex",
"ipnet",
"pem",
"quick-protobuf",
"rand",
"rand_core 0.6.4",
"sha2",
"x25519-dalek",
]
[[package]]
name = "trifid_api_entities"
version = "0.1.0"
@ -3503,6 +3594,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "universal-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.7"

View File

@ -23,4 +23,7 @@ trifid_api_entities = { version = "0.1.0", path = "trifid_api_entities" }
rand = "0.8" # Misc.
hex = "0.4" # Misc.
totp-rs = { version = "5.0.1", features = ["gen_secret", "otpauth"] } # Misc.
totp-rs = { version = "5.0.1", features = ["gen_secret", "otpauth"] } # Misc.
trifid-pki = { version = "0.1.9" } # Cryptography
chacha20poly1305 = "0.10.1" # Cryptography

View File

@ -67,6 +67,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
.service(routes::v1::auth::verify_magic_link::verify_magic_link_request)
.service(routes::v1::totp_authenticators::totp_authenticators_request)
.service(routes::v1::verify_totp_authenticators::verify_totp_authenticators_request)
.service(routes::v1::auth::totp::totp_request)
}).bind(CONFIG.server.bind)?.run().await?;
Ok(())

View File

@ -1,2 +1,3 @@
pub mod magic_link;
pub mod verify_magic_link;
pub mod verify_magic_link;
pub mod totp;

View File

@ -0,0 +1,169 @@
use actix_web::{HttpRequest, HttpResponse, post};
use actix_web::web::{Data, Json};
use log::{debug, error};
use serde::{Serialize, Deserialize};
use trifid_api_entities::entity::totp_authenticator;
use crate::AppState;
use crate::auth_tokens::{enforce_session, TokenInfo};
use crate::error::{APIError, APIErrorsResponse};
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, IntoActiveModel, ActiveModelTrait};
use sea_orm::ActiveValue::Set;
use totp_rs::{Algorithm, Secret, TOTP};
use trifid_api_entities::entity::auth_token;
use crate::config::CONFIG;
use crate::timers::expires_in_seconds;
use crate::tokens::random_token;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TotpRequest {
pub code: String
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TotpResponse {
pub data: TotpResponseData,
pub metadata: TotpResponseMetadata
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TotpResponseData {
#[serde(rename = "authToken")]
pub auth_token: String
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TotpResponseMetadata {}
#[post("/v1/auth/totp")]
pub async fn totp_request(req: Json<TotpRequest>, req_data: HttpRequest, db: Data<AppState>) -> HttpResponse {
// require a user session
let session_token = match enforce_session(&req_data, &db.conn).await {
Ok(r) => {
match r {
TokenInfo::SessionToken(i) => i,
_ => unreachable!()
}
}
Err(e) => {
error!("error enforcing session: {}", e);
return HttpResponse::Unauthorized().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_UNAUTHORIZED".to_string(),
message: "Unauthorized".to_string(),
path: None,
}
],
});
}
};
// determine if the user has a totp authenticator
let auther = match totp_authenticator::Entity::find().filter(totp_authenticator::Column::User.eq(&session_token.user.id)).one(&db.conn).await {
Ok(r) => r,
Err(e) => {
error!("database error: {}", e);
return HttpResponse::InternalServerError().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_DB_ERROR".to_string(),
message: "There was an error with the database request, please try again later.".to_string(),
path: None,
}
],
});
}
};
let auther = match auther {
Some(a) => {
a
},
None => {
return HttpResponse::BadRequest().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_USER_NO_TOTP".to_string(),
message: "This user does not have a totp authenticator".to_string(),
path: None,
}
]
});
}
};
let secret = Secret::Encoded(auther.secret.clone());
let totpmachine = match TOTP::from_url(auther.url.clone()) {
Ok(m) => m,
Err(e) => {
error!("totp url error: {}", e);
return HttpResponse::InternalServerError().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_SECRET_ERROR".to_string(),
message: "There was an error parsing the totpmachine. Please try again later.".to_string(),
path: None,
}
],
});
}
};
let valid = match totpmachine.check_current(&req.code) {
Ok(valid) => valid,
Err(e) => {
error!("system time error: {}", e);
return HttpResponse::InternalServerError().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_TIME_ERROR".to_string(),
message: "There was an with the server-side time clock.".to_string(),
path: None,
}
],
});
}
};
if !valid {
debug!("current: {}", totpmachine.generate_current().unwrap());
error!("user send invalid totp code");
return HttpResponse::Unauthorized().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_UNAUTHORIZED".to_string(),
message: "Unauthorized".to_string(),
path: None,
}
],
})
}
let model: auth_token::Model = auth_token::Model {
id: random_token("auth"),
user: session_token.user.id,
expires_on: expires_in_seconds(CONFIG.tokens.mfa_tokens_expiry_time_seconds) as i64,
};
let token = model.id.clone();
let active_model = model.into_active_model();
match active_model.insert(&db.conn).await {
Ok(_) => (),
Err(e) => {
error!("database error: {}", e);
return HttpResponse::InternalServerError().json(APIErrorsResponse {
errors: vec![
APIError {
code: "ERR_DB_ERROR".to_string(),
message: "There was an error issuing the authentication token.".to_string(),
path: None,
}
],
});
}
}
HttpResponse::Ok().json(TotpResponse {
data: TotpResponseData { auth_token: token },
metadata: TotpResponseMetadata {},
})
}

View File

@ -66,7 +66,7 @@ pub async fn verify_magic_link_request(db: Data<AppState>, req: Json<VerifyMagic
}
};
if !expired(link.expires_on as u64) {
if expired(link.expires_on as u64) {
return HttpResponse::Unauthorized().json(APIErrorsResponse {
errors: vec![
APIError {

View File

@ -61,7 +61,7 @@ pub async fn verify_totp_authenticators_request(req: Json<VerifyTotpAuthenticato
};
// determine if the user has a totp authenticator
let auther = match totp_authenticator::Entity::find().filter(totp_authenticator::Column::User.eq(&session_token.user.id)).one(&db.conn).await {
let auther = match totp_authenticator::Entity::find().filter(totp_authenticator::Column::Id.eq(&req.totp_token)).one(&db.conn).await {
Ok(r) => r,
Err(e) => {
error!("database error: {}", e);

View File

@ -9,5 +9,6 @@ pub mod magic_link;
pub mod network;
pub mod organization;
pub mod session_token;
pub mod signing_ca;
pub mod totp_authenticator;
pub mod user;

View File

@ -7,5 +7,6 @@ pub use super::magic_link::Entity as MagicLink;
pub use super::network::Entity as Network;
pub use super::organization::Entity as Organization;
pub use super::session_token::Entity as SessionToken;
pub use super::signing_ca::Entity as SigningCa;
pub use super::totp_authenticator::Entity as TotpAuthenticator;
pub use super::user::Entity as User;

View File

@ -0,0 +1,22 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "signing_ca")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
pub organization: String,
pub cert: String,
#[sea_orm(unique)]
pub key: String,
pub expires: i64,
#[sea_orm(unique)]
pub nonce: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -11,6 +11,7 @@ pub mod m20230402_233043_create_table_api_keys;
pub mod m20230402_233047_create_table_api_keys_scopes;
mod m20230402_234025_create_table_totp_authenticators;
mod m20230403_002256_create_table_auth_tokens;
mod m20230403_142517_create_table_signing_cas;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
@ -25,6 +26,7 @@ impl MigratorTrait for Migrator {
Box::new(m20230402_233047_create_table_api_keys_scopes::Migration),
Box::new(m20230402_234025_create_table_totp_authenticators::Migration),
Box::new(m20230403_002256_create_table_auth_tokens::Migration),
Box::new(m20230403_142517_create_table_signing_cas::Migration),
]
}
}

View File

@ -0,0 +1,37 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.create_table(
Table::create()
.table(SigningCA::Table)
.col(ColumnDef::new(SigningCA::Id).string().not_null().primary_key())
.col(ColumnDef::new(SigningCA::Organization).string().not_null())
.col(ColumnDef::new(SigningCA::Cert).string().not_null())
.col(ColumnDef::new(SigningCA::Key).string().not_null().unique_key())
.col(ColumnDef::new(SigningCA::Expires).big_integer().not_null())
.col(ColumnDef::new(SigningCA::Nonce).string().not_null().unique_key())
.to_owned()
).await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager.drop_table(Table::drop().table(SigningCA::Table).to_owned()).await
}
}
/// Learn more at https://docs.rs/sea-query#iden
#[derive(Iden)]
pub enum SigningCA {
Table,
Id,
Organization,
Cert,
Key,
Expires,
Nonce
}