From 0a877d639b41b939b744727509516168c9dffe58 Mon Sep 17 00:00:00 2001 From: Ty Date: Thu, 11 Jul 2024 23:02:55 -0600 Subject: [PATCH] Create naja-server yes i just programmed the entire server in one sitting/commit, shush --- Cargo.lock | 437 +++++++++++++++++- crates/naja-server/Cargo.toml | 14 + crates/naja-server/src/entities/mod.rs | 5 + crates/naja-server/src/entities/prelude.rs | 3 + crates/naja-server/src/entities/profiles.rs | 16 + crates/naja-server/src/main.rs | 231 +++++++++ crates/server-migrations/Cargo.toml | 10 + crates/server-migrations/src/lib.rs | 15 + .../m20240711_000001_create_profiles_table.rs | 43 ++ crates/server-migrations/src/main.rs | 6 + 10 files changed, 767 insertions(+), 13 deletions(-) create mode 100644 crates/naja-server/Cargo.toml create mode 100644 crates/naja-server/src/entities/mod.rs create mode 100644 crates/naja-server/src/entities/prelude.rs create mode 100644 crates/naja-server/src/entities/profiles.rs create mode 100644 crates/naja-server/src/main.rs create mode 100644 crates/server-migrations/Cargo.toml create mode 100644 crates/server-migrations/src/lib.rs create mode 100644 crates/server-migrations/src/m20240711_000001_create_profiles_table.rs create mode 100644 crates/server-migrations/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 54fb2dc..ba28905 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,188 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.6.0", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae682f693a9cd7b058f2b0b5d9a6d7728a8555779bedbbc35dd88528611d020" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.11", + "base64 0.22.1", + "bitflags 2.6.0", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2 0.3.26", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn 2.0.70", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b02303ce8d4e8be5b855af6cf3c3a08f3eff26880faad82bab679c22d3650cb5" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1988c02af8d2b718c05bc4aeb6a66395b7cdf32858c2c71131e5637a8c05a9ff" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.8.11", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn 2.0.70", +] + [[package]] name = "addr2line" version = "0.22.0" @@ -91,6 +273,21 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -423,6 +620,27 @@ dependencies = [ "syn_derive", ] +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.9.1" @@ -492,6 +710,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "bytestring" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" +dependencies = [ + "bytes", +] + [[package]] name = "bzip2" version = "0.4.4" @@ -518,6 +745,11 @@ name = "cc" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] [[package]] name = "cesu8" @@ -594,9 +826,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -613,9 +845,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", @@ -693,6 +925,23 @@ dependencies = [ "custom_derive", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -837,6 +1086,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.70", +] + [[package]] name = "dialoguer" version = "0.11.0" @@ -1253,6 +1515,25 @@ dependencies = [ "winreg 0.10.1", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.5" @@ -1264,7 +1545,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -1365,6 +1646,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -1383,7 +1675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -1394,7 +1686,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", + "http 1.1.0", "http-body", "pin-project-lite", ] @@ -1405,6 +1697,12 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.4.0" @@ -1414,8 +1712,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", + "h2 0.4.5", + "http 1.1.0", "http-body", "httparse", "itoa", @@ -1432,7 +1730,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", - "http", + "http 1.1.0", "hyper", "hyper-util", "rustls 0.23.11", @@ -1467,7 +1765,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.1.0", "http-body", "hyper", "pin-project-lite", @@ -1726,6 +2024,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "josekit" version = "0.8.6" @@ -1783,6 +2090,12 @@ dependencies = [ "regex-automata 0.4.7", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1864,6 +2177,23 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + [[package]] name = "lock_api" version = "0.4.12" @@ -1948,6 +2278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] @@ -2002,6 +2333,18 @@ dependencies = [ "url", ] +[[package]] +name = "naja-server" +version = "0.1.0" +dependencies = [ + "actix-web", + "clap", + "naja-lib", + "sea-orm", + "serde", + "server-migrations", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -2622,6 +2965,12 @@ dependencies = [ "regex-syntax 0.8.4", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -2654,8 +3003,8 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", + "h2 0.4.5", + "http 1.1.0", "http-body", "http-body-util", "hyper", @@ -2778,6 +3127,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.34" @@ -3116,6 +3474,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "sequoia-openpgp" version = "1.21.1" @@ -3208,6 +3572,14 @@ dependencies = [ "serde", ] +[[package]] +name = "server-migrations" +version = "0.1.0" +dependencies = [ + "sea-orm-migration", + "tokio", +] + [[package]] name = "sha1" version = "0.10.6" @@ -3261,6 +3633,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -3839,7 +4220,9 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", @@ -4701,3 +5084,31 @@ dependencies = [ "quote", "syn 2.0.70", ] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.12+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/crates/naja-server/Cargo.toml b/crates/naja-server/Cargo.toml new file mode 100644 index 0000000..cec4ed4 --- /dev/null +++ b/crates/naja-server/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "naja-server" +version = "0.1.0" +edition = "2021" +repository = "https://codeberg.org/tyy/naja.git" +homepage = "https://codeberg.org/tyy/naja" + +[dependencies] +actix-web = "4.8.0" +clap = { version = "4.5.9", features = ["derive", "env"] } +sea-orm = { version = "0.12", features = ["sqlx-sqlite", "runtime-tokio-rustls", "macros"] } +serde = { version = "1.0.204", features = ["derive"] } +naja-lib = { path = "../naja-lib" } +migrations = { path = "../server-migrations", package = "server-migrations" } diff --git a/crates/naja-server/src/entities/mod.rs b/crates/naja-server/src/entities/mod.rs new file mode 100644 index 0000000..aa61409 --- /dev/null +++ b/crates/naja-server/src/entities/mod.rs @@ -0,0 +1,5 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +pub mod prelude; + +pub mod profiles; diff --git a/crates/naja-server/src/entities/prelude.rs b/crates/naja-server/src/entities/prelude.rs new file mode 100644 index 0000000..0436dca --- /dev/null +++ b/crates/naja-server/src/entities/prelude.rs @@ -0,0 +1,3 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +pub use super::profiles::Entity as Profiles; diff --git a/crates/naja-server/src/entities/profiles.rs b/crates/naja-server/src/entities/profiles.rs new file mode 100644 index 0000000..b81c594 --- /dev/null +++ b/crates/naja-server/src/entities/profiles.rs @@ -0,0 +1,16 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "profiles")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub fingerprint: String, + pub jwt: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/naja-server/src/main.rs b/crates/naja-server/src/main.rs new file mode 100644 index 0000000..87da352 --- /dev/null +++ b/crates/naja-server/src/main.rs @@ -0,0 +1,231 @@ +#[allow(warnings)] +mod entities; + +use std::{fs, io, path::PathBuf}; + +use actix_web::{get, http::header, post, web, App, HttpResponse, HttpServer, Responder}; +use clap::Parser; +use entities::{prelude::*, profiles}; +use migrations::{Migrator, MigratorTrait as _}; +use naja_lib::{ + aspe::requests::{AspeRequest, AspeRequestVariant}, + profiles::AriadneSignatureProfile, + utils::jwt::{JwtDeserializationError, JwtSerialize}, +}; +use sea_orm::{ + ActiveValue, ColumnTrait as _, Database, DatabaseConnection, DbErr, EntityTrait, QueryFilter, +}; +use serde::Serialize; + +#[derive(Serialize)] +struct VersionResponse { + name: &'static str, + version: &'static str, + repository: &'static str, + homepage: &'static str, +} + +#[derive(Debug, Clone)] +struct ServerState { + db: DatabaseConnection, + domain: String, +} + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// The path to store data in + #[arg(long, env = "NAJA_DATA_PATH")] + data_path: PathBuf, + /// The bind address to listen on + #[arg(long, env = "NAJA_BIND_ADDRESS")] + bind_address: String, + /// The port to listen on + #[arg(long, env = "NAJA_BIND_PORT", default_value = "80")] + port: u16, + /// The front-facing domain for this server + #[arg(long, env = "NAJA_DOMAIN")] + domain: String, +} + +#[actix_web::main] +async fn main() -> io::Result<()> { + let args = Args::parse(); + + fs::create_dir_all(&args.data_path)?; + let db = Database::connect(format!( + "sqlite://{}?mode=rwc", + args.data_path.join("db.sqlite").to_str().unwrap() + )) + .await + .expect("Unable to connect to sqlite database, are permissions correct?"); + + Migrator::up(&db, None) + .await + .expect("Unable to migrate database"); + + let state = ServerState { + db, + domain: args.domain, + }; + + HttpServer::new(move || { + App::new() + .app_data(web::Data::new(state.clone())) + .service(version) + .service(get_by_fingerprint) + .service(post_request) + }) + .bind((args.bind_address, args.port))? + .run() + .await +} + +#[get("/.well-known/aspe/version")] +async fn version(accept: web::Header) -> impl Responder { + match accept.to_string().as_str() { + "text/html" | "text/plain" => HttpResponse::Ok().body(format!( + "{}/{}", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION") + )), + _ => HttpResponse::Ok().json(VersionResponse { + name: env!("CARGO_PKG_NAME"), + version: env!("CARGO_PKG_VERSION"), + homepage: env!("CARGO_PKG_HOMEPAGE"), + repository: env!("CARGO_PKG_REPOSITORY"), + }), + } +} + +#[get("/.well-known/aspe/id/{fingerprint}")] +async fn get_by_fingerprint( + state: web::Data, + fingerprint: web::Path, +) -> impl Responder { + match Profiles::find_by_id(fingerprint.as_str()) + .one(&state.db) + .await + { + Ok(Some(profile)) => HttpResponse::Ok() + .content_type("application/asp+jwt; charset=UTF-8") + .body(profile.jwt), + Ok(None) => HttpResponse::NotFound().finish(), + Err(e) => { + eprintln!("{}", e); + HttpResponse::InternalServerError().finish() + } + } +} + +#[post("/.well-known/aspe/post")] +async fn post_request( + state: web::Data, + data: String, + content_type: web::Header, +) -> impl Responder { + if content_type.to_string().as_str() != "application/asp+jwt; charset=UTF-8" { + return HttpResponse::BadRequest() + .body("Content type header was not set to \"application/asp+jwt; charset=UTF-8\""); + } + + match AspeRequest::decode_and_verify::(&data, None) { + Ok((key, request)) => { + if let AspeRequestVariant::Update { aspe_uri, .. } + | AspeRequestVariant::Delete { aspe_uri } = &request.request + { + if aspe_uri != &format!("aspe:{}:{}", &state.domain, &key.fingerprint) { + return HttpResponse::BadRequest().finish(); + } + } + + match &request.request { + AspeRequestVariant::Create { profile_jws } + | AspeRequestVariant::Update { profile_jws, .. } => { + match AriadneSignatureProfile::decode_and_verify( + profile_jws, + Some(&key.fingerprint), + ) { + Ok(_) if matches!(request.request, AspeRequestVariant::Create { .. }) => { + match Profiles::insert(profiles::ActiveModel { + fingerprint: ActiveValue::Set(key.fingerprint), + jwt: ActiveValue::Set(profile_jws.to_owned()), + }) + .exec(&state.db) + .await + { + Ok(_) => HttpResponse::Created().finish(), + Err(e) => { + eprintln!("{e}"); + HttpResponse::InternalServerError().finish() + } + } + } + Ok(_) => { + // Must be AspeRequestvariant::Update { .. } + match Profiles::update(profiles::ActiveModel { + jwt: ActiveValue::Set(profile_jws.to_owned()), + ..Default::default() + }) + .filter(profiles::Column::Fingerprint.eq(&key.fingerprint)) + .exec(&state.db) + .await + { + Ok(_) => HttpResponse::Created().finish(), + Err(DbErr::RecordNotUpdated) => { + HttpResponse::BadRequest() + .body("Profile does not already exist") + } + Err(e) => { + eprintln!("{e}"); + HttpResponse::InternalServerError().finish() + } + } + } + Err(e) => HttpResponse::BadRequest().body(match e { + JwtDeserializationError::HeaderDecodeError => { + "Unable to decode profile JWT header" + } + JwtDeserializationError::MalformedJwkError => { + "Profile JWT JWK was malformed" + } + JwtDeserializationError::WrongJwkError => { + "The wrong JWK was encoded inside the profile JWT" + } + JwtDeserializationError::JwtDecodeError => { + "Unable to decoded profile JWT" + } + JwtDeserializationError::JwkUsageError => { + "Profile JWT JWK had invalid usage claim(s)" + } + }), + } + } + AspeRequestVariant::Delete { .. } => { + match Profiles::delete_by_id(&key.fingerprint) + .exec(&state.db) + .await + { + Ok(_) => HttpResponse::Ok().finish(), + Err(DbErr::RecordNotFound(_)) => { + HttpResponse::NotFound().body("Profile does not exist") + } + Err(e) => { + eprintln!("{e}"); + HttpResponse::InternalServerError().finish() + } + } + } + } + } + Err(e) => HttpResponse::BadRequest().body(match e { + JwtDeserializationError::HeaderDecodeError => "Unable to decode request JWT header", + JwtDeserializationError::MalformedJwkError => "Request JWT JWK was malformed", + JwtDeserializationError::WrongJwkError => { + "The wrong JWK was encoded inside the request JWT" + } + JwtDeserializationError::JwtDecodeError => "Unable to decoded request JWT", + JwtDeserializationError::JwkUsageError => "Request JWT JWK had invalid usage claim(s)", + }), + } +} diff --git a/crates/server-migrations/Cargo.toml b/crates/server-migrations/Cargo.toml new file mode 100644 index 0000000..eab4d45 --- /dev/null +++ b/crates/server-migrations/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "server-migrations" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sea-orm-migration = { version = "0.12.15", features = ["sqlx-sqlite", "runtime-tokio-rustls"] } +tokio = { version = "1.38.0", features = ["rt-multi-thread", "macros"] } diff --git a/crates/server-migrations/src/lib.rs b/crates/server-migrations/src/lib.rs new file mode 100644 index 0000000..a6f50a6 --- /dev/null +++ b/crates/server-migrations/src/lib.rs @@ -0,0 +1,15 @@ +mod m20240711_000001_create_profiles_table; + +use sea_orm_migration::prelude::*; + +pub use sea_orm_migration::{MigratorTrait, SchemaManager}; + +pub struct Migrator; + +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20240711_000001_create_profiles_table::Migration), + ] + } +} diff --git a/crates/server-migrations/src/m20240711_000001_create_profiles_table.rs b/crates/server-migrations/src/m20240711_000001_create_profiles_table.rs new file mode 100644 index 0000000..028d78c --- /dev/null +++ b/crates/server-migrations/src/m20240711_000001_create_profiles_table.rs @@ -0,0 +1,43 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + // Define how to apply this migration: Create the Profiles table. + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Profiles::Table) + .col( + ColumnDef::new(Profiles::Fingerprint) + .string_len(26) + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(Profiles::Jwt).string().not_null()) + .to_owned(), + ) + .await?; + + Ok(()) + } + + // Define how to rollback this migration: Drop the Profiles table. + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Profiles::Table).to_owned()) + .await?; + + Ok(()) + } +} + +#[derive(Iden)] +pub enum Profiles { + Table, + Fingerprint, + Jwt +} \ No newline at end of file diff --git a/crates/server-migrations/src/main.rs b/crates/server-migrations/src/main.rs new file mode 100644 index 0000000..9c8eb8b --- /dev/null +++ b/crates/server-migrations/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::cli; + +#[tokio::main] +async fn main() { + cli::run_cli(server_migrations::Migrator).await; +}