add basic login

This commit is contained in:
Charlotte 🦝 Delenk 2024-12-03 09:15:25 +01:00
parent 83160bd329
commit ca0b13acb6
Signed by: darkkirb
GPG key ID: AB2BD8DAF2E37122
17 changed files with 915 additions and 28 deletions

174
Cargo.lock generated
View file

@ -59,6 +59,18 @@ dependencies = [
"libc",
]
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "arrayref"
version = "0.3.9"
@ -588,6 +600,17 @@ dependencies = [
"tracing",
]
[[package]]
name = "axum-client-ip"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac"
dependencies = [
"axum",
"forwarded-header-value",
"serde",
]
[[package]]
name = "axum-core"
version = "0.4.5"
@ -630,6 +653,12 @@ dependencies = [
"tower-http 0.5.2",
]
[[package]]
name = "b64-ct"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32261e5a95e234446dd31774ef4305dc164039370fe2622fd3e935a0f2730689"
[[package]]
name = "backtrace"
version = "0.3.71"
@ -730,6 +759,15 @@ dependencies = [
"serde",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "blake3"
version = "1.5.5"
@ -820,6 +858,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "chir-rs"
version = "0.1.0"
@ -873,6 +922,7 @@ version = "0.1.0"
dependencies = [
"bincode",
"blake3",
"chir-rs-http-api",
"eyre",
"mime",
"serde",
@ -901,20 +951,27 @@ dependencies = [
name = "chir-rs-http"
version = "0.1.0"
dependencies = [
"argon2",
"axum",
"axum-client-ip",
"axum-prometheus",
"b64-ct",
"base64 0.22.1",
"bincode",
"chir-rs-castore",
"chir-rs-config",
"chir-rs-db",
"chir-rs-http-api",
"chir-rs-misc",
"chrono",
"eyre",
"mime",
"rusty_paseto",
"sentry-tower",
"tokio",
"tower-http 0.6.2",
"tracing",
"unicode-normalization",
]
[[package]]
@ -925,8 +982,10 @@ dependencies = [
"axum-core",
"bincode",
"bytes",
"educe",
"http 1.1.0",
"mime",
"serde",
"thiserror 2.0.3",
"tracing",
]
@ -954,6 +1013,16 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
@ -1311,6 +1380,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d"
dependencies = [
"serde",
"typeid",
]
[[package]]
name = "errno"
version = "0.3.10"
@ -1425,6 +1504,16 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "forwarded-header-value"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
dependencies = [
"nonempty",
"thiserror 1.0.69",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
@ -2061,12 +2150,30 @@ dependencies = [
"hashbrown 0.15.2",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "ipnet"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
[[package]]
name = "iso8601"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153"
dependencies = [
"nom",
]
[[package]]
name = "itertools"
version = "0.12.1"
@ -2304,6 +2411,12 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nonempty"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@ -2467,6 +2580,17 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.15"
@ -3043,6 +3167,28 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "rusty_paseto"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c28b9f48df2de43da68fb7dcbd60c4f85bca5f6ae4c92cdd9540bcd4a606cb3"
dependencies = [
"base64 0.22.1",
"blake2",
"chacha20",
"digest",
"erased-serde",
"hex",
"iso8601",
"rand_core",
"ring",
"serde",
"serde_json",
"thiserror 1.0.69",
"time",
"zeroize",
]
[[package]]
name = "ryu"
version = "1.0.18"
@ -3801,9 +3947,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.36"
version = "0.3.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
dependencies = [
"deranged",
"itoa",
@ -3822,9 +3968,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.18"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
dependencies = [
"num-conv",
"time-core",
@ -4134,6 +4280,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "typeid"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
[[package]]
name = "typenum"
version = "1.17.0"
@ -4748,6 +4900,20 @@ name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zerovec"

484
Cargo.nix
View file

@ -8,9 +8,9 @@ args@{
"chir-rs-config/default"
"chir-rs-misc/default"
"chir-rs-db/default"
"chir-rs-http-api/default"
"chir-rs-gemini/default"
"chir-rs-http/default"
"chir-rs-http-api/default"
"chir-rs/default"
],
rustPackages,
@ -31,7 +31,7 @@ args@{
ignoreLockHash,
}:
let
nixifiedLockHash = "15bd19e95e9d0f4da8a30b83aa463de93e06b1af9b257aa15d3779c3c64feae9";
nixifiedLockHash = "73ff0a6e5f052a9577d0aac2888498a5a588a21319b04b60f8739765f62dae09";
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
lockHashIgnored =
@ -95,9 +95,9 @@ else
chir-rs-config = rustPackages.unknown.chir-rs-config."0.1.0";
chir-rs-misc = rustPackages.unknown.chir-rs-misc."0.1.0";
chir-rs-db = rustPackages.unknown.chir-rs-db."0.1.0";
chir-rs-http-api = rustPackages.unknown.chir-rs-http-api."0.1.0";
chir-rs-gemini = rustPackages.unknown.chir-rs-gemini."0.1.0";
chir-rs-http = rustPackages.unknown.chir-rs-http."0.1.0";
chir-rs-http-api = rustPackages.unknown.chir-rs-http-api."0.1.0";
chir-rs = rustPackages.unknown.chir-rs."0.1.0";
};
"registry+https://github.com/rust-lang/crates.io-index".addr2line."0.21.0" =
@ -239,6 +239,47 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".argon2."0.5.3" =
overridableMkRustCrate
(profileName: rec {
name = "argon2";
version = "0.5.3";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072";
};
features = builtins.concatLists [
[ "alloc" ]
[ "default" ]
[ "password-hash" ]
[ "rand" ]
];
dependencies = {
base64ct =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64ct."1.6.0" {
inherit profileName;
}).out;
blake2 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".blake2."0.10.6" {
inherit profileName;
}).out;
${
if hostPlatform.parsed.cpu.name == "i686" || hostPlatform.parsed.cpu.name == "x86_64" then
"cpufeatures"
else
null
} =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".cpufeatures."0.2.16" {
inherit profileName;
}).out;
password_hash =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".password-hash."0.5.0" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".arrayref."0.3.9" =
overridableMkRustCrate
(profileName: rec {
@ -570,7 +611,7 @@ else
inherit profileName;
}).out;
time =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" {
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.37" {
inherit profileName;
}).out;
tokio =
@ -1254,7 +1295,7 @@ else
inherit profileName;
}).out;
time =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" {
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.37" {
inherit profileName;
}).out;
tracing =
@ -1733,7 +1774,7 @@ else
inherit profileName;
}).out;
time =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" {
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.37" {
inherit profileName;
}).out;
tokio =
@ -1935,6 +1976,32 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".axum-client-ip."0.6.1" =
overridableMkRustCrate
(profileName: rec {
name = "axum-client-ip";
version = "0.6.1";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "9eefda7e2b27e1bda4d6fa8a06b50803b8793769045918bc37ad062d48a6efac";
};
dependencies = {
axum =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".axum."0.7.9" {
inherit profileName;
}).out;
forwarded_header_value =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".forwarded-header-value."0.1.1"
{ inherit profileName; }
).out;
serde =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.215" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".axum-core."0.4.5" =
overridableMkRustCrate
(profileName: rec {
@ -2076,6 +2143,22 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.2" =
overridableMkRustCrate
(profileName: rec {
name = "b64-ct";
version = "0.1.2";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "32261e5a95e234446dd31774ef4305dc164039370fe2622fd3e935a0f2730689";
};
features = builtins.concatLists [
[ "default" ]
[ "std" ]
];
});
"registry+https://github.com/rust-lang/crates.io-index".backtrace."0.3.71" =
overridableMkRustCrate
(profileName: rec {
@ -2412,6 +2495,28 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".blake2."0.10.6" =
overridableMkRustCrate
(profileName: rec {
name = "blake2";
version = "0.10.6";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe";
};
features = builtins.concatLists [
[ "default" ]
[ "std" ]
];
dependencies = {
digest =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.7" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".blake3."1.5.5" =
overridableMkRustCrate
(profileName: rec {
@ -2635,6 +2740,37 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".chacha20."0.9.1" =
overridableMkRustCrate
(profileName: rec {
name = "chacha20";
version = "0.9.1";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818";
};
dependencies = {
cfg_if =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".cfg-if."1.0.0" {
inherit profileName;
}).out;
cipher =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".cipher."0.4.4" {
inherit profileName;
}).out;
${
if hostPlatform.parsed.cpu.name == "x86_64" || hostPlatform.parsed.cpu.name == "i686" then
"cpufeatures"
else
null
} =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".cpufeatures."0.2.16" {
inherit profileName;
}).out;
};
});
"unknown".chir-rs."0.1.0" = overridableMkRustCrate (profileName: rec {
name = "chir-rs";
version = "0.1.0";
@ -2771,6 +2907,7 @@ else
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".blake3."1.5.5" {
inherit profileName;
}).out;
chir_rs_http_api = (rustPackages."unknown".chir-rs-http-api."0.1.0" { inherit profileName; }).out;
eyre =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".eyre."0.6.12" {
inherit profileName;
@ -2844,14 +2981,30 @@ else
registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/chir-rs-http");
dependencies = {
argon2 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".argon2."0.5.3" {
inherit profileName;
}).out;
axum =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".axum."0.7.9" {
inherit profileName;
}).out;
axum_client_ip =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".axum-client-ip."0.6.1" {
inherit profileName;
}).out;
axum_prometheus =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".axum-prometheus."0.7.0" {
inherit profileName;
}).out;
b64_ct =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".b64-ct."0.1.2" {
inherit profileName;
}).out;
base64 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.22.1" {
inherit profileName;
}).out;
bincode =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".bincode."2.0.0-rc.3" {
inherit profileName;
@ -2861,6 +3014,10 @@ else
chir_rs_db = (rustPackages."unknown".chir-rs-db."0.1.0" { inherit profileName; }).out;
chir_rs_http_api = (rustPackages."unknown".chir-rs-http-api."0.1.0" { inherit profileName; }).out;
chir_rs_misc = (rustPackages."unknown".chir-rs-misc."0.1.0" { inherit profileName; }).out;
chrono =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".chrono."0.4.38" {
inherit profileName;
}).out;
eyre =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".eyre."0.6.12" {
inherit profileName;
@ -2869,6 +3026,10 @@ else
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime."0.3.17" {
inherit profileName;
}).out;
rusty_paseto =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".rusty_paseto."0.7.1" {
inherit profileName;
}).out;
sentry_tower =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".sentry-tower."0.34.0" {
inherit profileName;
@ -2885,6 +3046,10 @@ else
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".tracing."0.1.41" {
inherit profileName;
}).out;
unicode_normalization =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".unicode-normalization."0.1.24"
{ inherit profileName; }
).out;
};
});
@ -2918,6 +3083,10 @@ else
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.9.0" {
inherit profileName;
}).out;
educe =
(buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".educe."0.6.0" {
profileName = "__noProfile";
}).out;
http =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.1.0" {
inherit profileName;
@ -2926,6 +3095,10 @@ else
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".mime."0.3.17" {
inherit profileName;
}).out;
serde =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.215" {
inherit profileName;
}).out;
thiserror =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".thiserror."2.0.3" {
inherit profileName;
@ -3033,6 +3206,28 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".cipher."0.4.4" =
overridableMkRustCrate
(profileName: rec {
name = "cipher";
version = "0.4.4";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad";
};
dependencies = {
crypto_common =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".crypto-common."0.1.6" {
inherit profileName;
}).out;
inout =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".inout."0.1.3" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".clang-sys."1.8.1" =
overridableMkRustCrate
(profileName: rec {
@ -3998,6 +4193,33 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".erased-serde."0.4.5" =
overridableMkRustCrate
(profileName: rec {
name = "erased-serde";
version = "0.4.5";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d";
};
features = builtins.concatLists [
[ "alloc" ]
[ "default" ]
[ "std" ]
];
dependencies = {
serde =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.215" {
inherit profileName;
}).out;
typeid =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".typeid."1.0.2" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".errno."0.3.10" =
overridableMkRustCrate
(profileName: rec {
@ -4310,6 +4532,28 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".forwarded-header-value."0.1.1" =
overridableMkRustCrate
(profileName: rec {
name = "forwarded-header-value";
version = "0.1.1";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9";
};
dependencies = {
nonempty =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".nonempty."0.7.0" {
inherit profileName;
}).out;
thiserror =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".thiserror."1.0.69" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".fs_extra."1.3.0" =
overridableMkRustCrate
(profileName: rec {
@ -6142,6 +6386,24 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".inout."0.1.3" =
overridableMkRustCrate
(profileName: rec {
name = "inout";
version = "0.1.3";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5";
};
dependencies = {
generic_array =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".generic-array."0.14.7" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".ipnet."2.10.1" =
overridableMkRustCrate
(profileName: rec {
@ -6158,6 +6420,28 @@ else
];
});
"registry+https://github.com/rust-lang/crates.io-index".iso8601."0.6.1" =
overridableMkRustCrate
(profileName: rec {
name = "iso8601";
version = "0.6.1";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153";
};
features = builtins.concatLists [
[ "default" ]
[ "std" ]
];
dependencies = {
nom =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".nom."7.1.3" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".itertools."0.12.1" =
overridableMkRustCrate
(profileName: rec {
@ -6796,6 +7080,18 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".nonempty."0.7.0" =
overridableMkRustCrate
(profileName: rec {
name = "nonempty";
version = "0.7.0";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7";
};
});
"registry+https://github.com/rust-lang/crates.io-index".nu-ansi-term."0.46.0" =
overridableMkRustCrate
(profileName: rec {
@ -7221,6 +7517,37 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".password-hash."0.5.0" =
overridableMkRustCrate
(profileName: rec {
name = "password-hash";
version = "0.5.0";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166";
};
features = builtins.concatLists [
[ "alloc" ]
[ "default" ]
[ "rand_core" ]
];
dependencies = {
base64ct =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64ct."1.6.0" {
inherit profileName;
}).out;
rand_core =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand_core."0.6.4" {
inherit profileName;
}).out;
subtle =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".subtle."2.6.1" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".paste."1.0.15" =
overridableMkRustCrate
(profileName: rec {
@ -8395,6 +8722,7 @@ else
[ "alloc" ]
[ "default" ]
[ "dev_urandom_fallback" ]
[ "std" ]
[ "wasm32_unknown_unknown_js" ]
];
dependencies = {
@ -9050,6 +9378,89 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".rusty_paseto."0.7.1" =
overridableMkRustCrate
(profileName: rec {
name = "rusty_paseto";
version = "0.7.1";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "0c28b9f48df2de43da68fb7dcbd60c4f85bca5f6ae4c92cdd9540bcd4a606cb3";
};
features = builtins.concatLists [
[ "batteries_included" ]
[ "blake2" ]
[ "chacha20" ]
[ "core" ]
[ "erased-serde" ]
[ "generic" ]
[ "local" ]
[ "serde" ]
[ "serde_json" ]
[ "v4" ]
[ "v4_local" ]
];
dependencies = {
base64 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.22.1" {
inherit profileName;
}).out;
blake2 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".blake2."0.10.6" {
inherit profileName;
}).out;
chacha20 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".chacha20."0.9.1" {
inherit profileName;
}).out;
digest =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.7" {
inherit profileName;
}).out;
erased_serde =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".erased-serde."0.4.5" {
inherit profileName;
}).out;
hex =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" {
inherit profileName;
}).out;
iso8601 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".iso8601."0.6.1" {
inherit profileName;
}).out;
rand_core =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand_core."0.6.4" {
inherit profileName;
}).out;
ring =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".ring."0.17.8" {
inherit profileName;
}).out;
serde =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.215" {
inherit profileName;
}).out;
serde_json =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde_json."1.0.133" {
inherit profileName;
}).out;
thiserror =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".thiserror."1.0.69" {
inherit profileName;
}).out;
time =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.37" {
inherit profileName;
}).out;
zeroize =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.8.1" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".ryu."1.0.18" =
overridableMkRustCrate
(profileName: rec {
@ -9656,7 +10067,7 @@ else
inherit profileName;
}).out;
time =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" {
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".time."0.3.37" {
inherit profileName;
}).out;
url =
@ -11308,15 +11719,15 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".time."0.3.36" =
"registry+https://github.com/rust-lang/crates.io-index".time."0.3.37" =
overridableMkRustCrate
(profileName: rec {
name = "time";
version = "0.3.36";
version = "0.3.37";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885";
sha256 = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21";
};
features = builtins.concatLists [
[ "alloc" ]
@ -11351,7 +11762,7 @@ else
inherit profileName;
}).out;
time_macros =
(buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.18" {
(buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.19" {
profileName = "__noProfile";
}).out;
};
@ -11369,15 +11780,15 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.18" =
"registry+https://github.com/rust-lang/crates.io-index".time-macros."0.2.19" =
overridableMkRustCrate
(profileName: rec {
name = "time-macros";
version = "0.2.18";
version = "0.2.19";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf";
sha256 = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de";
};
features = builtins.concatLists [
[ "formatting" ]
@ -12286,6 +12697,18 @@ else
};
});
"registry+https://github.com/rust-lang/crates.io-index".typeid."1.0.2" =
overridableMkRustCrate
(profileName: rec {
name = "typeid";
version = "1.0.2";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e";
};
});
"registry+https://github.com/rust-lang/crates.io-index".typenum."1.17.0" =
overridableMkRustCrate
(profileName: rec {
@ -13953,7 +14376,40 @@ else
features = builtins.concatLists [
[ "alloc" ]
[ "default" ]
[ "zeroize_derive" ]
];
dependencies = {
zeroize_derive =
(buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize_derive."1.4.2" {
profileName = "__noProfile";
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".zeroize_derive."1.4.2" =
overridableMkRustCrate
(profileName: rec {
name = "zeroize_derive";
version = "1.4.2";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69";
};
dependencies = {
proc_macro2 =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.92" {
inherit profileName;
}).out;
quote =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".quote."1.0.37" {
inherit profileName;
}).out;
syn =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".syn."2.0.90" {
inherit profileName;
}).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".zerovec."0.10.4" =

View file

@ -218,6 +218,8 @@ pub struct ChirRs {
pub s3: S3Config,
/// Ca Cache max size in bytes
pub cache_max_size: u64,
/// Paseto secret key file
pub paseto_secret_key_file: String,
}
impl ChirRs {

View file

@ -12,6 +12,7 @@ tracing = "0.1.40"
blake3 = { version = "1.5.4", features = ["serde"] }
tokio = { version = "1.41.1", features = ["fs", "macros", "rt", "sync"] }
mime = "0.3.17"
chir-rs-http-api = { version = "0.1.0", path = "../chir-rs-http-api" }
[lints.rust]
deprecated-safe = "forbid"

View file

@ -1,2 +0,0 @@
-- Add migration script here
INSERT INTO "user" (username, password_hash) VALUES ('service-raccount', '');

View file

@ -1,9 +1,13 @@
//! User-related APIs
use std::{collections::HashSet, fmt::Debug};
use bincode::{Decode, Encode};
use chir_rs_http_api::auth::Scope;
use eyre::{Context, Result};
use serde::{Deserialize, Serialize};
use sqlx::{prelude::FromRow, query};
use tracing::instrument;
use crate::Database;
@ -23,7 +27,7 @@ impl User {
///
/// # Errors
/// An error occurs if accessing the database fails
#[allow(clippy::missing_panics_doc, reason = "sqlx moment")]
#[instrument(skip(db))]
pub async fn get(db: &Database, username: &str) -> Result<Option<Self>> {
#[allow(clippy::panic, reason = "sqlx moment")]
let res = query!(r#"SELECT * FROM "user" WHERE username = $1"#, username)
@ -41,4 +45,38 @@ impl User {
Ok(None)
}
}
/// Creates a new session for user
///
/// The caller has to ensure that the user has authorized this.
///
/// # Errors
/// An error occurs if accessing the database fails
#[instrument(skip(db))]
#[allow(clippy::panic, reason = "sqlx moment")]
pub async fn new_session(&self, db: &Database, scopes: HashSet<Scope>) -> Result<i64> {
let mut txn = db.0.begin().await?;
let user_id: i64 = self.id.try_into()?;
let session = query!(
"INSERT INTO sessions (user_id) VALUES ($1) RETURNING (id)",
user_id
)
.fetch_one(&mut *txn)
.await?;
for scope in scopes {
query!(
"INSERT INTO session_scopes (session_id, scope) VALUES ($1, $2)",
session.id,
scope.to_i64()
)
.execute(&mut *txn)
.await?;
}
txn.commit().await?;
Ok(session.id)
}
}

View file

@ -11,8 +11,10 @@ async-trait = { version = "0.1.83", optional = true }
axum-core = { version = "0.4.5", optional = true }
bincode = "2.0.0-rc.3"
bytes = { version = "1.8.0", optional = true }
educe = { version = "0.6.0", default-features = false, features = ["Debug"] }
http = "1.1.0"
mime = { version = "0.3.17", optional = true }
serde = { version = "1.0.215", features = ["derive"] }
thiserror = "2.0.3"
tracing = { version = "0.1.40", optional = true }

View file

@ -0,0 +1,36 @@
use std::collections::HashSet;
/// Authentication APIs
use bincode::{Decode, Encode};
use educe::Educe;
use serde::{Deserialize, Serialize};
/// List of supported scopes for authentication
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Encode, Decode, Hash)]
pub enum Scope {
/// Full scope granted by logging in.
Full,
}
impl Scope {
/// Converts the scope into an integer suitable for database storage
#[must_use]
pub const fn to_i64(self) -> i64 {
match self {
Self::Full => 0,
}
}
}
/// Login request for the user
#[derive(Clone, Educe, Serialize, Deserialize, Encode, Decode)]
#[educe(Debug)]
pub struct LoginRequest {
/// Username
pub username: String,
/// Password
#[educe(Debug(ignore))]
pub password: String,
/// Scopes
pub scopes: HashSet<Scope>,
}

View file

@ -33,6 +33,15 @@ pub enum APIError {
/// Returned when the error is unknown
#[error("Unknown Error")]
Unknown(String),
/// Returned when there is a database error
#[error("Database error: {0}")]
DatabaseError(String),
/// Returned when the specified user was not found
#[error("Resource not found: {0}")]
UserNotFound(String),
/// Invalid password
#[error("Invalid password for user {0}")]
InvalidPassword(String),
}
impl APIError {
@ -45,7 +54,8 @@ impl APIError {
}
Self::PayloadTooBig => StatusCode::PAYLOAD_TOO_LARGE,
Self::PayloadLoadError | Self::PayloadInvalid => StatusCode::BAD_REQUEST,
Self::Unknown(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::UserNotFound(_) | Self::InvalidPassword(_) => StatusCode::UNAUTHORIZED,
Self::Unknown(_) | Self::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}

View file

@ -1,5 +1,6 @@
//! API Type Definitions for chir.rs
pub mod auth;
#[cfg(feature = "axum")]
pub mod axum;
pub mod errors;

View file

@ -4,8 +4,12 @@ version = "0.1.0"
edition = "2021"
[dependencies]
argon2 = "0.5.3"
axum = { version = "0.7.9", features = ["tracing"] }
axum-client-ip = "0.6.1"
axum-prometheus = "0.7.0"
b64-ct = "0.1.2"
base64 = "0.22.1"
bincode = "2.0.0-rc.3"
chir-rs-castore = { version = "0.1.0", path = "../chir-rs-castore" }
chir-rs-config = { version = "0.1.0", path = "../chir-rs-config" }
@ -16,12 +20,15 @@ chir-rs-http-api = { version = "0.1.0", path = "../chir-rs-http-api", features =
chir-rs-misc = { version = "0.1.0", path = "../chir-rs-misc", features = [
"base64",
] }
chrono = "0.4.38"
eyre = "0.6.12"
mime = "0.3.17"
rusty_paseto = { version = "0.7.1", default-features = false, features = ["batteries_included", "v4_local"] }
sentry-tower = { version = "0.34.0", features = ["axum", "axum-matched-path"] }
tokio = { version = "1.41.1", features = ["net"] }
tokio = { version = "1.41.1", features = ["fs", "net"] }
tower-http = { version = "0.6.2", features = ["trace"] }
tracing = "0.1.40"
unicode-normalization = "0.1.24"
[lints.rust]
deprecated-safe = "forbid"

View file

@ -0,0 +1,3 @@
//! Authentication related functionality
pub mod password_login;

View file

@ -0,0 +1,82 @@
//! Password based login flow
use argon2::{Argon2, PasswordHash, PasswordVerifier};
use axum::extract::State;
use chir_rs_db::user::User;
use chir_rs_http_api::{auth::LoginRequest, axum::bincode::Bincode, errors::APIError};
use chrono::{Days, Utc};
use eyre::{Context as _, OptionExt};
use rusty_paseto::{
core::{Local, V4},
prelude::{
AudienceClaim, ExpirationClaim, IssuerClaim, PasetoBuilder, SubjectClaim,
TokenIdentifierClaim,
},
};
use tokio::task::spawn_blocking;
use tracing::instrument;
use unicode_normalization::UnicodeNormalization;
use crate::AppState;
/// Logs in using username and password
#[instrument]
pub async fn login(
State(state): State<AppState>,
Bincode(login_request): Bincode<LoginRequest>,
) -> Result<Bincode<String>, APIError> {
let Some(user_info) = User::get(&state.db, &login_request.username)
.await
.with_context(|| format!("Fetching user info for {}", login_request.username))
.map_err(|e| APIError::DatabaseError(format!("{e:?}")))?
else {
return Err(APIError::UserNotFound(login_request.username));
};
let password = login_request.password.trim().nfkc().collect::<String>();
let pwhash = user_info.password_hash.clone();
spawn_blocking(move || {
let pwhash = PasswordHash::new(&pwhash)?;
Argon2::default().verify_password(password.as_bytes(), &pwhash)
})
.await
.with_context(|| {
format!(
"Hashing password for login request for {}",
login_request.username
)
})
.map_err(|e| APIError::Unknown(format!("{e:?}")))?
.map_err(|_| APIError::InvalidPassword(login_request.username.clone()))?;
let session_id = user_info
.new_session(&state.db, login_request.scopes)
.await
.with_context(|| format!("Creating context for {}", login_request.username))
.map_err(|e| APIError::DatabaseError(format!("{e:?}")))?
.to_string();
let now = Utc::now();
let tomorrow = now
.checked_add_days(Days::new(1))
.ok_or_eyre("We should not be running several thousand years in the future!")
.map_err(|e| APIError::Unknown(format!("{e:?}")))?;
let token = PasetoBuilder::<V4, Local>::default()
.set_claim(AudienceClaim::from("https://lotte.chir.rs"))
.set_claim(SubjectClaim::from(login_request.username.as_str()))
.set_claim(IssuerClaim::from("https://lotte.chir.rs"))
.set_claim(TokenIdentifierClaim::from(session_id.as_str()))
.set_claim(
ExpirationClaim::try_from(tomorrow.to_rfc3339().as_str())
.context("valid date")
.map_err(|e| APIError::Unknown(format!("{e:?}")))?,
)
.build(&state.paseto_key)
.context("Signing paseto key")
.map_err(|e| APIError::Unknown(format!("{e:?}")))?;
Ok(Bincode(token))
}

View file

@ -1,32 +1,43 @@
//! HTTP server implementation for chir-rs
use std::sync::Arc;
use std::{fmt::Debug, sync::Arc};
use axum::{
extract::{MatchedPath, Request, State},
http::StatusCode,
routing::get,
routing::{get, post},
Router,
};
use axum_prometheus::PrometheusMetricLayer;
use b64_ct::FromBase64;
use chir_rs_castore::CaStore;
use chir_rs_config::ChirRs;
use chir_rs_db::Database;
use chir_rs_http_api::{axum::bincode::Bincode, readiness::ReadyState};
use eyre::{Context, Result};
use eyre::{bail, eyre, Context, Result};
use rusty_paseto::core::{Key, Local, PasetoSymmetricKey, V4};
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
use tracing::{error, info, info_span};
pub mod auth;
pub mod ca_server;
/// Application state
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct AppState {
/// Database handle
pub db: Database,
/// CA store handle
pub ca: CaStore,
/// PASETO private key
pub paseto_key: Arc<PasetoSymmetricKey<V4, Local>>,
}
impl Debug for AppState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AppState").finish_non_exhaustive()
}
}
/// Entrypoint for the HTTP server component
@ -36,6 +47,16 @@ pub struct AppState {
///
/// Errors it encounters during runtime should be automatically handled.
pub async fn main(cfg: Arc<ChirRs>, db: Database, castore: CaStore) -> Result<()> {
let paseto_symmetric_key =
tokio::fs::read_to_string(cfg.paseto_secret_key_file.clone()).await?;
let paseto_symmetric_key = paseto_symmetric_key
.from_base64()
.map_err(|e| eyre!("{e:?}"))?;
if paseto_symmetric_key.len() != 32 {
bail!("Invalid symmetric key size");
}
let paseto_symmetric_key = PasetoSymmetricKey::from(Key::from(paseto_symmetric_key.as_slice()));
let (prometheus_layer, metric_handle) = PrometheusMetricLayer::pair();
let app = Router::new()
// Routes here
@ -58,8 +79,13 @@ pub async fn main(cfg: Arc<ChirRs>, db: Database, castore: CaStore) -> Result<()
"/.api/metrics",
get(|| async move { metric_handle.render() }),
)
.route("/.api/auth/login", post(auth::password_login::login))
.fallback(get(ca_server::serve_files))
.with_state(AppState { db, ca: castore })
.with_state(AppState {
db,
ca: castore,
paseto_key: Arc::new(paseto_symmetric_key),
})
.layer(
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
let matched_path = request

View file

@ -1,5 +1,6 @@
# Maximum size of the in-memory object cache.
cache_max_size = 32_000_000
paseto_secret_key_file = "secrets/paseto-secret"
[logging]
#sentry_dsn = "…"

View file

@ -77,6 +77,8 @@
sqlx-cli
cargo-expand
sqlite
treefmt
nixfmt-rfc-style
];
};
packages = pkgs.lib.mapAttrs (_: v: (v { }).overrideAttrs { dontStrip = true; }) rustPkgs.workspace;

56
treefmt.toml Normal file
View file

@ -0,0 +1,56 @@
# One CLI to format the code tree - https://github.com/numtide/treefmt
# Do not exit with error if a configured formatter is missing
# Env $TREEFMT_ALLOW_MISSING_FORMATTER
# allow-missing-formatter = true
# The file into which a cpu profile will be written
# Env $TREEFMT_CPU_PROFILE
# cpu-profile = ./cpu.pprof
# Exclude files or directories matching the specified globs
# Env $TREEFMT_EXCLUDES
# excludes = ["*.md", "*.gif"]
# Exit with error if any changes were made during execution
# Useful for CI
# Env $TREEFMT_FAIL_ON_CHANGE
# fail-on-change = true
# A list of formatters to apply
# Defaults to all configured formatters
# Env $TREEFMT_FORMATTERS
# formatters = ["gofmt", "prettier"]
# Log paths that did not match any formatters at the specified log level
# Possible values are <debug|info|warn|error|fatal>
# Env $TREEFMT_ON_UNMATCHED
# on-unmatched = "info"
# The root directory from which treefmt will start walking the filesystem
# Defaults to the directory containing the config file
# Env $TREEFMT_TREE_ROOT
# tree-root = "/tmp/foo"
# File to search for to find the tree root (if tree-root is not set)
# Env $TREEFMT_TREE_ROOT_FILE
# tree-root-file = ".git/config"
# Set the verbosity of logs
# 0 = warn, 1 = info, 2 = debug
# Env $TREEFMT_VERBOSE
# verbose = 2
# The method used to traverse the files within the tree root
# Currently, we support 'auto', 'git' or 'filesystem'
# Env $TREEFMT_WALK
# walk = "filesystem"
[formatter.nix]
command = "nixfmt"
includes = ["*.nix"]
[formatter.rust]
command = "rustfmt"
options = ["--edition", "2021"]
includes = ["*.rs"]