whoa, i forgot to commit and made another ton of stuff
This commit is contained in:
parent
1453509dc4
commit
1e66d14710
|
@ -465,6 +465,12 @@ dependencies = [
|
|||
"syn 1.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base32"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
|
@ -788,6 +794,12 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13418e745008f7349ec7e449155f419a61b92b58a99cc3616942b926825ec76b"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -3313,6 +3325,22 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "totp-rs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "332e333b188e843cb4cc477b2911160a533bcfc6e9e488d7bef25011f9e2ba1b"
|
||||
dependencies = [
|
||||
"base32",
|
||||
"constant_time_eq",
|
||||
"hmac",
|
||||
"rand",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"url",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
|
@ -3381,6 +3409,7 @@ dependencies = [
|
|||
"serde",
|
||||
"simple_logger",
|
||||
"toml 0.7.3",
|
||||
"totp-rs",
|
||||
"trifid_api_entities",
|
||||
"trifid_api_migration",
|
||||
]
|
||||
|
@ -3497,6 +3526,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.0"
|
||||
|
|
|
@ -21,5 +21,6 @@ sea-orm = { version = "^0", features = [ "sqlx-postgres", "runtime-actix-rustls"
|
|||
trifid_api_migration = { version = "0.1.0", path = "trifid_api_migration" } # Database
|
||||
trifid_api_entities = { version = "0.1.0", path = "trifid_api_entities" } # Database
|
||||
|
||||
rand = "0.8" # Misc.
|
||||
hex = "0.4" # Misc.
|
||||
rand = "0.8" # Misc.
|
||||
hex = "0.4" # Misc.
|
||||
totp-rs = { version = "5.0.1", features = ["gen_secret", "otpauth"] } # Misc.
|
|
@ -0,0 +1,136 @@
|
|||
use std::error::Error;
|
||||
use actix_web::HttpRequest;
|
||||
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter};
|
||||
use crate::tokens::get_token_type;
|
||||
use trifid_api_entities::entity::{auth_token, session_token};
|
||||
use trifid_api_entities::entity::api_key;
|
||||
use trifid_api_entities::entity::api_key_scope;
|
||||
use trifid_api_entities::entity::user;
|
||||
use crate::timers::expired;
|
||||
|
||||
pub enum TokenInfo {
|
||||
SessionToken(SessionTokenInfo),
|
||||
AuthToken(AuthTokenInfo),
|
||||
ApiToken(ApiTokenInfo)
|
||||
}
|
||||
|
||||
pub struct SessionTokenInfo {
|
||||
pub token: String,
|
||||
pub user: SessionTokenUser,
|
||||
pub expires_at: i64
|
||||
}
|
||||
|
||||
pub struct SessionTokenUser {
|
||||
pub id: String,
|
||||
pub email: String
|
||||
}
|
||||
|
||||
pub struct ApiTokenInfo {
|
||||
pub scopes: Vec<String>,
|
||||
pub organization: String
|
||||
}
|
||||
|
||||
pub struct AuthTokenInfo {
|
||||
pub token: String,
|
||||
pub session_info: SessionTokenInfo
|
||||
}
|
||||
|
||||
pub async fn enforce_session(req: &HttpRequest, db: &DatabaseConnection) -> Result<TokenInfo, Box<dyn Error>> {
|
||||
let header = req.headers().get("Authorization").ok_or("Missing authorization header")?;
|
||||
let authorization = header.to_str()?;
|
||||
|
||||
let authorization_split: Vec<&str> = authorization.split(' ').collect();
|
||||
if authorization_split[0] != "Bearer" {
|
||||
return Err("Not a bearer token".into())
|
||||
}
|
||||
let tokens = &authorization_split[1..];
|
||||
|
||||
let sess_token = tokens.iter().find(|i| get_token_type(**i).unwrap_or("n-sess") == "sess").copied().ok_or("Missing session token")?;
|
||||
|
||||
let token: session_token::Model = session_token::Entity::find().filter(session_token::Column::Id.eq(sess_token)).one(db).await?.ok_or("Invalid session token")?;
|
||||
|
||||
if expired(token.expires_on as u64) {
|
||||
return Err("Token expired".into());
|
||||
}
|
||||
|
||||
let user: user::Model = user::Entity::find().filter(user::Column::Id.eq(token.user)).one(db).await?.ok_or("Session token has a nonexistent user")?;
|
||||
|
||||
Ok(TokenInfo::SessionToken(SessionTokenInfo {
|
||||
token: token.id,
|
||||
user: SessionTokenUser {
|
||||
id: user.id,
|
||||
email: user.email
|
||||
},
|
||||
expires_at: token.expires_on,
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn enforce_2fa(req: &HttpRequest, db: &DatabaseConnection) -> Result<TokenInfo, Box<dyn Error>> {
|
||||
let session_data = match enforce_session(req, db).await? {
|
||||
TokenInfo::SessionToken(i) => i,
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
let header = req.headers().get("Authorization").ok_or("Missing authorization header")?;
|
||||
let authorization = header.to_str()?;
|
||||
|
||||
let authorization_split: Vec<&str> = authorization.split(' ').collect();
|
||||
if authorization_split[0] != "Bearer" {
|
||||
return Err("Not a bearer token".into())
|
||||
}
|
||||
let tokens = &authorization_split[1..];
|
||||
|
||||
let auth_token = tokens.iter().find(|i| get_token_type(**i).unwrap_or("n-auth") == "auth").copied().ok_or("Missing auth token")?;
|
||||
|
||||
let token: auth_token::Model = auth_token::Entity::find().filter(auth_token::Column::Id.eq(auth_token)).one(db).await?.ok_or("Invalid session token")?;
|
||||
|
||||
if expired(token.expires_on as u64) {
|
||||
return Err("Token expired".into());
|
||||
}
|
||||
|
||||
Ok(TokenInfo::AuthToken(AuthTokenInfo {
|
||||
token: token.id,
|
||||
session_info: session_data,
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn enforce_api_token(req: &HttpRequest, scopes: &[&str], db: &DatabaseConnection) -> Result<TokenInfo, Box<dyn Error>> {
|
||||
let header = req.headers().get("Authorization").ok_or("Missing authorization header")?;
|
||||
let authorization = header.to_str()?;
|
||||
|
||||
let authorization_split: Vec<&str> = authorization.split(' ').collect();
|
||||
if authorization_split[0] != "Bearer" {
|
||||
return Err("Not a bearer token".into())
|
||||
}
|
||||
let tokens = &authorization_split[1..];
|
||||
|
||||
let api_token = tokens.iter().find(|i| get_token_type(**i).unwrap_or("n-tfkey") == "tfkey").copied().ok_or("Missing api token")?;
|
||||
|
||||
// API tokens are special and have a different form than other keys.
|
||||
// They follow the form:
|
||||
// tfkey-[ID]-[TOKEN]
|
||||
|
||||
let api_token_split: Vec<&str> = api_token.split('-').collect();
|
||||
if api_token_split.len() != 3 {
|
||||
return Err("API token is missing key".into());
|
||||
}
|
||||
let token_id = format!("{}-{}", api_token_split[0], api_token_split[1]);
|
||||
let token_key = api_token_split[2].to_string();
|
||||
|
||||
let token: api_key::Model = api_key::Entity::find().filter(
|
||||
Condition::all().add(api_key::Column::Id.eq(token_id)).add(api_key::Column::Key.eq(token_key))
|
||||
).one(db).await?.ok_or("Invalid api token")?;
|
||||
let token_scopes: Vec<api_key_scope::Model> = api_key_scope::Entity::find().filter(api_key_scope::Column::ApiKey.eq(api_token)).all(db).await?;
|
||||
let token_scopes: Vec<&str> = token_scopes.iter().map(|i| i.scope.as_str()).collect();
|
||||
|
||||
for scope in scopes {
|
||||
if !token_scopes.contains(scope) {
|
||||
return Err(format!("API token is missing scope {}", scope).into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(TokenInfo::ApiToken(ApiTokenInfo {
|
||||
scopes: token_scopes.iter().map(|i| i.to_string()).collect(),
|
||||
organization: token.organization,
|
||||
}))
|
||||
}
|
|
@ -59,7 +59,9 @@ pub struct TrifidConfigTokens {
|
|||
#[serde(default = "magic_link_expiry_time")]
|
||||
pub magic_link_expiry_time_seconds: u64,
|
||||
#[serde(default = "session_token_expiry_time")]
|
||||
pub session_token_expiry_time_seconds: u64
|
||||
pub session_token_expiry_time_seconds: u64,
|
||||
#[serde(default = "totp_setup_timeout_time")]
|
||||
pub totp_setup_timeout_time_seconds: u64
|
||||
}
|
||||
|
||||
fn max_connections_default() -> u32 { 100 }
|
||||
|
@ -69,3 +71,4 @@ fn sqlx_logging_default() -> bool { true }
|
|||
fn socketaddr_8080() -> SocketAddr { SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0, 0, 0, 0]), 8080)) }
|
||||
fn magic_link_expiry_time() -> u64 { 3600 } // 1 hour
|
||||
fn session_token_expiry_time() -> u64 { 15780000 } // 6 months
|
||||
fn totp_setup_timeout_time() -> u64 { 600 } // 10 minutes
|
|
@ -16,6 +16,7 @@ pub mod error;
|
|||
pub mod tokens;
|
||||
pub mod timers;
|
||||
pub mod magic_link;
|
||||
pub mod auth_tokens;
|
||||
|
||||
pub struct AppState {
|
||||
pub conn: DatabaseConnection
|
||||
|
@ -64,6 +65,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
.service(routes::v1::auth::magic_link::magic_link_request)
|
||||
.service(routes::v1::signup::signup_request)
|
||||
.service(routes::v1::auth::verify_magic_link::verify_magic_link_request)
|
||||
.service(routes::v1::totp_authenticators::totp_authenticators_request)
|
||||
}).bind(CONFIG.server.bind)?.run().await?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod auth;
|
||||
pub mod signup;
|
||||
pub mod totp_authenticators;
|
|
@ -0,0 +1,141 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
use actix_web::{HttpRequest, HttpResponse, post};
|
||||
use actix_web::web::{Data, Json};
|
||||
use log::error;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
|
||||
use totp_rs::{Algorithm, Secret, TOTP};
|
||||
use crate::AppState;
|
||||
use crate::auth_tokens::{enforce_2fa, enforce_session, TokenInfo};
|
||||
use crate::error::{APIError, APIErrorsResponse};
|
||||
use trifid_api_entities::entity::totp_authenticator;
|
||||
use crate::config::CONFIG;
|
||||
use crate::timers::expires_in_seconds;
|
||||
use crate::tokens::random_id;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TotpAuthenticatorsRequest {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TotpAuthenticatorsResponseMetadata {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TotpAuthenticatorsResponseData {
|
||||
#[serde(rename = "totpToken")]
|
||||
pub totp_token: String,
|
||||
pub secret: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct TotpAuthenticatorsResponse {
|
||||
pub data: TotpAuthenticatorsResponseData,
|
||||
pub metadata: TotpAuthenticatorsResponseMetadata,
|
||||
}
|
||||
|
||||
#[post("/v1/totp-authenticators")]
|
||||
pub async fn totp_authenticators_request(db: Data<AppState>, req_data: HttpRequest, req: Json<TotpAuthenticatorsRequest>) -> 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,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
if auther.is_some() {
|
||||
return HttpResponse::BadRequest().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_ALREADY_HAS_TOTP".to_string(),
|
||||
message: "This user already has a totp authenticator".to_string(),
|
||||
path: None,
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
let secret = Secret::generate_secret();
|
||||
let totpmachine = match TOTP::new(Algorithm::SHA1, 6, 1, 30, secret.to_bytes().expect("Invalid randomized data"), Some("trifid-api".to_string()), session_token.user.email) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("totp machine create error: {}", e);
|
||||
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_SECRET_ERR".to_string(),
|
||||
message: "There was an error configuring the authenticator, please try again later.".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let model = totp_authenticator::Model {
|
||||
id: random_id("totp"),
|
||||
secret: Secret::Raw(totpmachine.secret.clone()).to_encoded().to_string(),
|
||||
url: totpmachine.get_url(),
|
||||
verified: false,
|
||||
expires_on: expires_in_seconds(CONFIG.tokens.totp_setup_timeout_time_seconds) as i64,
|
||||
user: session_token.user.id,
|
||||
};
|
||||
let id = model.id.clone();
|
||||
let secret = model.secret.clone();
|
||||
let url = model.url.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 with the database request, please try again later.".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
HttpResponse::Ok().json(TotpAuthenticatorsResponse {
|
||||
data: TotpAuthenticatorsResponseData {
|
||||
totp_token: id,
|
||||
secret,
|
||||
url,
|
||||
},
|
||||
metadata: TotpAuthenticatorsResponseMetadata {},
|
||||
})
|
||||
}
|
|
@ -31,3 +31,7 @@ fn random_with_charset(len: u32, charset: &[u8]) -> String {
|
|||
charset[idx] as char
|
||||
}).collect()
|
||||
}
|
||||
|
||||
pub fn get_token_type(token: &str) -> Option<&str> {
|
||||
token.split('-').collect::<Vec<&str>>().get(0).copied()
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//! `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 = "api_key")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(unique)]
|
||||
pub key: String,
|
||||
pub organization: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::api_key_scope::Entity")]
|
||||
ApiKeyScope,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::organization::Entity",
|
||||
from = "Column::Organization",
|
||||
to = "super::organization::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Organization,
|
||||
}
|
||||
|
||||
impl Related<super::api_key_scope::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ApiKeyScope.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::organization::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Organization.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -0,0 +1,32 @@
|
|||
//! `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 = "api_key_scope")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
pub scope: String,
|
||||
pub api_key: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::api_key::Entity",
|
||||
from = "Column::ApiKey",
|
||||
to = "super::api_key::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
ApiKey,
|
||||
}
|
||||
|
||||
impl Related<super::api_key::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ApiKey.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -0,0 +1,32 @@
|
|||
//! `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 = "auth_token")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
pub user: String,
|
||||
pub expires_on: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::User",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod api_key;
|
||||
pub mod api_key_scope;
|
||||
pub mod auth_token;
|
||||
pub mod magic_link;
|
||||
pub mod network;
|
||||
pub mod organization;
|
||||
pub mod session_token;
|
||||
pub mod totp_authenticator;
|
||||
pub mod user;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
//! `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 = "network")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(unique)]
|
||||
pub organization: String,
|
||||
pub ip_block: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::organization::Entity",
|
||||
from = "Column::Organization",
|
||||
to = "super::organization::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Organization,
|
||||
}
|
||||
|
||||
impl Related<super::organization::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Organization.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -0,0 +1,49 @@
|
|||
//! `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 = "organization")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
#[sea_orm(unique)]
|
||||
pub owner: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::api_key::Entity")]
|
||||
ApiKey,
|
||||
#[sea_orm(has_one = "super::network::Entity")]
|
||||
Network,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::Owner",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::api_key::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ApiKey.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::network::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Network.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -1,5 +1,11 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.2
|
||||
|
||||
pub use super::api_key::Entity as ApiKey;
|
||||
pub use super::api_key_scope::Entity as ApiKeyScope;
|
||||
pub use super::auth_token::Entity as AuthToken;
|
||||
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::totp_authenticator::Entity as TotpAuthenticator;
|
||||
pub use super::user::Entity as User;
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
//! `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 = "totp_authenticator")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: String,
|
||||
#[sea_orm(unique)]
|
||||
pub secret: String,
|
||||
#[sea_orm(unique)]
|
||||
pub url: String,
|
||||
pub verified: bool,
|
||||
pub expires_on: i64,
|
||||
#[sea_orm(unique)]
|
||||
pub user: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::User",
|
||||
to = "super::user::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
|
@ -13,10 +13,22 @@ pub struct Model {
|
|||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::auth_token::Entity")]
|
||||
AuthToken,
|
||||
#[sea_orm(has_many = "super::magic_link::Entity")]
|
||||
MagicLink,
|
||||
#[sea_orm(has_one = "super::organization::Entity")]
|
||||
Organization,
|
||||
#[sea_orm(has_many = "super::session_token::Entity")]
|
||||
SessionToken,
|
||||
#[sea_orm(has_one = "super::totp_authenticator::Entity")]
|
||||
TotpAuthenticator,
|
||||
}
|
||||
|
||||
impl Related<super::auth_token::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AuthToken.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::magic_link::Entity> for Entity {
|
||||
|
@ -25,10 +37,22 @@ impl Related<super::magic_link::Entity> for Entity {
|
|||
}
|
||||
}
|
||||
|
||||
impl Related<super::organization::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Organization.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::session_token::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::SessionToken.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::totp_authenticator::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::TotpAuthenticator.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
|
|
@ -5,6 +5,12 @@ pub struct Migrator;
|
|||
pub mod m20230402_162601_create_table_users;
|
||||
pub mod m20230402_183515_create_table_magic_links;
|
||||
pub mod m20230402_213712_create_table_session_tokens;
|
||||
pub mod m20230402_232316_create_table_organizations;
|
||||
pub mod m20230402_232323_create_table_networks;
|
||||
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;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigratorTrait for Migrator {
|
||||
|
@ -13,6 +19,12 @@ impl MigratorTrait for Migrator {
|
|||
Box::new(m20230402_162601_create_table_users::Migration),
|
||||
Box::new(m20230402_183515_create_table_magic_links::Migration),
|
||||
Box::new(m20230402_213712_create_table_session_tokens::Migration),
|
||||
Box::new(m20230402_232316_create_table_organizations::Migration),
|
||||
Box::new(m20230402_232323_create_table_networks::Migration),
|
||||
Box::new(m20230402_233043_create_table_api_keys::Migration),
|
||||
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),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
use sea_orm_migration::prelude::*;
|
||||
use crate::m20230402_162601_create_table_users::User;
|
||||
|
||||
#[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(Organization::Table)
|
||||
.col(ColumnDef::new(Organization::Id).string().not_null().primary_key())
|
||||
.col(ColumnDef::new(Organization::Name).string().not_null())
|
||||
.col(ColumnDef::new(Organization::Owner).string().not_null().unique_key())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.from(Organization::Table, Organization::Owner)
|
||||
.to(User::Table, User::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
).to_owned()
|
||||
).await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(Organization::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Learn more at https://docs.rs/sea-query#iden
|
||||
#[derive(Iden)]
|
||||
pub enum Organization {
|
||||
Table,
|
||||
Id,
|
||||
Name,
|
||||
Owner
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
use sea_orm_migration::prelude::*;
|
||||
use crate::m20230402_232316_create_table_organizations::Organization;
|
||||
|
||||
#[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(Network::Table)
|
||||
.col(ColumnDef::new(Network::Id).string().not_null().primary_key())
|
||||
.col(ColumnDef::new(Network::Organization).string().not_null().unique_key())
|
||||
.col(ColumnDef::new(Network::IpBlock).string().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.from(Network::Table, Network::Organization)
|
||||
.to(Organization::Table, Organization::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
).to_owned()
|
||||
).await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(Network::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Learn more at https://docs.rs/sea-query#iden
|
||||
#[derive(Iden)]
|
||||
pub enum Network {
|
||||
Table,
|
||||
Id,
|
||||
Organization,
|
||||
IpBlock
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
use sea_orm_migration::prelude::*;
|
||||
use crate::m20230402_232316_create_table_organizations::Organization;
|
||||
|
||||
#[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(ApiKey::Table)
|
||||
.col(ColumnDef::new(ApiKey::Id).string().not_null().primary_key())
|
||||
.col(ColumnDef::new(ApiKey::Key).string().not_null().unique_key())
|
||||
.col(ColumnDef::new(ApiKey::Organization).string().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.from(ApiKey::Table, ApiKey::Organization)
|
||||
.to(Organization::Table, Organization::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
)
|
||||
.to_owned()
|
||||
).await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(ApiKey::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Learn more at https://docs.rs/sea-query#iden
|
||||
#[derive(Iden)]
|
||||
pub enum ApiKey {
|
||||
Table,
|
||||
Id,
|
||||
Key,
|
||||
Organization
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
use sea_orm_migration::prelude::*;
|
||||
use crate::m20230402_233043_create_table_api_keys::ApiKey;
|
||||
|
||||
#[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(ApiKeyScope::Table)
|
||||
.col(ColumnDef::new(ApiKeyScope::Id).string().not_null().primary_key())
|
||||
.col(ColumnDef::new(ApiKeyScope::Scope).string().not_null())
|
||||
.col(ColumnDef::new(ApiKeyScope::ApiKey).string().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.from(ApiKeyScope::Table, ApiKeyScope::ApiKey)
|
||||
.to(ApiKey::Table, ApiKey::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
).to_owned()
|
||||
).await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(ApiKeyScope::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Learn more at https://docs.rs/sea-query#iden
|
||||
#[derive(Iden)]
|
||||
pub enum ApiKeyScope {
|
||||
Table,
|
||||
Id,
|
||||
Scope,
|
||||
ApiKey
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
use sea_orm_migration::prelude::*;
|
||||
use crate::m20230402_162601_create_table_users::User;
|
||||
|
||||
#[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(TotpAuthenticator::Table)
|
||||
.col(ColumnDef::new(TotpAuthenticator::Id).string().not_null().primary_key())
|
||||
.col(ColumnDef::new(TotpAuthenticator::Secret).string().not_null().unique_key())
|
||||
.col(ColumnDef::new(TotpAuthenticator::Url).string().not_null().unique_key())
|
||||
.col(ColumnDef::new(TotpAuthenticator::Verified).boolean().not_null())
|
||||
.col(ColumnDef::new(TotpAuthenticator::ExpiresOn).big_integer().not_null())
|
||||
.col(ColumnDef::new(TotpAuthenticator::User).string().not_null().unique_key())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.from(TotpAuthenticator::Table, TotpAuthenticator::User)
|
||||
.to(User::Table, User::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
).to_owned()
|
||||
).await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(TotpAuthenticator::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Learn more at https://docs.rs/sea-query#iden
|
||||
#[derive(Iden)]
|
||||
pub enum TotpAuthenticator {
|
||||
Table,
|
||||
Id,
|
||||
Secret,
|
||||
Url,
|
||||
Verified,
|
||||
ExpiresOn,
|
||||
User
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
use sea_orm_migration::prelude::*;
|
||||
use crate::m20230402_162601_create_table_users::User;
|
||||
|
||||
#[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(AuthToken::Table)
|
||||
.if_not_exists()
|
||||
.col(ColumnDef::new(AuthToken::Id).string().not_null().primary_key())
|
||||
.col(ColumnDef::new(AuthToken::User).string().not_null())
|
||||
.col(ColumnDef::new(AuthToken::ExpiresOn).big_integer().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.from(AuthToken::Table, AuthToken::User)
|
||||
.to(User::Table, User::Id)
|
||||
.on_delete(ForeignKeyAction::Cascade)
|
||||
.on_update(ForeignKeyAction::Cascade)
|
||||
)
|
||||
.to_owned()
|
||||
).await
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager.drop_table(Table::drop().table(AuthToken::Table).to_owned()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Learn more at https://docs.rs/sea-query#iden
|
||||
#[derive(Iden)]
|
||||
pub enum AuthToken {
|
||||
Table,
|
||||
Id,
|
||||
User,
|
||||
ExpiresOn
|
||||
}
|
Loading…
Reference in New Issue