diff --git a/Cargo.lock b/Cargo.lock index 9682f2f..e138bb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,60 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug", +] + [[package]] name = "ahash" version = "0.7.4" @@ -146,6 +200,21 @@ dependencies = [ "serde", ] +[[package]] +name = "biscuit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dee631cea28b00e115fd355a1adedc860b155096941dc01259969eabd434a37" +dependencies = [ + "chrono", + "data-encoding", + "num", + "once_cell", + "ring", + "serde", + "serde_json", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -226,10 +295,20 @@ dependencies = [ "libc", "num-integer", "num-traits", + "serde", "time 0.1.43", "winapi", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -242,15 +321,18 @@ version = "0.2.0" dependencies = [ "base64 0.13.0", "bytes", + "chrono", "crossbeam", "directories", "heed", - "hmac", + "hmac 0.11.0", "http", "image", "jsonwebtoken", "lru-cache", + "macaroon", "num_cpus", + "openid", "opentelemetry", "opentelemetry-jaeger", "parking_lot", @@ -277,6 +359,7 @@ dependencies = [ "tracing-flame", "tracing-subscriber", "trust-dns-resolver", + "uuid", "webpki 0.22.0", ] @@ -304,7 +387,13 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" dependencies = [ + "aes-gcm", + "base64 0.13.0", + "hkdf", "percent-encoding", + "rand 0.8.4", + "sha2", + "subtle", "time 0.2.27", "version_check", ] @@ -334,6 +423,12 @@ dependencies = [ "libc", ] +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + [[package]] name = "crc32fast" version = "1.2.1" @@ -430,6 +525,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-mac" version = "0.11.1" @@ -440,6 +545,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -634,6 +748,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -802,6 +931,16 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gif" version = "0.11.2" @@ -909,13 +1048,33 @@ dependencies = [ "libc", ] +[[package]] +name = "hkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +dependencies = [ + "digest", + "hmac 0.10.1", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac 0.10.1", + "digest", +] + [[package]] name = "hmac" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac", + "crypto-mac 0.11.1", "digest", ] @@ -1003,6 +1162,19 @@ dependencies = [ "webpki 0.21.4", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "idna" version = "0.2.3" @@ -1014,6 +1186,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "image" version = "0.23.14" @@ -1173,6 +1351,18 @@ version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +[[package]] +name = "libsodium-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd" +dependencies = [ + "cc", + "libc", + "pkg-config", + "walkdir", +] + [[package]] name = "libsqlite3-sys" version = "0.22.2" @@ -1241,6 +1431,18 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "macaroon" +version = "0.1.1" +source = "git+https://github.com/macaroon-rs/macaroon.git?branch=trunk#74cc7b50c04fcd2ca468d28d6bc9a155f26fa047" +dependencies = [ + "base64 0.12.3", + "log", + "serde", + "serde_json", + "sodiumoxide", +] + [[package]] name = "maplit" version = "1.0.2" @@ -1340,6 +1542,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -1349,6 +1569,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-bigint 0.3.3", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.2.6" @@ -1360,6 +1594,26 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -1388,6 +1642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg", + "num-bigint 0.3.3", "num-integer", "num-traits", ] @@ -1423,12 +1678,57 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openid" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab30a9456b3484c408d9708b6f65b2bd834fdf22b73567775e1ca6de5524dd19" +dependencies = [ + "base64 0.13.0", + "biscuit", + "chrono", + "lazy_static", + "reqwest", + "serde", + "serde_json", + "thiserror", + "url", + "validator", +] + +[[package]] +name = "openssl" +version = "0.10.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + [[package]] name = "openssl-probe" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" +[[package]] +name = "openssl-sys" +version = "0.9.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df13d165e607909b363a4757a6f133f8a818a74e9d3a98d09c6128e15fa4c73" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.16.0" @@ -1623,6 +1923,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1639,6 +1950,30 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1858,18 +2193,22 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "rustls", "rustls-native-certs", "serde", + "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-socks", "url", @@ -2319,6 +2658,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.19" @@ -2415,6 +2763,7 @@ version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" dependencies = [ + "indexmap", "itoa", "ryu", "serde", @@ -2507,7 +2856,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" dependencies = [ "chrono", - "num-bigint", + "num-bigint 0.2.6", "num-traits", ] @@ -2561,6 +2910,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "sodiumoxide" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028" +dependencies = [ + "ed25519", + "libc", + "libsodium-sys", + "serde", +] + [[package]] name = "spin" version = "0.5.2" @@ -2854,6 +3215,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.22.0" @@ -3128,6 +3499,16 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -3144,8 +3525,58 @@ dependencies = [ "idna", "matches", "percent-encoding", + "serde", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", + "serde", ] +[[package]] +name = "validator" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d6937c33ec6039d8071bcf72933146b5bbe378d645d8fa59bdadabfc2a249" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", + "validator_types", +] + +[[package]] +name = "validator_derive" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286b4497f270f59276a89ae0ad109d5f8f18c69b613e3fb22b61201aadb0c4d" +dependencies = [ + "if_chain", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9680608df133af2c1ddd5eaf1ddce91d60d61b6bc51494ef326458365a470a" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3158,6 +3589,17 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" @@ -3312,6 +3754,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 91c7e25..568868c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,11 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chrono = "0.4" # Used to handle requests # TODO: This can become optional as soon as proper configs are supported # rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "801e04bd5369eb39e126c75f6d11e1e9597304d8", features = ["tls"] } # Used to handle requests -rocket = { version = "0.5.0-rc.1", features = ["tls"] } # Used to handle requests +rocket = { version = "0.5.0-rc.1", features = ["tls", "secrets"] } # Used to handle requests # Used for matrix spec type definitions and helpers #ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] } @@ -35,6 +36,8 @@ bytes = "1.1.0" http = "0.2.4" # Used to find data directory for default db path directories = "3.0.2" +macaroon = { git = "https://github.com/macaroon-rs/macaroon.git", branch = "trunk" } +openid = "0.9" # Used for ruma wrapper serde_json = { version = "1.0.67", features = ["raw_value"] } # Used for appservice registration files @@ -82,6 +85,7 @@ thread_local = "1.1.3" # used for TURN server authentication hmac = "0.11.0" sha-1 = "0.9.8" +uuid = { version = "0.8", features = ["serde", "v4"] } [features] default = ["conduit_bin", "backend_sqlite"] diff --git a/conduit-example.toml b/conduit-example.toml index 4275f52..14495e8 100644 --- a/conduit-example.toml +++ b/conduit-example.toml @@ -46,3 +46,10 @@ proxy = "none" # more examples can be found at src/database/proxy.rs:6 # The total amount of memory that the database will use. #db_cache_capacity_mb = 200 + +[default.openid] +client_id = "conduit" +secret = "00000000-0000-0000-0000-000000000000" +discover_url = "https://keycloak.domain.com/auth/realms/Realm_name" +redirect_url = "http://localhost:8080/sso_return" +macaroon_key = "this is the key" diff --git a/src/client_server/mod.rs b/src/client_server/mod.rs index 115ddaf..b2f240b 100644 --- a/src/client_server/mod.rs +++ b/src/client_server/mod.rs @@ -20,6 +20,7 @@ mod report; mod room; mod search; mod session; +mod sso; mod state; mod sync; mod tag; @@ -52,6 +53,7 @@ pub use report::*; pub use room::*; pub use search::*; pub use session::*; +pub use sso::*; pub use state::*; pub use sync::*; pub use tag::*; diff --git a/src/client_server/session.rs b/src/client_server/session.rs index 61e5519..2edb71e 100644 --- a/src/client_server/session.rs +++ b/src/client_server/session.rs @@ -1,5 +1,10 @@ +use std::sync::Arc; + use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH}; -use crate::{database::DatabaseGuard, utils, ConduitResult, Error, Ruma}; +use crate::{database::DatabaseGuard, utils, ConduitResult, Database, Error, Ruma}; +// use log::info; +use macaroon::Verifier; +use rocket::State; use ruma::{ api::client::{ error::ErrorKind, @@ -8,9 +13,11 @@ use ruma::{ uiaa::IncomingUserIdentifier, }, }, + events::EventType, UserId, }; use serde::Deserialize; +use tokio::sync::RwLock; use tracing::info; #[derive(Debug, Deserialize)] @@ -19,6 +26,29 @@ struct Claims { exp: usize, } +fn verifier_callback(v: &macaroon::ByteString) -> bool { + // TODO: why convert to string first, maybe ByteString has tailing zeros? + let v0 = v.to_string(); + let v1 = base64::decode(v0.as_bytes()).unwrap(); + + if v1.starts_with(b"time < ") { + // TODO: is utf-8 needed? + let v2 = std::str::from_utf8(&v1).unwrap(); + let v3 = v2.trim_start_matches("time < "); + let v4: i64 = v3.parse().unwrap(); + let now = chrono::Utc::now().timestamp(); + if now < v4 { + println!("OK!!"); + true + } else { + println!("expired, v4={} , now={}, v4-now={}", v4, now, v4 - now); + false + } + } else { + false + } +} + #[cfg(feature = "conduit_bin")] use rocket::{get, post}; @@ -27,14 +57,21 @@ use rocket::{get, post}; /// Get the supported login types of this server. One of these should be used as the `type` field /// when logging in. #[cfg_attr(feature = "conduit_bin", get("/_matrix/client/r0/login"))] -#[tracing::instrument] -pub async fn get_login_types_route() -> ConduitResult { - Ok( - get_login_types::Response::new(vec![get_login_types::LoginType::Password( - Default::default(), - )]) - .into(), - ) +// #[tracing::instrument] // TODO: need Debug on Database +pub async fn get_login_types_route( + db: &rocket::State>>, +) -> ConduitResult { + let mut flows = vec![get_login_types::LoginType::Password(Default::default())]; + + let db_lock = db.read().await; + + if db_lock.globals.openid_client.is_some() { + flows.push(get_login_types::LoginType::Sso( + get_login_types::SsoLoginType::default(), + )); + } + + Ok(get_login_types::Response { flows }.into()) } /// # `POST /_matrix/client/r0/login` @@ -109,6 +146,61 @@ pub async fn login_route( UserId::parse_with_server_name(username, db.globals.server_name()).map_err( |_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."), )? + } else if macaroon::Macaroon::deserialize( + &base64::decode_config(token, base64::URL_SAFE_NO_PAD).unwrap(), + ) + .is_ok() + { + println!("TOKEN! {}", token); + + let macaroon = macaroon::Macaroon::deserialize( + &base64::decode_config(token, base64::URL_SAFE_NO_PAD).unwrap(), + ) + .unwrap(); + + let v0 = macaroon.identifier().to_string(); + let v1 = base64::decode(v0.as_bytes()).unwrap(); + let user_id = std::str::from_utf8(&v1).unwrap(); + println!("identifier: {}", user_id); + + println!("location: {:?}", macaroon.location()); + println!("sig: {:?}", macaroon.signature()); + + let mut verifier = Verifier::default(); + verifier.satisfy_general(verifier_callback); + + let (key, _) = &db.globals.openid_client.as_ref().unwrap(); + + match verifier.verify(&macaroon, &key, Default::default()) { + Ok(()) => println!("Macaroon verified!"), + Err(error) => println!("Error validating macaroon: {:?}", error), + } + + let user_id = UserId::parse_with_server_name(user_id, db.globals.server_name()) + .map_err(|_| { + Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.") + })?; + + println!("user_id: {}", user_id); + + if !db.users.exists(&user_id)? { + // TODO None? + db.users + .create(&user_id, Some("00000000000000000000000000000000000000000"))?; // TODO + db.account_data.update( + None, + &user_id, + EventType::PushRules, + &ruma::events::push_rules::PushRulesEvent { + content: ruma::events::push_rules::PushRulesEventContent { + global: ruma::push::Ruleset::server_default(&user_id), + }, + }, + &db.globals, + )?; + } + + user_id } else { return Err(Error::BadRequest( ErrorKind::Unknown, diff --git a/src/client_server/sso.rs b/src/client_server/sso.rs new file mode 100644 index 0000000..4f711ed --- /dev/null +++ b/src/client_server/sso.rs @@ -0,0 +1,188 @@ +use std::sync::Arc; + +// use super::State; +use crate::{server_server, ConduitResult, Database, Error, Ruma}; +use http::status; +use macaroon::Macaroon; +use openid::{Token, Userinfo}; +use reqwest::Url; +use rocket::{ + http::{Cookie, CookieJar, Header, SameSite, Status}, + response::{Redirect, Responder, Response}, +}; + +#[cfg(feature = "conduit_bin")] +use rocket::get; +use tokio::sync::RwLock; + +const MAC_VALID_SECS: i64 = 10; + +#[cfg_attr( + feature = "conduit_bin", + get("/_matrix/client/r0/login/sso/redirect?") +)] +pub async fn get_sso_redirect( + db: &rocket::State>>, + redirectUrl: &str, + mut cookies: &CookieJar<'_>, +) -> Redirect { + let db_lock = db.read().await; + + let (_key, client) = db_lock.globals.openid_client.as_ref().unwrap(); + + let state = "value"; // TODO: random + + // cookies.add_private(Cookie::new("openid-state", state)); + // cookies.add_private(Cookie::new("openid-redirect-url", redirectUrl.to_string())); + + let cookie1 = Cookie::build("openid-state", state) + .secure(false) + .http_only(true) + .same_site(SameSite::None) + .finish(); + + let cookie2 = Cookie::build("openid-redirect-url", redirectUrl.to_string()) + .secure(false) + .http_only(true) + .same_site(SameSite::None) + .finish(); + + cookies.add_private(cookie1); + cookies.add_private(cookie2); + + // https://docs.rs/openid/0.4.0/openid/struct.Options.html + let auth_url = client.auth_url(&openid::Options { + scope: Some("email".into()), // TODO: openid only? + //TODO: nonce? + state: Some(state.to_string()), + ..Default::default() + }); + + Redirect::to(auth_url.to_string()) +} + +async fn request_token( + oidc_client: &openid::DiscoveredClient, + code: &str, +) -> Result, Error> { + let mut token: Token = oidc_client.request_token(&code).await.unwrap().into(); + if let Some(mut id_token) = token.id_token.as_mut() { + oidc_client.decode_token(&mut id_token).unwrap(); + oidc_client.validate_token(&id_token, None, None).unwrap(); + // eprintln!("token: {:?}", id_token); + } else { + return Ok(None); + } + let userinfo = oidc_client.request_userinfo(&token).await.unwrap(); + + // eprintln!("user info: {:?}", userinfo); + Ok(Some((token, userinfo))) +} + +#[derive(Debug)] +struct User { + id: String, + login: Option, + first_name: Option, + last_name: Option, + email: Option, + image_url: Option, + activated: bool, + lang_key: Option, + authorities: Vec, +} + +#[derive(Debug, Responder)] +pub enum ExampleResponse<'a> { + Redirect(Redirect), + Unauthorized(rocket::response::status::Unauthorized<&'a str>), +} + +#[cfg_attr( + feature = "conduit_bin", + get("/sso_return?&&") +)] +// #[tracing::instrument] +pub async fn get_sso_return<'a>( + db: &rocket::State>>, + session_state: &str, + state: &str, + code: &str, + cookies: &CookieJar<'_>, +) -> ExampleResponse<'a> { + let state_is_valid = cookies + .get_private("openid-state") + .map_or(false, |v| v.value() == state); + if !state_is_valid { + return ExampleResponse::Unauthorized(rocket::response::status::Unauthorized(Some( + "invalid state", + ))); + } + + let db_lock = db.read().await; + + let (_key, client) = db_lock.globals.openid_client.as_ref().unwrap(); + + let username; + match request_token(client, code).await { + Ok(Some((_token, userinfo))) => { + /* + let id = uuid::Uuid::new_v4().to_string(); + + let login = userinfo.preferred_username.clone(); + let email = userinfo.email.clone(); + + let new_user = User { + id: userinfo.sub.clone().unwrap_or_default(), + login, + last_name: userinfo.family_name.clone(), + first_name: userinfo.name.clone(), + email, + activated: userinfo.email_verified, + image_url: userinfo.picture.clone().map(|x| x.to_string()), + lang_key: Some("en".to_string()), + authorities: vec!["ROLE_USER".to_string()], //FIXME: read from token + }; + */ + + // user = new_user.login.unwrap(); + username = userinfo.preferred_username.unwrap(); + } + Ok(None) => { + return ExampleResponse::Unauthorized(rocket::response::status::Unauthorized(Some( + "no id_token found", + ))); + } + Err(err) => { + eprintln!("login error in call: {:?}", err); + return ExampleResponse::Unauthorized(rocket::response::status::Unauthorized(Some( + "login error in call", + ))); + } + } + + let (key, _client) = db_lock.globals.openid_client.as_ref().unwrap(); + + // Create our macaroon + let mut macaroon = match Macaroon::create(Some("location".into()), &key, username.into()) { + Ok(macaroon) => macaroon, + Err(error) => panic!("Error creating macaroon: {:?}", error), + }; + + let something = format!("time < {}", chrono::Utc::now().timestamp() + MAC_VALID_SECS).into(); + macaroon.add_first_party_caveat(something); + + let serialized = macaroon.serialize(macaroon::Format::V2).unwrap(); + let encoded = base64::encode_config(serialized, base64::URL_SAFE_NO_PAD); + + let url = cookies + .get_private("openid-redirect-url") + .map(|v| { + let redirectUrl = + Url::parse_with_params(v.value(), &[("loginToken", encoded)]).unwrap(); + redirectUrl + }) + .unwrap(); + + ExampleResponse::Redirect(Redirect::to(url.to_string())) +} diff --git a/src/database.rs b/src/database.rs index 080e24b..7d7efee 100644 --- a/src/database.rs +++ b/src/database.rs @@ -18,6 +18,7 @@ use crate::{utils, Error, Result}; use abstraction::DatabaseEngine; use directories::ProjectDirs; use lru_cache::LruCache; +use reqwest::Url; use rocket::{ futures::{channel::mpsc, stream::FuturesUnordered, StreamExt}, outcome::{try_outcome, IntoOutcome}, @@ -84,11 +85,21 @@ pub struct Config { turn_secret: String, #[serde(default = "default_turn_ttl")] turn_ttl: u64, + openid: Option, #[serde(flatten)] catchall: BTreeMap, } +#[derive(Clone, Debug, Deserialize)] +struct OpenIdConfig { + client_id: String, + secret: String, + redirect_url: String, + discover_url: Url, + macaroon_key: String, +} + const DEPRECATED_KEYS: &[&str] = &["cache_capacity"]; impl Config { @@ -359,7 +370,8 @@ impl Database { builder.open_tree("global")?, builder.open_tree("server_signingkeys")?, config.clone(), - )?, + ) + .await?, })); { diff --git a/src/database/globals.rs b/src/database/globals.rs index 05ecb56..e6fa0b7 100644 --- a/src/database/globals.rs +++ b/src/database/globals.rs @@ -48,6 +48,7 @@ pub struct Globals { pub roomid_mutex_state: RwLock>>>, pub roomid_mutex_federation: RwLock>>>, // this lock will be held longer pub rotate: RotationHandler, + pub openid_client: Option<(macaroon::crypto::MacaroonKey, openid::DiscoveredClient)>, } /// Handles "rotation" of long-polling requests. "Rotation" in this context is similar to "rotation" of log files and the like. @@ -81,7 +82,7 @@ impl Default for RotationHandler { } impl Globals { - pub fn load( + pub async fn load( globals: Arc, server_signingkeys: Arc, config: Config, @@ -132,9 +133,29 @@ impl Globals { .as_ref() .map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static()); + let openid_client = match config.openid.as_ref() { + Some(openid) => { + let key = macaroon::MacaroonKey::from(openid.macaroon_key.as_str()); + + let r = ( + key, + openid::DiscoveredClient::discover( + openid.client_id.to_owned(), + openid.secret.to_owned(), + Some(openid.redirect_url.to_owned()), + openid.discover_url.to_owned(), + ) + .await + .unwrap(), + ); + Some(r) + } + None => None, + }; + let s = Self { globals, - config, + config: config.clone(), keypair: Arc::new(keypair), dns_resolver: TokioAsyncResolver::tokio_from_system_conf().map_err(|_| { Error::bad_config("Failed to set up trust dns resolver with system config.") @@ -143,6 +164,7 @@ impl Globals { tls_name_override, server_signingkeys, jwt_decoding_key, + openid_client, bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())), bad_signature_ratelimiter: Arc::new(RwLock::new(HashMap::new())), servername_ratelimiter: Arc::new(RwLock::new(HashMap::new())), diff --git a/src/main.rs b/src/main.rs index 56faa3e..e51a642 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,6 +157,8 @@ fn setup_rocket(config: Figment, data: Arc>) -> rocket::Rocket< server_server::get_server_version_route, server_server::get_server_keys_route, server_server::get_server_keys_deprecated_route, + client_server::get_sso_redirect, + client_server::get_sso_return, server_server::get_public_rooms_route, server_server::get_public_rooms_filtered_route, server_server::send_transaction_message_route, @@ -216,6 +218,8 @@ async fn main() { let start = async { config.warn_deprecated(); + macaroon::initialize().expect("can't initialize macaroon"); + let db = match Database::load_or_create(&config).await { Ok(db) => db, Err(e) => {