some unfinished database work - /v1/signup works now
This commit is contained in:
parent
a6ea23c32d
commit
c3990486b8
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SqlDialectMappings">
|
<component name="SqlDialectMappings">
|
||||||
<file url="file://$PROJECT_DIR$/trifid-api/migrations/20230203025121_create_users.sql" dialect="GenericSQL" />
|
|
||||||
<file url="PROJECT" dialect="PostgreSQL" />
|
<file url="PROJECT" dialect="PostgreSQL" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -2,6 +2,12 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aead"
|
name = "aead"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -115,6 +121,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base32"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
|
@ -160,6 +172,12 @@ version = "3.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
@ -194,6 +212,18 @@ dependencies = [
|
||||||
"inout",
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.16.2"
|
version = "0.16.2"
|
||||||
|
@ -252,6 +282,15 @@ version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
|
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -405,6 +444,16 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -717,6 +766,20 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.2"
|
version = "1.9.2"
|
||||||
|
@ -858,6 +921,15 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -937,6 +1009,27 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1120,6 +1213,18 @@ version = "0.3.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crc32fast",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyval"
|
name = "polyval"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -1160,6 +1265,12 @@ dependencies = [
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "qrcodegen"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
@ -1866,6 +1977,25 @@ dependencies = [
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "totp-rs"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fdd21080b6cf581e0c8fe849626ad627b42af1a0f71ce980244f2d6b1a47836"
|
||||||
|
dependencies = [
|
||||||
|
"base32",
|
||||||
|
"base64 0.20.0",
|
||||||
|
"constant_time_eq",
|
||||||
|
"hmac",
|
||||||
|
"image",
|
||||||
|
"qrcodegen",
|
||||||
|
"rand",
|
||||||
|
"sha1",
|
||||||
|
"sha2",
|
||||||
|
"url",
|
||||||
|
"urlencoding",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -1947,6 +2077,10 @@ dependencies = [
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.7.1",
|
"toml 0.7.1",
|
||||||
|
"totp-rs",
|
||||||
|
"url",
|
||||||
|
"urlencoding",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2038,6 +2172,35 @@ dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"rand",
|
||||||
|
"uuid-macro-internal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid-macro-internal"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1b300a878652a387d2a0de915bdae8f1a548f0c6d45e072fe2688794b656cc9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
POST /v1/auth/magic-link HTTP/2
|
||||||
|
Host: api.defined.net
|
||||||
|
Content-Length: 29
|
||||||
|
Sec-Ch-Ua: "Chromium";v="109", "Not_A Brand";v="99"
|
||||||
|
Accept: application/json
|
||||||
|
Content-Type: application/json
|
||||||
|
Sec-Ch-Ua-Mobile: ?0
|
||||||
|
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.75 Safari/537.36
|
||||||
|
Sec-Ch-Ua-Platform: "Linux"
|
||||||
|
Origin: https://admin.defined.net
|
||||||
|
Sec-Fetch-Site: same-site
|
||||||
|
Sec-Fetch-Mode: cors
|
||||||
|
Sec-Fetch-Dest: empty
|
||||||
|
Accept-Encoding: gzip, deflate
|
||||||
|
Accept-Language: en-US,en;q=0.9
|
||||||
|
|
||||||
|
{"email":"core@coredoes.dev"}
|
||||||
|
|
||||||
|
HTTP/2 200 OK
|
||||||
|
Access-Control-Allow-Credentials: true
|
||||||
|
Access-Control-Allow-Origin: https://admin.defined.net
|
||||||
|
Access-Control-Expose-Headers: X-Request-Id
|
||||||
|
Cache-Control: no-store
|
||||||
|
Content-Security-Policy: default-src 'none'
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
Strict-Transport-Security: max-age=31536000; includeSubdomains
|
||||||
|
Vary: Origin
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
X-Frame-Options: DENY
|
||||||
|
X-Request-Id: GI4RVV7JQIINE3DDPCYOBAT6TI
|
||||||
|
Content-Length: 26
|
||||||
|
Date: Sat, 04 Feb 2023 17:45:20 GMT
|
||||||
|
|
||||||
|
{"data":{},"metadata":{}}
|
|
@ -14,4 +14,8 @@ tokio = { version = "1", features = ["full"] }
|
||||||
toml = "0.7.1"
|
toml = "0.7.1"
|
||||||
serde = "1.0.152"
|
serde = "1.0.152"
|
||||||
dotenvy = "0.15.6"
|
dotenvy = "0.15.6"
|
||||||
paste = "1.0.11"
|
paste = "1.0.11"
|
||||||
|
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"
|
|
@ -1,2 +1,4 @@
|
||||||
listen_port = 8000
|
listen_port = 8000
|
||||||
db_url = "postgres://postgres@localhost/trifidapi"
|
db_url = "postgres://postgres@localhost/trifidapi"
|
||||||
|
base = "http://localhost:8000"
|
||||||
|
magic_links_valid_for = 86400
|
|
@ -1,6 +0,0 @@
|
||||||
CREATE TABLE organizations (
|
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
|
||||||
network_range VARCHAR(18) NOT NULL, -- https://docs.defined.net/guides/choosing-a-cidr/
|
|
||||||
org_name VARCHAR(258) NOT NULL
|
|
||||||
);
|
|
||||||
CREATE INDEX idx_organizations_name ON organizations(org_name);
|
|
|
@ -1,10 +0,0 @@
|
||||||
CREATE TABLE users (
|
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
|
||||||
email VARCHAR(320) NOT NULL,
|
|
||||||
|
|
||||||
totp_enabled INTEGER NOT NULL,
|
|
||||||
totp_verified INTEGER NOT NULL,
|
|
||||||
totp_base32 VARCHAR(1024) NOT NULL,
|
|
||||||
totp_auth_url VARCHAR(1024) NOT NULL
|
|
||||||
);
|
|
||||||
CREATE INDEX idx_users_email ON users(email);
|
|
|
@ -1,10 +0,0 @@
|
||||||
CREATE TABLE roles (
|
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
|
||||||
|
|
||||||
role_name VARCHAR(128) NOT NULL,
|
|
||||||
role_desc VARCHAR(512) NOT NULL,
|
|
||||||
|
|
||||||
org_id SERIAL NOT NULL REFERENCES organizations(id)
|
|
||||||
);
|
|
||||||
CREATE INDEX idx_roles_org_id ON roles(org_id);
|
|
||||||
-- every org should have an id=0 role which every node is a member of
|
|
|
@ -1,9 +0,0 @@
|
||||||
CREATE TABLE firewall_rules (
|
|
||||||
id SERIAL NOT NULL PRIMARY KEY,
|
|
||||||
|
|
||||||
allow_role_id SERIAL NOT NULL REFERENCES roles(id), -- all roles: use the organization role itself
|
|
||||||
|
|
||||||
protocol INTEGER NOT NULL, -- 0: any, 1: icmp, 2: udp, 3: tcp
|
|
||||||
port VARCHAR(256) NOT NULL, -- port range (ignore if protocol==1)
|
|
||||||
description VARCHAR(256) NOT NULL -- description
|
|
||||||
)
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
CREATE TABLE users (
|
||||||
|
id SERIAL NOT NULL PRIMARY KEY,
|
||||||
|
email VARCHAR(320) NOT NULL UNIQUE,
|
||||||
|
created_on INTEGER NOT NULL,
|
||||||
|
|
||||||
|
banned INTEGER NOT NULL,
|
||||||
|
ban_reason VARCHAR(1024) NOT NULL,
|
||||||
|
|
||||||
|
totp_secret VARCHAR(128) NOT NULL,
|
||||||
|
totp_verified INTEGER NOT NULL,
|
||||||
|
totp_otpurl VARCHAR(3000) NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_users_email ON users(email);
|
|
@ -0,0 +1,5 @@
|
||||||
|
CREATE TABLE magic_links (
|
||||||
|
id VARCHAR(36) NOT NULL PRIMARY KEY UNIQUE,
|
||||||
|
user_id SERIAL NOT NULL REFERENCES users(id),
|
||||||
|
expires_on INTEGER NOT NULL
|
||||||
|
);
|
|
@ -1,7 +1,10 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct TFConfig {
|
pub struct TFConfig {
|
||||||
pub listen_port: u16,
|
pub listen_port: u16,
|
||||||
pub db_url: String
|
pub db_url: String,
|
||||||
|
pub base: Url,
|
||||||
|
pub magic_links_valid_for: i64
|
||||||
}
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use log::info;
|
||||||
|
use sqlx::PgPool;
|
||||||
|
use uuid::Uuid;
|
||||||
|
use crate::config::TFConfig;
|
||||||
|
// https://admin.defined.net/auth/magic-link?email=coredoescode%40gmail.com&token=ml-ckBsgw_5IdK5VYgseBYcoV_v_cQjtdq1re_RhDu_MKg
|
||||||
|
pub async fn send_magic_link(id: i64, email: String, db: &PgPool, config: &TFConfig) -> Result<(), Box<dyn Error>> {
|
||||||
|
let otp = Uuid::new_v4().to_string();
|
||||||
|
let otp_url = format!("{}/{}", config.base, urlencoding::encode(&format!("/auth/magic-link?email={}&token={}", email.clone(), otp.clone())));
|
||||||
|
sqlx::query!("INSERT INTO magic_links (id, user_id, expires_on) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;", otp, id, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64 + config.config.magic_links_valid_for).await?;
|
||||||
|
// TODO: send email
|
||||||
|
info!("sent magic link {} to {}, valid for {} seconds", otp_url, email.clone(), config.magic_links_valid_for);
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ pub mod format;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod email;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||||
|
@ -75,29 +75,19 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
info!("[tfapi] building rocket config");
|
info!("[tfapi] building rocket config");
|
||||||
|
|
||||||
let figment = rocket::Config::figment().merge(("port", config.listen_port));
|
let figment = rocket::Config::figment().merge(("port", config.listen_port));
|
||||||
/*
|
|
||||||
error_handler!(400, "ERR_MALFORMED_REQUEST", "unable to parse the request body, is it properly formatted?");
|
|
||||||
error_handler!(401, "ERR_AUTHENTICATION_REQUIRED", "this endpoint requires authentication but it was not provided");
|
|
||||||
error_handler!(403, "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid");
|
|
||||||
error_handler!(404, "ERR_NOT_FOUND", "resource not found");
|
|
||||||
error_handler!(405, "ERR_METHOD_NOT_ALLOWED", "method not allowed for this endpoint");
|
|
||||||
error_handler!(500, "ERR_QL_QUERY_FAILED", "graphql query timed out");
|
|
||||||
error_handler!(501, "ERR_NOT_IMPLEMENTED", "query not supported by this version of graphql");
|
|
||||||
error_handler!(502, "ERR_PROXY_ERR", "servers under load, please try again later");
|
|
||||||
error_handler!(503, "ERR_SERVER_OVERLOADED", "servers under load, please try again later");
|
|
||||||
error_handler!(504, "ERR_PROXY_TIMEOUT", "servers under load, please try again later");
|
|
||||||
error_handler!(505, "ERR_CLIENT_UNSUPPORTED", "your version of dnclient is out of date, please update");
|
|
||||||
|
|
||||||
*/
|
|
||||||
let _ = rocket::custom(figment)
|
let _ = rocket::custom(figment)
|
||||||
.mount("/", routes![
|
.mount("/", routes![
|
||||||
crate::routes::v1::auth::verify_magic_link::verify_magic_link
|
//crate::routes::v1::auth::verify_magic_link::verify_magic_link
|
||||||
|
crate::routes::v1::signup::signup_request
|
||||||
])
|
])
|
||||||
.register("/", catchers![
|
.register("/", catchers![
|
||||||
crate::routes::handler_400,
|
crate::routes::handler_400,
|
||||||
crate::routes::handler_401,
|
crate::routes::handler_401,
|
||||||
crate::routes::handler_403,
|
crate::routes::handler_403,
|
||||||
crate::routes::handler_404,
|
crate::routes::handler_404,
|
||||||
|
crate::routes::handler_422,
|
||||||
|
|
||||||
crate::routes::handler_500,
|
crate::routes::handler_500,
|
||||||
crate::routes::handler_501,
|
crate::routes::handler_501,
|
||||||
crate::routes::handler_502,
|
crate::routes::handler_502,
|
||||||
|
|
|
@ -35,6 +35,8 @@ error_handler!(401, "ERR_AUTHENTICATION_REQUIRED", "this endpoint requires authe
|
||||||
error_handler!(403, "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid");
|
error_handler!(403, "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid");
|
||||||
error_handler!(404, "ERR_NOT_FOUND", "resource not found");
|
error_handler!(404, "ERR_NOT_FOUND", "resource not found");
|
||||||
error_handler!(405, "ERR_METHOD_NOT_ALLOWED", "method not allowed for this endpoint");
|
error_handler!(405, "ERR_METHOD_NOT_ALLOWED", "method not allowed for this endpoint");
|
||||||
|
error_handler!(422, "ERR_MALFORMED_REQUEST", "unable to parse the request body, is it properly formatted?");
|
||||||
|
|
||||||
error_handler!(500, "ERR_QL_QUERY_FAILED", "graphql query timed out");
|
error_handler!(500, "ERR_QL_QUERY_FAILED", "graphql query timed out");
|
||||||
error_handler!(501, "ERR_NOT_IMPLEMENTED", "query not supported by this version of graphql");
|
error_handler!(501, "ERR_NOT_IMPLEMENTED", "query not supported by this version of graphql");
|
||||||
error_handler!(502, "ERR_PROXY_ERR", "servers under load, please try again later");
|
error_handler!(502, "ERR_PROXY_ERR", "servers under load, please try again later");
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod auth;
|
//pub mod auth;
|
||||||
|
pub mod signup;
|
|
@ -0,0 +1,55 @@
|
||||||
|
use rocket::{post, State};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use rocket::http::{ContentType, Status};
|
||||||
|
use sqlx::PgPool;
|
||||||
|
use crate::config::TFConfig;
|
||||||
|
use crate::email::send_magic_link;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct SignupRequest {
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct SignupResponseMetadata {}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct SignupResponse {
|
||||||
|
pub data: Option<String>,
|
||||||
|
pub metadata: SignupResponseMetadata,
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
created_on TIMESTAMP NOT NULL,
|
||||||
|
|
||||||
|
banned INTEGER NOT NULL,
|
||||||
|
ban_reason VARCHAR(1024) NOT NULL
|
||||||
|
*/
|
||||||
|
#[post("/v1/signup", data = "<req>")]
|
||||||
|
pub async fn signup_request(req: Json<SignupRequest>, pool: &State<PgPool>, config: &State<TFConfig>) -> Result<(ContentType, Json<SignupResponse>), (Status, String)> {
|
||||||
|
match sqlx::query!("INSERT INTO users (email, created_on, banned, ban_reason, totp_secret, totp_otpurl, totp_verified) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id ON CONFLICT DO NOTHING;", req.email, SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64, 0, "", "", "", 0).execute(pool.inner()).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// send magic link to email
|
||||||
|
match send_magic_link(req.email.clone(), pool.inner(), config.inner()).await {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
return Err((Status::InternalServerError, format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{} - {}\"}}]}}", "ERR_QL_QUERY_FAILED", "an error occurred while running the graphql query", e)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// this endpoint doesn't actually ever return an error? it will send you the magic link no matter what
|
||||||
|
// this appears to do the exact same thing as /v1/auth/magic-link, but it doesn't check if you have an account (magic-link does)
|
||||||
|
Ok((ContentType::JSON, Json(SignupResponse {
|
||||||
|
data: None,
|
||||||
|
metadata: SignupResponseMetadata {},
|
||||||
|
})))
|
||||||
|
}
|
|
@ -1,4 +1,11 @@
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use totp_rs::Algorithm;
|
||||||
|
|
||||||
|
pub const TOTP_ALGORITHM: Algorithm = Algorithm::SHA1;
|
||||||
|
pub const TOTP_DIGITS: usize = 6;
|
||||||
|
pub const TOTP_SKEW: u8 = 1;
|
||||||
|
pub const TOTP_STEP: u64 = 30;
|
||||||
|
pub const TOTP_ISSUER: &'static str = "trifidapi";
|
||||||
|
|
||||||
pub fn base64decode(val: &str) -> Result<Vec<u8>, base64::DecodeError> {
|
pub fn base64decode(val: &str) -> Result<Vec<u8>, base64::DecodeError> {
|
||||||
base64::engine::general_purpose::STANDARD.decode(val)
|
base64::engine::general_purpose::STANDARD.decode(val)
|
||||||
|
|
Loading…
Reference in New Issue