org work
This commit is contained in:
parent
d00df353d8
commit
bba39afc6b
|
@ -4,6 +4,7 @@
|
|||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/tfclient/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-pki/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
|
|
@ -2205,11 +2205,14 @@ dependencies = [
|
|||
name = "trifid-api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64 0.21.0",
|
||||
"chrono",
|
||||
"dotenvy",
|
||||
"hex",
|
||||
"log",
|
||||
"paste",
|
||||
"rand",
|
||||
"rocket",
|
||||
"serde",
|
||||
"sqlx",
|
||||
|
@ -2221,6 +2224,10 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trifid-pki"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.4"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"trifid-api",
|
||||
"tfclient"
|
||||
"tfclient",
|
||||
"trifid-pki"
|
||||
]
|
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
rustc-wrapper = "sccache"
|
|
@ -19,4 +19,7 @@ totp-rs = { version = "4.2.0", features = ["qr", "otpauth", "gen_secret"]}
|
|||
uuid = { version = "1.3.0", features = ["v4", "fast-rng", "macro-diagnostics"]}
|
||||
url = { version = "2.3.1", features = ["serde"] }
|
||||
urlencoding = "2.1.2"
|
||||
chrono = "0.4.23"
|
||||
chrono = "0.4.23"
|
||||
aes-gcm = "0.10.1"
|
||||
hex = "0.4.3"
|
||||
rand = "0.8.5"
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE organization_authorized_users (
|
||||
id SERIAL NOT NULL PRIMARY KEY,
|
||||
user_id SERIAL NOT NULL REFERENCES users(id),
|
||||
org_id SERIAL NOT NULL REFERENCES organizations(id)
|
||||
);
|
||||
CREATE INDEX idx_organization_authorized_users_user ON organization_authorized_users(user_id);
|
||||
CREATE INDEX idx_organization_authorized_users_org ON organization_authorized_users(org_id);
|
|
@ -0,0 +1,21 @@
|
|||
use std::error::Error;
|
||||
use aes_gcm::{Aes256Gcm, KeyInit, Nonce};
|
||||
use aes_gcm::aead::{Aead, Payload};
|
||||
use crate::config::TFConfig;
|
||||
|
||||
pub fn get_cipher_from_config(config: &TFConfig) -> Result<Aes256Gcm, Box<dyn Error>> {
|
||||
let key_slice = hex::decode(&config.data_key)?;
|
||||
Ok(Aes256Gcm::new_from_slice(&key_slice)?)
|
||||
}
|
||||
|
||||
pub fn encrypt_with_nonce(plaintext: &[u8], nonce: [u8; 12], cipher: &Aes256Gcm) -> Result<Vec<u8>, aes_gcm::Error> {
|
||||
let nonce = Nonce::from_slice(&nonce);
|
||||
let ciphertext = cipher.encrypt(nonce, plaintext)?;
|
||||
Ok(ciphertext)
|
||||
}
|
||||
|
||||
pub fn decrypt_with_nonce(ciphertext: &[u8], nonce: [u8; 12], cipher: &Aes256Gcm) -> Result<Vec<u8>, aes_gcm::Error> {
|
||||
let nonce = Nonce::from_slice(&nonce);
|
||||
let plaintext = cipher.decrypt(nonce, Payload::from(ciphertext))?;
|
||||
Ok(plaintext)
|
||||
}
|
|
@ -19,6 +19,8 @@ pub mod config;
|
|||
pub mod tokens;
|
||||
pub mod routes;
|
||||
pub mod auth;
|
||||
pub mod crypto;
|
||||
pub mod org;
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
|
@ -118,7 +120,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
crate::routes::v1::auth::check_session::options,
|
||||
crate::routes::v1::auth::check_session::options_auth,
|
||||
crate::routes::v2::whoami::whoami_request,
|
||||
crate::routes::v2::whoami::options
|
||||
crate::routes::v2::whoami::options,
|
||||
|
||||
crate::routes::v1::organization::options,
|
||||
crate::routes::v1::organization::orgidoptions,
|
||||
crate::routes::v1::organization::orginfo_req,
|
||||
crate::routes::v1::organization::orglist_req,
|
||||
crate::routes::v1::organization::create_org
|
||||
])
|
||||
.register("/", catchers![
|
||||
crate::routes::handler_400,
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
use std::error::Error;
|
||||
use rocket::form::validate::Contains;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn get_org_by_owner_id(user: i32, db: &PgPool) -> Result<Option<i32>, Box<dyn Error>> {
|
||||
Ok(match sqlx::query!("SELECT id FROM organizations WHERE owner = $1", user).fetch_optional(db).await? {
|
||||
Some(r) => Some(r.id),
|
||||
None => None
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_orgs_by_assoc_id(user: i32, db: &PgPool) -> Result<Vec<i32>, Box<dyn Error>> {
|
||||
let res: Vec<_> = sqlx::query!("SELECT org_id FROM organization_authorized_users WHERE user_id = $1", user).fetch_all(db).await?;
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
for i in res {
|
||||
ret.push(i.org_id);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn get_users_by_assoc_org(org: i32, db: &PgPool) -> Result<Vec<i32>, Box<dyn Error>> {
|
||||
let res: Vec<_> = sqlx::query!("SELECT user_id FROM organization_authorized_users WHERE org_id = $1", org).fetch_all(db).await?;
|
||||
|
||||
let mut ret = vec![];
|
||||
|
||||
for i in res {
|
||||
ret.push(i.org_id);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn get_associated_orgs(user: i32, db: &PgPool) -> Result<Vec<i32>, Box<dyn Error>> {
|
||||
let mut assoc_orgs = vec![];
|
||||
|
||||
if let Some(owned_org) = get_org_by_owner_id(user, db).await? {
|
||||
assoc_orgs.push(owned_org);
|
||||
}
|
||||
|
||||
assoc_orgs.append(&mut get_orgs_by_assoc_id(user, db).await?);
|
||||
|
||||
Ok(assoc_orgs)
|
||||
}
|
||||
|
||||
pub async fn user_has_org_assoc(user: i32, org: i32, db: &PgPool) -> Result<bool, Box<dyn Error>> {
|
||||
Ok(get_associated_orgs(user, db).await?.contains(org))
|
||||
}
|
|
@ -2,4 +2,5 @@ pub mod auth;
|
|||
pub mod signup;
|
||||
|
||||
pub mod totp_authenticators;
|
||||
pub mod verify_totp_authenticator;
|
||||
pub mod verify_totp_authenticator;
|
||||
pub mod organization;
|
|
@ -0,0 +1,84 @@
|
|||
use std::slice::from_raw_parts_mut;
|
||||
use rocket::{get, post, options, State};
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::serde::json::Json;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sqlx::PgPool;
|
||||
use crate::auth::TOTPAuthenticatedUserInfo;
|
||||
use crate::config::TFConfig;
|
||||
use crate::org::{get_associated_orgs, get_org_by_owner_id, get_users_by_assoc_org, user_has_org_assoc};
|
||||
|
||||
#[options("/v1/orgs")]
|
||||
pub fn options() -> &'static str {
|
||||
""
|
||||
}
|
||||
#[options("/v1/org/<_id>")]
|
||||
pub fn orgidoptions(_id: i32) -> &'static str {
|
||||
""
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct OrglistStruct {
|
||||
pub org_ids: Vec<i32>
|
||||
}
|
||||
|
||||
|
||||
#[get("/v1/orgs")]
|
||||
pub async fn orglist_req(user: TOTPAuthenticatedUserInfo, db: &State<PgPool>) -> Result<(ContentType, Json<OrglistStruct>), (Status, String)> {
|
||||
// this endpoint lists the associated organizations this user has access to
|
||||
let associated_orgs = match get_associated_orgs(user.user_id, db.inner()).await {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_DB_QUERY_FAILED", "an error occurred while running the database query")))
|
||||
};
|
||||
|
||||
Ok((ContentType::JSON, Json(OrglistStruct {
|
||||
org_ids: associated_orgs
|
||||
})))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct OrginfoStruct {
|
||||
pub org_id: i32,
|
||||
pub owner_id: i32,
|
||||
pub ca_crt: String,
|
||||
pub authorized_users: Vec<i32>
|
||||
}
|
||||
|
||||
#[get("/v1/org/<orgid>")]
|
||||
pub async fn orginfo_req(orgid: i32, user: TOTPAuthenticatedUserInfo, db: &State<PgPool>) -> Result<(ContentType, Json<OrginfoStruct>), (Status, String)> {
|
||||
if !user_has_org_assoc(orgid, user.user_id, db.inner()).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))? {
|
||||
return Err((Status::Unauthorized, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_MISSING_ORG_AUTHORIZATION", "this user does not have permission to access this org")));
|
||||
}
|
||||
|
||||
let org = sqlx::query!("SELECT id, owner, ca_crt FROM organizations WHERE id = $1", orgid).fetch_one(orgid).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))?;
|
||||
let authorized_users = get_users_by_assoc_org(orgid, db.inner()).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))?;
|
||||
|
||||
Ok((ContentType::JSON, Json(
|
||||
OrginfoStruct {
|
||||
org_id: orgid,
|
||||
owner_id: org.owner,
|
||||
ca_crt: org.ca_crt,
|
||||
authorized_users,
|
||||
}
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/v1/org")]
|
||||
pub async fn create_org(user: TOTPAuthenticatedUserInfo, db: &State<PgPool>, config: &State<TFConfig>) -> Result<(ContentType, Json<OrginfoStruct>), (Status, String)> {
|
||||
if get_org_by_owner_id(user.user_id, db.inner()).await.map_err(|e| (Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))?.is_some() {
|
||||
return Err((Status::Conflict, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", "ERR_USER_OWNS_ORG", "a user can only own one organization at a time")))
|
||||
}
|
||||
|
||||
// we need to generate a keypair and CA certificate for this new org
|
||||
|
||||
Ok((ContentType::JSON, Json(
|
||||
OrginfoStruct {
|
||||
org_id: orgid,
|
||||
owner_id: org.owner,
|
||||
ca_crt: org.ca_crt,
|
||||
authorized_users,
|
||||
}
|
||||
)))
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "trifid-pki"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "A rust implementation of the Nebula PKI system"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
|
@ -0,0 +1,14 @@
|
|||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue