diff --git a/Cargo.lock b/Cargo.lock index 7b3df40..eb2576e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,12 +93,6 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" -[[package]] -name = "base64ct" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" - [[package]] name = "benefice" version = "0.1.0" @@ -109,6 +103,7 @@ dependencies = [ "num_cpus", "once_cell", "openidconnect", + "serde", "tempfile", "tokio", "toml", @@ -118,15 +113,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -148,12 +134,6 @@ version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - [[package]] name = "bytes" version = "1.1.0" @@ -227,12 +207,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "const-oid" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" - [[package]] name = "cpufeatures" version = "0.2.2" @@ -251,16 +225,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "crypto-common" version = "0.1.3" @@ -271,16 +235,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "der" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" -dependencies = [ - "const-oid", - "crypto-bigint", -] - [[package]] name = "digest" version = "0.10.3" @@ -291,25 +245,6 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "drawbridge-auth" -version = "0.1.0" -dependencies = [ - "axum", - "base64", - "bincode", - "hyper", - "oauth2", - "rand", - "regex", - "rsa", - "serde", - "serde_json", - "tokio", - "tower", - "ureq", -] - [[package]] name = "either" version = "1.6.1" @@ -584,9 +519,6 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] [[package]] name = "libc" @@ -594,12 +526,6 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" -[[package]] -name = "libm" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" - [[package]] name = "log" version = "0.4.17" @@ -692,23 +618,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -719,17 +628,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.15" @@ -737,7 +635,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -853,28 +750,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" -dependencies = [ - "der", - "pkcs8", - "zeroize", -] - -[[package]] -name = "pkcs8" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" -dependencies = [ - "der", - "spki", - "zeroize", -] - [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1010,26 +885,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rsa" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" -dependencies = [ - "byteorder", - "digest", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "smallvec", - "subtle", - "zeroize", -] - [[package]] name = "rustls" version = "0.20.6" @@ -1188,22 +1043,6 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" -[[package]] -name = "spki" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - [[package]] name = "syn" version = "1.0.98" @@ -1474,8 +1313,6 @@ dependencies = [ "log", "once_cell", "rustls", - "serde", - "serde_json", "url", "webpki", "webpki-roots", @@ -1678,9 +1515,3 @@ name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "zeroize" -version = "1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07" diff --git a/Cargo.nix b/Cargo.nix index 9b935a7..ee196b4 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -4,7 +4,6 @@ args@{ release ? true, rootFeatures ? [ - "drawbridge-auth/default" "benefice/default" ], rustPackages, @@ -38,7 +37,6 @@ in { cargo2nixVersion = "0.11.0"; workspace = { - drawbridge-auth = rustPackages.unknown.drawbridge-auth."0.1.0"; benefice = rustPackages.unknown.benefice."0.1.0"; }; "registry+https://github.com/rust-lang/crates.io-index".adler."1.0.2" = overridableMkRustCrate (profileName: rec { @@ -162,16 +160,6 @@ in ]; }); - "registry+https://github.com/rust-lang/crates.io-index".base64ct."1.5.0" = overridableMkRustCrate (profileName: rec { - name = "base64ct"; - version = "1.5.0"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179"; }; - features = builtins.concatLists [ - [ "alloc" ] - ]; - }); - "unknown".benefice."0.1.0" = overridableMkRustCrate (profileName: rec { name = "benefice"; version = "0.1.0"; @@ -184,6 +172,7 @@ in num_cpus = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num_cpus."1.13.1" { inherit profileName; }; once_cell = rustPackages."registry+https://github.com/rust-lang/crates.io-index".once_cell."1.12.0" { inherit profileName; }; openidconnect = rustPackages."registry+https://github.com/rust-lang/crates.io-index".openidconnect."2.3.1" { inherit profileName; }; + serde = rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.137" { inherit profileName; }; tempfile = rustPackages."registry+https://github.com/rust-lang/crates.io-index".tempfile."3.3.0" { inherit profileName; }; tokio = rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.19.2" { inherit profileName; }; toml = rustPackages."registry+https://github.com/rust-lang/crates.io-index".toml."0.5.9" { inherit profileName; }; @@ -194,16 +183,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".bincode."1.3.3" = overridableMkRustCrate (profileName: rec { - name = "bincode"; - version = "1.3.3"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"; }; - dependencies = { - serde = rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.137" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".bitflags."1.3.2" = overridableMkRustCrate (profileName: rec { name = "bitflags"; version = "1.3.2"; @@ -234,13 +213,6 @@ in ]; }); - "registry+https://github.com/rust-lang/crates.io-index".byteorder."1.4.3" = overridableMkRustCrate (profileName: rec { - name = "byteorder"; - version = "1.4.3"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"; }; - }); - "registry+https://github.com/rust-lang/crates.io-index".bytes."1.1.0" = overridableMkRustCrate (profileName: rec { name = "bytes"; version = "1.1.0"; @@ -342,13 +314,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".const-oid."0.7.1" = overridableMkRustCrate (profileName: rec { - name = "const-oid"; - version = "0.7.1"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"; }; - }); - "registry+https://github.com/rust-lang/crates.io-index".cpufeatures."0.2.2" = overridableMkRustCrate (profileName: rec { name = "cpufeatures"; version = "0.2.2"; @@ -373,20 +338,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".crypto-bigint."0.3.2" = overridableMkRustCrate (profileName: rec { - name = "crypto-bigint"; - version = "0.3.2"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"; }; - features = builtins.concatLists [ - [ "generic-array" ] - ]; - dependencies = { - generic_array = rustPackages."registry+https://github.com/rust-lang/crates.io-index".generic-array."0.14.5" { inherit profileName; }; - subtle = rustPackages."registry+https://github.com/rust-lang/crates.io-index".subtle."2.4.1" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".crypto-common."0.1.3" = overridableMkRustCrate (profileName: rec { name = "crypto-common"; version = "0.1.3"; @@ -401,24 +352,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".der."0.5.1" = overridableMkRustCrate (profileName: rec { - name = "der"; - version = "0.5.1"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"; }; - features = builtins.concatLists [ - [ "alloc" ] - [ "bigint" ] - [ "const-oid" ] - [ "crypto-bigint" ] - [ "oid" ] - ]; - dependencies = { - const_oid = rustPackages."registry+https://github.com/rust-lang/crates.io-index".const-oid."0.7.1" { inherit profileName; }; - crypto_bigint = rustPackages."registry+https://github.com/rust-lang/crates.io-index".crypto-bigint."0.3.2" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".digest."0.10.3" = overridableMkRustCrate (profileName: rec { name = "digest"; version = "0.10.3"; @@ -437,30 +370,6 @@ in }; }); - "unknown".drawbridge-auth."0.1.0" = overridableMkRustCrate (profileName: rec { - name = "drawbridge-auth"; - version = "0.1.0"; - registry = "unknown"; - src = fetchCrateLocal (workspaceSrc + "/crates/auth"); - dependencies = { - axum = rustPackages."registry+https://github.com/rust-lang/crates.io-index".axum."0.5.9" { inherit profileName; }; - base64 = rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.13.0" { inherit profileName; }; - bincode = rustPackages."registry+https://github.com/rust-lang/crates.io-index".bincode."1.3.3" { inherit profileName; }; - oauth2 = rustPackages."registry+https://github.com/rust-lang/crates.io-index".oauth2."4.2.0" { inherit profileName; }; - rand = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }; - rsa = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rsa."0.6.1" { inherit profileName; }; - serde = rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.137" { inherit profileName; }; - serde_json = rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde_json."1.0.81" { inherit profileName; }; - ureq = rustPackages."registry+https://github.com/rust-lang/crates.io-index".ureq."2.4.0" { inherit profileName; }; - }; - devDependencies = { - hyper = rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."0.14.19" { inherit profileName; }; - regex = rustPackages."registry+https://github.com/rust-lang/crates.io-index".regex."1.5.6" { inherit profileName; }; - tokio = rustPackages."registry+https://github.com/rust-lang/crates.io-index".tokio."1.19.2" { inherit profileName; }; - tower = rustPackages."registry+https://github.com/rust-lang/crates.io-index".tower."0.4.13" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".either."1.6.1" = overridableMkRustCrate (profileName: rec { name = "either"; version = "1.6.1"; @@ -835,13 +744,6 @@ in version = "1.4.0"; registry = "registry+https://github.com/rust-lang/crates.io-index"; src = fetchCratesIo { inherit name version; sha256 = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"; }; - features = builtins.concatLists [ - [ "spin" ] - [ "spin_no_std" ] - ]; - dependencies = { - spin = rustPackages."registry+https://github.com/rust-lang/crates.io-index".spin."0.5.2" { inherit profileName; }; - }; }); "registry+https://github.com/rust-lang/crates.io-index".libc."0.2.126" = overridableMkRustCrate (profileName: rec { @@ -855,16 +757,6 @@ in ]; }); - "registry+https://github.com/rust-lang/crates.io-index".libm."0.2.2" = overridableMkRustCrate (profileName: rec { - name = "libm"; - version = "0.2.2"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"; }; - features = builtins.concatLists [ - [ "default" ] - ]; - }); - "registry+https://github.com/rust-lang/crates.io-index".log."0.4.17" = overridableMkRustCrate (profileName: rec { name = "log"; version = "0.4.17"; @@ -994,31 +886,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".num-bigint-dig."0.8.1" = overridableMkRustCrate (profileName: rec { - name = "num-bigint-dig"; - version = "0.8.1"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011"; }; - features = builtins.concatLists [ - [ "i128" ] - [ "prime" ] - [ "rand" ] - [ "u64_digit" ] - [ "zeroize" ] - ]; - dependencies = { - byteorder = rustPackages."registry+https://github.com/rust-lang/crates.io-index".byteorder."1.4.3" { inherit profileName; }; - lazy_static = rustPackages."registry+https://github.com/rust-lang/crates.io-index".lazy_static."1.4.0" { inherit profileName; }; - libm = rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.2" { inherit profileName; }; - num_integer = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-integer."0.1.45" { inherit profileName; }; - num_iter = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-iter."0.1.43" { inherit profileName; }; - num_traits = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-traits."0.2.15" { inherit profileName; }; - rand = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }; - smallvec = rustPackages."registry+https://github.com/rust-lang/crates.io-index".smallvec."1.8.0" { inherit profileName; }; - zeroize = rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.5.5" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".num-integer."0.1.45" = overridableMkRustCrate (profileName: rec { name = "num-integer"; version = "0.1.45"; @@ -1036,20 +903,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".num-iter."0.1.43" = overridableMkRustCrate (profileName: rec { - name = "num-iter"; - version = "0.1.43"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"; }; - dependencies = { - num_integer = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-integer."0.1.45" { inherit profileName; }; - num_traits = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-traits."0.2.15" { inherit profileName; }; - }; - buildDependencies = { - autocfg = buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".autocfg."1.1.0" { profileName = "__noProfile"; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".num-traits."0.2.15" = overridableMkRustCrate (profileName: rec { name = "num-traits"; version = "0.2.15"; @@ -1057,12 +910,8 @@ in src = fetchCratesIo { inherit name version; sha256 = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"; }; features = builtins.concatLists [ [ "i128" ] - [ "libm" ] [ "std" ] ]; - dependencies = { - libm = rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.2" { inherit profileName; }; - }; buildDependencies = { autocfg = buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".autocfg."1.1.0" { profileName = "__noProfile"; }; }; @@ -1121,6 +970,9 @@ in version = "2.3.1"; registry = "registry+https://github.com/rust-lang/crates.io-index"; src = fetchCratesIo { inherit name version; sha256 = "32f73e47a1766acd7bedd605742a1a2651c111f34ed3e0be117d8651432d509c"; }; + features = builtins.concatLists [ + [ "ureq" ] + ]; dependencies = { base64 = rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.13.0" { inherit profileName; }; chrono = rustPackages."registry+https://github.com/rust-lang/crates.io-index".chrono."0.4.19" { inherit profileName; }; @@ -1208,39 +1060,6 @@ in src = fetchCratesIo { inherit name version; sha256 = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"; }; }); - "registry+https://github.com/rust-lang/crates.io-index".pkcs1."0.3.3" = overridableMkRustCrate (profileName: rec { - name = "pkcs1"; - version = "0.3.3"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320"; }; - features = builtins.concatLists [ - [ "alloc" ] - [ "pkcs8" ] - [ "zeroize" ] - ]; - dependencies = { - der = rustPackages."registry+https://github.com/rust-lang/crates.io-index".der."0.5.1" { inherit profileName; }; - pkcs8 = rustPackages."registry+https://github.com/rust-lang/crates.io-index".pkcs8."0.8.0" { inherit profileName; }; - zeroize = rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.5.5" { inherit profileName; }; - }; - }); - - "registry+https://github.com/rust-lang/crates.io-index".pkcs8."0.8.0" = overridableMkRustCrate (profileName: rec { - name = "pkcs8"; - version = "0.8.0"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"; }; - features = builtins.concatLists [ - [ "alloc" ] - [ "zeroize" ] - ]; - dependencies = { - der = rustPackages."registry+https://github.com/rust-lang/crates.io-index".der."0.5.1" { inherit profileName; }; - spki = rustPackages."registry+https://github.com/rust-lang/crates.io-index".spki."0.5.4" { inherit profileName; }; - zeroize = rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.5.5" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".ppv-lite86."0.2.16" = overridableMkRustCrate (profileName: rec { name = "ppv-lite86"; version = "0.2.16"; @@ -1455,27 +1274,6 @@ in }; }); - "registry+https://github.com/rust-lang/crates.io-index".rsa."0.6.1" = overridableMkRustCrate (profileName: rec { - name = "rsa"; - version = "0.6.1"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b"; }; - dependencies = { - byteorder = rustPackages."registry+https://github.com/rust-lang/crates.io-index".byteorder."1.4.3" { inherit profileName; }; - digest = rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.3" { inherit profileName; }; - num_bigint = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-bigint-dig."0.8.1" { inherit profileName; }; - num_integer = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-integer."0.1.45" { inherit profileName; }; - num_iter = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-iter."0.1.43" { inherit profileName; }; - num_traits = rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-traits."0.2.15" { inherit profileName; }; - pkcs1 = rustPackages."registry+https://github.com/rust-lang/crates.io-index".pkcs1."0.3.3" { inherit profileName; }; - pkcs8 = rustPackages."registry+https://github.com/rust-lang/crates.io-index".pkcs8."0.8.0" { inherit profileName; }; - rand_core = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand_core."0.6.3" { inherit profileName; }; - smallvec = rustPackages."registry+https://github.com/rust-lang/crates.io-index".smallvec."1.8.0" { inherit profileName; }; - subtle = rustPackages."registry+https://github.com/rust-lang/crates.io-index".subtle."2.4.1" { inherit profileName; }; - zeroize = rustPackages."registry+https://github.com/rust-lang/crates.io-index".zeroize."1.5.5" { inherit profileName; }; - }; - }); - "registry+https://github.com/rust-lang/crates.io-index".rustls."0.20.6" = overridableMkRustCrate (profileName: rec { name = "rustls"; version = "0.20.6"; @@ -1686,28 +1484,6 @@ in ]; }); - "registry+https://github.com/rust-lang/crates.io-index".spki."0.5.4" = overridableMkRustCrate (profileName: rec { - name = "spki"; - version = "0.5.4"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"; }; - features = builtins.concatLists [ - [ "alloc" ] - [ "base64ct" ] - ]; - dependencies = { - base64ct = rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64ct."1.5.0" { inherit profileName; }; - der = rustPackages."registry+https://github.com/rust-lang/crates.io-index".der."0.5.1" { inherit profileName; }; - }; - }); - - "registry+https://github.com/rust-lang/crates.io-index".subtle."2.4.1" = overridableMkRustCrate (profileName: rec { - name = "subtle"; - version = "2.4.1"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"; }; - }); - "registry+https://github.com/rust-lang/crates.io-index".syn."1.0.98" = overridableMkRustCrate (profileName: rec { name = "syn"; version = "1.0.98"; @@ -2103,10 +1879,7 @@ in [ "default" ] [ "flate2" ] [ "gzip" ] - [ "json" ] [ "rustls" ] - [ "serde" ] - [ "serde_json" ] [ "tls" ] [ "webpki" ] [ "webpki-roots" ] @@ -2118,8 +1891,6 @@ in log = rustPackages."registry+https://github.com/rust-lang/crates.io-index".log."0.4.17" { inherit profileName; }; once_cell = rustPackages."registry+https://github.com/rust-lang/crates.io-index".once_cell."1.12.0" { inherit profileName; }; rustls = rustPackages."registry+https://github.com/rust-lang/crates.io-index".rustls."0.20.6" { inherit profileName; }; - serde = rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde."1.0.137" { inherit profileName; }; - serde_json = rustPackages."registry+https://github.com/rust-lang/crates.io-index".serde_json."1.0.81" { inherit profileName; }; url = rustPackages."registry+https://github.com/rust-lang/crates.io-index".url."2.2.2" { inherit profileName; }; webpki = rustPackages."registry+https://github.com/rust-lang/crates.io-index".webpki."0.22.0" { inherit profileName; }; webpki_roots = rustPackages."registry+https://github.com/rust-lang/crates.io-index".webpki-roots."0.22.3" { inherit profileName; }; @@ -2423,15 +2194,4 @@ in src = fetchCratesIo { inherit name version; sha256 = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"; }; }); - "registry+https://github.com/rust-lang/crates.io-index".zeroize."1.5.5" = overridableMkRustCrate (profileName: rec { - name = "zeroize"; - version = "1.5.5"; - registry = "registry+https://github.com/rust-lang/crates.io-index"; - src = fetchCratesIo { inherit name version; sha256 = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07"; }; - features = builtins.concatLists [ - [ "alloc" ] - [ "default" ] - ]; - }); - } diff --git a/Cargo.toml b/Cargo.toml index 27fb37c..8b12936 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,13 +3,8 @@ name = "benefice" version = "0.1.0" edition = "2021" -[workspace] -members = [ - "crates/auth", -] - [dependencies] -axum = { version = "0.5.5", features = ["multipart"] } +axum = { version = "0.5.5", features = ["multipart", "headers"] } tokio = { version = "1.19.2", features = ["macros", "process", "rt-multi-thread", "io-util", "sync"] } tracing = { version = "0.1.35", default-features = false, features = ["std", "release_max_level_info"] } tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } @@ -19,6 +14,7 @@ once_cell = "1.12.0" tempfile = "3.3.0" anyhow = { version = "1.0.57", default-features = false, features = ["std"] } clap = { version = "3.2.3", default-features = false, features = ["derive", "std"] } -openidconnect = { version = "2.3.1", default-features = false } +openidconnect = { version = "2.3.1", default-features = false, features = ["ureq"] } toml = { version = "0.5.9", default-features = false } num_cpus = "1.13.1" +serde = { version = "1.0.136", default-features = false } diff --git a/crates/auth/Cargo.toml b/crates/auth/Cargo.toml deleted file mode 100644 index e3e9695..0000000 --- a/crates/auth/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "drawbridge-auth" -version = "0.1.0" -authors = ["Profian Inc"] -edition = "2021" - -[dependencies] -# Internal dependencies -# External dependencies -axum = { version = "0.5.4", default-features = false, features = ["headers", "query"] } -base64 = { version = "0.13", default-features = false } -bincode = { version = "1", default-features = true } -oauth2 = { version = "4.1", default-features = false, features = ["ureq"] } -rand = { version = "0.8", default-features = false } -rsa = { version = "0.6", default-features = false } -serde = { version = "1.0.136", default-features = false } -serde_json = { version = "1.0.79", default-features = false } -ureq = { version = "2.4.0", default-features = false, features = ["json", "tls"] } - -[dev-dependencies] -hyper = { version = "0.14.18", default-features = false } -regex = { version = "1", default-features = false, features = ["std"] } -tokio = { version = "1.17.0", default-features = false, features = ["macros", "rt"] } -tower = { version = "0.4.12", default-features = false } diff --git a/crates/auth/build.rs b/crates/auth/build.rs deleted file mode 100644 index 3a30149..0000000 --- a/crates/auth/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -fn main() { - if option_env!("GITHUB_TOKEN").is_some() { - println!("cargo:rustc-cfg=has_github_token"); - } -} diff --git a/crates/auth/rsa2048-priv.der b/crates/auth/rsa2048-priv.der deleted file mode 100644 index f4590bb..0000000 Binary files a/crates/auth/rsa2048-priv.der and /dev/null differ diff --git a/crates/auth/src/builder.rs b/crates/auth/src/builder.rs deleted file mode 100644 index 90a3896..0000000 --- a/crates/auth/src/builder.rs +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::providers; - -use axum::routing::get; -use axum::{Extension, Router}; - -#[derive(Default)] -pub struct Builder { - host: String, - github: Option<(String, String)>, -} - -impl Builder { - /// Constructs a new [Builder]. - /// The host is the URL the server will run on (used for redirects). - pub fn new(host: String) -> Self { - Self { host, github: None } - } - - /// The github client id and client secret to use. - pub fn github(self, client_id: String, client_secret: String) -> Self { - Self { - github: Some((client_id, client_secret)), - ..self - } - } - - /// Builds the application and returns Drawbridge instance as a [tower::MakeService]. - pub fn build(self) -> Router { - let mut router = Router::new(); - - if let Some((client_id, client_secret)) = self.github { - router = router - .route( - providers::github::AUTHORIZED_URI, - get(providers::github::routes::authorized), - ) - .route( - providers::github::LOGIN_URI, - get(providers::github::routes::login), - ) - .layer(Extension(providers::github::OAuthClient::new( - &self.host, - client_id, - client_secret, - ))); - } - router - } -} diff --git a/crates/auth/src/lib.rs b/crates/auth/src/lib.rs deleted file mode 100644 index c2ce143..0000000 --- a/crates/auth/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -mod builder; -mod providers; -mod redirect; -mod session; - -pub use builder::Builder; -pub use providers::Provider; -pub use redirect::AuthRedirectRoot; -pub use session::{Session, COOKIE_NAME}; diff --git a/crates/auth/src/providers/github/mod.rs b/crates/auth/src/providers/github/mod.rs deleted file mode 100644 index f4b56f5..0000000 --- a/crates/auth/src/providers/github/mod.rs +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -pub mod routes; - -use super::super::session::Session; - -use std::{fmt, io}; - -use axum::http::header::{AUTHORIZATION, USER_AGENT}; -use axum::http::status::InvalidStatusCode; -use axum::http::StatusCode; -use oauth2::basic::BasicClient; -use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; -use serde::Deserialize; - -pub const LOGIN_URI: &str = "/github"; -pub const AUTHORIZED_URI: &str = "/github/authorized"; - -#[derive(Debug)] -pub enum ValidateError { - Http(ureq::Error), - Json(io::Error), - InvalidStatusCode(InvalidStatusCode), - GitHub(String), -} - -impl fmt::Display for ValidateError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "session {}", - match self { - ValidateError::Http(e) => format!("HTTP request error: {}", e), - ValidateError::Json(e) => format!("JSON deserialization error: {}", e), - ValidateError::InvalidStatusCode(e) => format!("invalid status code: {}", e), - ValidateError::GitHub(e) => format!("github error: {}", e), - } - ) - } -} - -impl std::error::Error for ValidateError {} - -#[derive(Deserialize)] -pub struct GitHubUser { - #[serde(rename = "login")] - pub username: String, - pub id: u64, -} - -pub async fn validate(session: &Session) -> Result { - #[derive(Deserialize)] - struct Error { - message: String, - } - - let res = ureq::get("https://api.github.com/user") - .set(USER_AGENT.as_str(), "drawbridge") - .set( - AUTHORIZATION.as_str(), - &format!("Bearer {}", session.token.secret()), - ) - .call() - .map_err(ValidateError::Http)?; - match StatusCode::from_u16(res.status()) { - Ok(s) if s.is_success() => res.into_json().map_err(ValidateError::Json), - Ok(_) => res - .into_json() - .map_err(ValidateError::Json) - .and_then(|Error { message }| Err(ValidateError::GitHub(message))), - Err(e) => Err(ValidateError::InvalidStatusCode(e)), - } -} - -#[derive(Clone)] -pub struct OAuthClient(pub BasicClient); - -impl OAuthClient { - pub fn new(host: &str, client_id: String, client_secret: String) -> OAuthClient { - const AUTH_URL: &str = "https://github.com/login/oauth/authorize?response_type=code"; - const TOKEN_URL: &str = "https://github.com/login/oauth/access_token"; - - OAuthClient( - BasicClient::new( - ClientId::new(client_id), - Some(ClientSecret::new(client_secret)), - AuthUrl::new(AUTH_URL.to_string()).unwrap(), - Some(TokenUrl::new(TOKEN_URL.to_string()).unwrap()), - ) - .set_redirect_uri( - RedirectUrl::new(format!("http://{}{}", host, AUTHORIZED_URI)).unwrap(), - ), - ) - } -} diff --git a/crates/auth/src/providers/github/routes.rs b/crates/auth/src/providers/github/routes.rs deleted file mode 100644 index f594133..0000000 --- a/crates/auth/src/providers/github/routes.rs +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::super::Provider; -use super::OAuthClient; -use crate::session::Session; - -use axum::extract::{Extension, Query}; -use axum::response::{IntoResponse, Redirect}; -use oauth2::basic::BasicClient; -use oauth2::ureq::http_client; -use oauth2::{AuthorizationCode, CsrfToken, Scope, TokenResponse}; -use rsa::RsaPrivateKey; -use serde::Deserialize; - -#[derive(Debug, Deserialize)] -pub struct AuthRequest { - pub code: String, - pub state: String, -} - -/// Authenticate with GitHub OAuth. -pub async fn login(Extension(OAuthClient(client)): Extension) -> impl IntoResponse { - let (auth_url, _csrf_token) = client - .authorize_url(CsrfToken::new_random) - .add_scope(Scope::new("identify".to_string())) - .url(); - - Redirect::to(auth_url.as_str()) -} - -/// Prepare an encrypted token for GitHub OAuth. -pub async fn authorized( - query: Query, - oauth_client: Extension, - key: Extension, -) -> Result { - let token = oauth_client - .exchange_code(AuthorizationCode::new(query.code.clone())) - .request(http_client) - .map_err(|e| format!("Failed to get token: {}", e))?; - - Session::new(Provider::GitHub, token.access_token().clone()) - .encrypt(&key.0) - .map_err(|e| format!("Failed to encrypt token: {}", e)) -} diff --git a/crates/auth/src/providers/mod.rs b/crates/auth/src/providers/mod.rs deleted file mode 100644 index 61fced3..0000000 --- a/crates/auth/src/providers/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -pub mod github; - -use std::fmt; - -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub enum Provider { - GitHub, -} - -impl fmt::Display for Provider { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Provider::GitHub => "GitHub.com", - } - ) - } -} - -#[cfg(test)] -mod tests { - use super::Provider; - - #[test] - fn auth_type_display() { - assert_eq!(format!("{}", Provider::GitHub), "GitHub.com"); - } -} diff --git a/crates/auth/src/redirect.rs b/crates/auth/src/redirect.rs deleted file mode 100644 index 2469582..0000000 --- a/crates/auth/src/redirect.rs +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::providers::github; - -use axum::response::{self, IntoResponse, Response}; - -#[derive(Clone)] -pub struct AuthRedirectRoot(pub String); - -impl AuthRedirectRoot { - pub fn error(&self, error: String) -> AuthRedirect { - AuthRedirect { - root: self.0.clone(), - error: Some(error), - } - } - - pub fn no_error(&self) -> AuthRedirect { - AuthRedirect { - root: self.0.clone(), - error: None, - } - } -} - -pub struct AuthRedirect { - pub root: String, - pub error: Option, -} - -impl IntoResponse for AuthRedirect { - fn into_response(self) -> Response { - // TODO: redirect the user to a general purpose sign in page so they can choose what to login with, show the user the message in the error field: https://github.com/profianinc/drawbridge/issues/50 - response::Redirect::temporary(&format!("{}{}", self.root, github::LOGIN_URI)) - .into_response() - } -} diff --git a/crates/auth/src/session.rs b/crates/auth/src/session.rs deleted file mode 100644 index b40a880..0000000 --- a/crates/auth/src/session.rs +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::providers::{github, Provider}; -use super::redirect::{AuthRedirect, AuthRedirectRoot}; - -use std::fmt; - -use axum::async_trait; -use axum::extract::rejection::TypedHeaderRejectionReason; -use axum::extract::{Extension, FromRequest, RequestParts, TypedHeader}; -use axum::headers; -use axum::http::header::COOKIE; -use oauth2::AccessToken; -use rand::rngs::OsRng; -use rsa::{PaddingScheme, PublicKey, RsaPrivateKey}; -use serde::{Deserialize, Serialize}; - -pub const COOKIE_NAME: &str = "SESSION"; - -#[derive(Debug)] -pub enum EncryptError { - Encrypt(rsa::errors::Error), - Serialize(bincode::Error), -} - -impl fmt::Display for EncryptError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "session {}", - match self { - EncryptError::Encrypt(e) => format!("encryption error: {}", e), - EncryptError::Serialize(e) => format!("serialization error: {}", e), - } - ) - } -} - -impl std::error::Error for EncryptError {} - -#[derive(Debug)] -pub enum DecryptError { - Decode(base64::DecodeError), - Decrypt(rsa::errors::Error), - Deserialize(bincode::Error), -} - -impl fmt::Display for DecryptError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "session {}", - match self { - DecryptError::Decode(e) => format!("decode error: {}", e), - DecryptError::Decrypt(e) => format!("decryption error: {}", e), - DecryptError::Deserialize(e) => format!("deserialization error: {}", e), - } - ) - } -} - -impl std::error::Error for DecryptError {} - -// TODO: consider protections against potential replay attacks: https://github.com/profianinc/drawbridge/issues/112 -#[derive(Debug, Serialize, Deserialize)] -pub struct Session { - pub provider: Provider, - pub token: AccessToken, - pub username: Option, - pub user_id: Option, -} - -impl Session { - /// Create a new session instance. - pub fn new(provider: Provider, token: AccessToken) -> Self { - Self { - provider, - token, - username: None, - user_id: None, - } - } - - pub fn set_user_info(mut self, username: String, user_id: String) -> Self { - self.username = Some(username); - self.user_id = Some(user_id); - self - } - - /// Encrypt the session so that it can be securely stored by the user. - pub fn encrypt(&self, key: &RsaPrivateKey) -> Result { - let bytes = bincode::serialize(self).map_err(EncryptError::Serialize)?; - key.encrypt( - &mut OsRng, - PaddingScheme::new_pkcs1v15_encrypt(), - &bytes[..], - ) - .map_err(EncryptError::Encrypt) - .map(base64::encode) - } - - /// Decrypt an untrusted user token to guarantee it was not modified by the user. - pub fn decrypt(string: &str, key: &RsaPrivateKey) -> Result { - let bytes = base64::decode(string).map_err(DecryptError::Decode)?; - let bytes = key - .decrypt(PaddingScheme::new_pkcs1v15_encrypt(), &bytes) - .map_err(DecryptError::Decrypt)?; - bincode::deserialize(&bytes).map_err(DecryptError::Deserialize) - } -} - -impl fmt::Display for Session { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "(Session via {}, username: {:?}, user_id: {:?})", - self.provider, self.username, self.user_id - ) - } -} - -#[async_trait] -impl FromRequest for Session -where - B: Send, -{ - type Rejection = AuthRedirect; - - async fn from_request(req: &mut RequestParts) -> Result { - let redirect = Extension::::from_request(req) - .await - .unwrap(); - let key = Extension::::from_request(req).await.unwrap(); - let cookies = TypedHeader::::from_request(req) - .await - .map_err(|e| match *e.name() { - COOKIE => match e.reason() { - TypedHeaderRejectionReason::Missing => redirect.no_error(), - TypedHeaderRejectionReason::Error(e) => { - redirect.error(format!("Failed to parse HTTP headers: {}", e)) - } - _ => redirect.no_error(), - }, - _ => redirect.no_error(), - })?; - - let session_data = cookies - .get(COOKIE_NAME) - .ok_or_else(|| redirect.no_error())?; - let session = Session::decrypt(session_data, &key.0).map_err(|_| redirect.no_error())?; - - match session.provider { - Provider::GitHub => github::validate(&session) - .await - .map(|github| session.set_user_info(github.username, github.id.to_string())) - .map_err(|_| redirect.no_error()), - } - } -} - -#[cfg(test)] -mod tests { - use super::super::providers::Provider; - use super::Session; - - use oauth2::AccessToken; - use rsa::RsaPrivateKey; - - #[test] - fn session_display() { - assert_eq!( - format!( - "{}", - Session::new(Provider::GitHub, AccessToken::new("some_token".to_owned()),) - .set_user_info("some_user".to_string(), "123".to_string()) - ), - "(Session via GitHub.com, username: Some(\"some_user\"), user_id: Some(\"123\"))" - ); - } - - #[test] - fn session_encrypt_decrypt() { - use rsa::pkcs8::DecodePrivateKey; - - let key = RsaPrivateKey::from_pkcs8_der(include_bytes!("../rsa2048-priv.der")).unwrap(); - let session = Session::new(Provider::GitHub, AccessToken::new("some_token".to_owned())); - - assert_eq!( - serde_json::to_string( - &Session::decrypt(&session.encrypt(&key).unwrap(), &key).unwrap() - ) - .unwrap(), - serde_json::to_string(&session).unwrap() - ); - } -} diff --git a/crates/auth/tests/integration.rs b/crates/auth/tests/integration.rs deleted file mode 100644 index 5b2e947..0000000 --- a/crates/auth/tests/integration.rs +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -mod protected; -mod providers; -mod status; - -use drawbridge_auth::{AuthRedirectRoot, Builder}; - -use axum::extract::Extension; -use axum::routing::get; -use axum::Router; -use rsa::pkcs8::DecodePrivateKey; -use rsa::RsaPrivateKey; - -pub const STATUS: &str = "/status"; -pub const PROTECTED: &str = "/protected"; - -pub fn test_app(host: String) -> Router { - // TODO: generate this key at runtime or pull the path from the command line args: https://github.com/profianinc/drawbridge/issues/18 - let key = RsaPrivateKey::from_pkcs8_der(include_bytes!("../rsa2048-priv.der")).unwrap(); - - Router::new() - .nest( - "/auth", - Builder::new(host.clone()) - .github("unused".to_string(), "unused".to_string()) - .build(), - ) - .route(STATUS, get(status::status)) - .route(PROTECTED, get(protected::protected)) - .layer(Extension(key)) - .layer(Extension(AuthRedirectRoot(host))) -} diff --git a/crates/auth/tests/protected/mod.rs b/crates/auth/tests/protected/mod.rs deleted file mode 100644 index cbfc1fb..0000000 --- a/crates/auth/tests/protected/mod.rs +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::{test_app, PROTECTED}; - -use drawbridge_auth::{Provider, Session, COOKIE_NAME}; - -use axum::http::{Request, StatusCode}; -use axum::response::IntoResponse; -use hyper::Body; -use oauth2::AccessToken; -use rsa::pkcs8::DecodePrivateKey; -use rsa::RsaPrivateKey; -use tower::util::ServiceExt; - -/// This is just an example of how to implement endpoints behind OAuth. -pub async fn protected(session: Session) -> impl IntoResponse { - format!( - "Welcome to the protected area\nHere's your info:\n{:?}", - session - ) -} - -#[tokio::test] -#[cfg_attr(not(has_github_token), ignore)] -async fn protected_authenticated() { - let key = RsaPrivateKey::from_pkcs8_der(include_bytes!("../../rsa2048-priv.der")).unwrap(); - let session = Session::new( - Provider::GitHub, - AccessToken::new(std::env::var("GITHUB_TOKEN").unwrap()), - ); - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri(PROTECTED) - .header( - "Cookie", - format!("{}={}", COOKIE_NAME, session.encrypt(&key).unwrap()), - ) - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::OK); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let body = std::str::from_utf8(&body).unwrap(); - assert_eq!( - body, - r#"Welcome to the protected area -Here's your info: -Session { provider: GitHub, token: AccessToken([redacted]) }"# - ); -} - -#[tokio::test] -async fn protected_invalid_token() { - let key = RsaPrivateKey::from_pkcs8_der(include_bytes!("../../rsa2048-priv.der")).unwrap(); - let session = Session::new(Provider::GitHub, AccessToken::new("BAD TOKEN".to_owned())); - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri(PROTECTED) - .header( - "Cookie", - format!("{}={}", COOKIE_NAME, session.encrypt(&key).unwrap()), - ) - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::TEMPORARY_REDIRECT); - assert_eq!( - response - .headers() - .get("Location") - .unwrap() - .to_str() - .unwrap(), - "localhost/auth/github" - ); -} - -#[tokio::test] -async fn protected_no_token() { - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri(PROTECTED) - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::TEMPORARY_REDIRECT); - assert_eq!( - response - .headers() - .get("Location") - .unwrap() - .to_str() - .unwrap(), - "localhost/auth/github" - ); -} diff --git a/crates/auth/tests/providers/github.rs b/crates/auth/tests/providers/github.rs deleted file mode 100644 index 8f42505..0000000 --- a/crates/auth/tests/providers/github.rs +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::super::test_app; - -use std::str; - -use axum::http::{Request, StatusCode}; -use hyper::Body; -use regex::Regex; -use tower::ServiceExt; - -#[tokio::test] -async fn login() { - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri("/auth/github") - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - let url = Regex::new( - r#"https://github.com/login/oauth/authorize\?response_type=code&response_type=code&client_id=(.+)&state=(.+)&redirect_uri=http%3A%2F%2Flocalhost%2Fauth%2Fgithub%2Fauthorized&scope=identify"# - ).unwrap(); - - assert_eq!(response.status(), StatusCode::SEE_OTHER); - assert!(url.is_match( - response - .headers() - .get("Location") - .unwrap() - .to_str() - .unwrap() - )); -} - -#[tokio::test] -async fn authorized() { - // TODO: write a successful test for this endpoint - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri("/auth/github/authorized") - .header("Cookie", format!("SESSION={}", "bad_session")) - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::UNPROCESSABLE_ENTITY); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let body = str::from_utf8(&body).unwrap(); - assert_eq!( - body, - "Failed to deserialize query string. Expected something of type `drawbridge_auth::providers::github::routes::AuthRequest`. Error: missing field `code`" - ); -} diff --git a/crates/auth/tests/providers/mod.rs b/crates/auth/tests/providers/mod.rs deleted file mode 100644 index b886aff..0000000 --- a/crates/auth/tests/providers/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -mod github; diff --git a/crates/auth/tests/status/mod.rs b/crates/auth/tests/status/mod.rs deleted file mode 100644 index 7329884..0000000 --- a/crates/auth/tests/status/mod.rs +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Profian Inc. -// SPDX-License-Identifier: AGPL-3.0-only - -use super::{test_app, STATUS}; - -use std::str; - -use drawbridge_auth::{Provider, Session, COOKIE_NAME}; - -use axum::http::{Request, StatusCode}; -use hyper::Body; -use oauth2::AccessToken; -use rsa::{pkcs8::DecodePrivateKey, RsaPrivateKey}; -use tower::util::ServiceExt; - -/// Check if a user is authenticated. -pub async fn status(session: Option) -> (StatusCode, String) { - match session { - None => (StatusCode::FORBIDDEN, "Not logged in.".to_owned()), - Some(session) => (StatusCode::OK, format!("Logged in with {}", session)), - } -} - -#[tokio::test] -#[cfg_attr(not(has_github_token), ignore)] -async fn status_authenticated() { - let key = RsaPrivateKey::from_pkcs8_der(include_bytes!("../../rsa2048-priv.der")).unwrap(); - let session = Session::new( - Provider::GitHub, - AccessToken::new(std::env::var("GITHUB_TOKEN").unwrap()), - ); - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri(STATUS) - .header( - "Cookie", - format!("{}={}", COOKIE_NAME, session.encrypt(&key).unwrap()), - ) - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::OK); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let body = std::str::from_utf8(&body).unwrap(); - assert_eq!(body, "Logged in with (Session via GitHub.com)"); -} - -#[tokio::test] -async fn status_bad_token() { - let key = RsaPrivateKey::from_pkcs8_der(include_bytes!("../../rsa2048-priv.der")).unwrap(); - let session = Session::new(Provider::GitHub, AccessToken::new("BAD TOKEN".to_owned())); - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot( - Request::builder() - .uri(STATUS) - .header( - "Cookie", - format!("{}={}", COOKIE_NAME, session.encrypt(&key).unwrap()), - ) - .body(Body::empty()) - .unwrap(), - ) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::FORBIDDEN); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let body = str::from_utf8(&body).unwrap(); - assert_eq!(body, "Not logged in."); -} - -#[tokio::test] -async fn status_unauthenticated() { - let app = test_app("localhost/auth".to_owned()); - let response = app - .oneshot(Request::builder().uri(STATUS).body(Body::empty()).unwrap()) - .await - .unwrap(); - - assert_eq!(response.status(), StatusCode::FORBIDDEN); - let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); - let body = str::from_utf8(&body).unwrap(); - assert_eq!(body, "Not logged in."); -} diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..0a80514 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,152 @@ +use std::ops::Deref; + +use axum::extract::{Extension, FromRequest, Query, RequestParts}; +use axum::headers; +use axum::http::header::SET_COOKIE; +use axum::http::{HeaderMap, StatusCode}; +use axum::response::{IntoResponse, Redirect, Response}; +use axum::{async_trait, TypedHeader}; +use openidconnect::core::{CoreClient, CoreResponseType, CoreUserInfoClaims}; +use openidconnect::ureq::http_client; +use openidconnect::{ + AccessToken, AuthenticationFlow, AuthorizationCode, CsrfToken, Nonce, OAuth2TokenResponse, +}; +use serde::Deserialize; +use tracing::{debug, error, trace}; + +const COOKIE_NAME: &str = "SESSION"; + +#[derive(Debug, Deserialize)] +pub struct AuthRequest { + pub code: String, + pub state: String, +} + +#[repr(transparent)] +#[derive(Clone, Debug)] +pub struct Claims(CoreUserInfoClaims); + +impl Deref for Claims { + type Target = CoreUserInfoClaims; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[async_trait] +impl FromRequest for Claims { + type Rejection = Response; + + async fn from_request(req: &mut RequestParts) -> Result { + let cookies = TypedHeader::::from_request(req) + .await + .map_err(|e| { + debug!("failed to retrieve cookies from request: {e}"); + ( + StatusCode::UNAUTHORIZED, + "Failed to get cookies from request", + ) + .into_response() + })?; + + let token = cookies.get(COOKIE_NAME).ok_or_else(|| { + (StatusCode::UNAUTHORIZED, "Authorization failed".to_string()).into_response() + })?; + + let Extension(oidc) = req.extract::>().await.map_err(|e| { + error!("OpenID Connect client extension missing: {e}"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "OpenID Connect client extension missing".to_string(), + ) + .into_response() + })?; + + let token = AccessToken::new(token.into()); + let info_req = oidc.user_info(token, None).map_err(|e| { + error!("failed to construct user info request: {e}"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "OpenID Connect client initialization failed", + ) + .into_response() + })?; + + trace!("request user info"); + let claims = info_req.request(http_client).map_err(|e| { + debug!("failed to request user info: {e}"); + ( + StatusCode::UNAUTHORIZED, + format!("OpenID Connect credential validation failed: {e}"), + ) + .into_response() + })?; + trace!("received user claims: {:?}", claims); + Ok(Self(claims)) + } +} + +pub async fn login(Extension(client): Extension) -> impl IntoResponse { + let (authorize_url, _csrf_state, _nonce) = client + .authorize_url( + AuthenticationFlow::::AuthorizationCode, + CsrfToken::new_random, + Nonce::new_random, + ) + .url(); + + Redirect::temporary(authorize_url.as_str()) +} + +// TODO: invalidate the session on the remote server properly +pub async fn logout() -> Result<(HeaderMap, Redirect), (StatusCode, String)> { + let cookie = format!("{COOKIE_NAME}=; Path=/; Max-Age=0"); + let mut headers = HeaderMap::new(); + headers.insert( + SET_COOKIE, + cookie.parse().map_err(|e| { + error!("failed prepare set cookie header: {e}"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Error preparing headers".to_string(), + ) + })?, + ); + Ok((headers, Redirect::to("/"))) +} + +pub async fn authorized( + query: Query, + Extension(client): Extension, +) -> Result<(HeaderMap, Redirect), (StatusCode, String)> { + let token_response = client + .exchange_code(AuthorizationCode::new(query.code.clone())) + .request(http_client) + .map_err(|e| { + error!("failed to exchange code for auth token: {e}"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to retrieve auth token".to_string(), + ) + })?; + + let access_token = token_response.access_token(); + let cookie = format!( + "{}={}; SameSite=Lax; Path=/", + COOKIE_NAME, + access_token.secret() + ); + let mut headers = HeaderMap::new(); + headers.insert( + SET_COOKIE, + cookie.parse().map_err(|e| { + error!("failed prepare set cookie header: {e}"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "Error preparing headers".to_string(), + ) + })?, + ); + Ok((headers, Redirect::to("/"))) +} diff --git a/src/main.rs b/src/main.rs index 16318a5..0f90b39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ #![forbid(unsafe_code)] #![warn(clippy::all, rust_2018_idioms, unused_lifetimes)] +mod auth; + use std::collections::HashMap; use std::fs::read; use std::io::Write; @@ -12,6 +14,7 @@ use std::process::Stdio; use std::sync::Arc; use std::time::Duration; +use axum::extract::Extension; use axum::extract::Path; use axum::http::StatusCode; use axum::response::IntoResponse; @@ -22,7 +25,10 @@ use axum::{Router, Server}; use anyhow::{bail, Context as _}; use clap::Parser; use once_cell::sync::Lazy; +use openidconnect::core::{CoreClient, CoreProviderMetadata}; +use openidconnect::ureq::http_client; use openidconnect::url::Url; +use openidconnect::{AuthType, ClientId, ClientSecret, IssuerUrl, RedirectUrl}; use tempfile::NamedTempFile; use tokio::io::AsyncReadExt; use tokio::process::{Child, Command}; @@ -61,6 +67,11 @@ struct Args { #[clap(long, default_value_t = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 3000))] addr: SocketAddr, + /// Externally accessible root URL. + /// For example: https://benefice.example.com + #[clap(long)] + url: Url, + /// Maximum jobs. #[clap(long, default_value_t = num_cpus::get())] jobs: usize, @@ -91,12 +102,13 @@ struct Args { async fn main() -> anyhow::Result<()> { let Args { addr, + url, jobs, timeout, command, - oidc_issuer: _, - oidc_client: _, - oidc_secret: _, + oidc_issuer, + oidc_client, + oidc_secret, } = std::env::args() .try_fold(Vec::new(), |mut args, arg| { if let Some(path) = arg.strip_prefix('@') { @@ -138,14 +150,31 @@ async fn main() -> anyhow::Result<()> { .with(tracing_subscriber::fmt::layer()) .init(); + let issuer_url = IssuerUrl::from_url(oidc_issuer); + let provider_metadata = CoreProviderMetadata::discover(&issuer_url, http_client)?; + let openid_client = CoreClient::from_provider_metadata( + provider_metadata, + ClientId::new(oidc_client), + oidc_secret.map(ClientSecret::new), + ) + .set_redirect_uri(RedirectUrl::from_url( + url.join("/authorized") + .with_context(|| "failed to append /authorized path to url")?, + )) + .set_auth_type(AuthType::RequestBody); + let app = Router::new() + .route("/login", get(auth::login)) + .route("/logout", get(auth::logout)) + .route("/authorized", get(auth::authorized)) .route("/:uuid/", get(uuid_get)) .route("/:uuid/out", post(uuid_out_post)) .route("/:uuid/err", post(uuid_err_post)) .route( "/", - get(root_get).post(move |mp| root_post(mp, command, timeout, jobs)), + get(root_get).post(move |claims, mp| root_post(claims, mp, command, timeout, jobs)), ) + .layer(Extension(openid_client)) .layer(TraceLayer::new_for_http()); Server::bind(&addr).serve(app.into_make_service()).await?; @@ -156,7 +185,9 @@ async fn root_get() -> Html<&'static str> { Html(include_str!("root_get.html")) } +// TODO: create tests for endpoints: #38 async fn root_post( + _claims: auth::Claims, mut multipart: Multipart, command: String, timeout: u64, diff --git a/src/root_get.html b/src/root_get.html index 0956f09..544e578 100644 --- a/src/root_get.html +++ b/src/root_get.html @@ -2,6 +2,7 @@ + Login

- - + +
+