Merge pull request 'Key backups and cross signing' (#132) from cross-signing into master

Reviewed-on: https://git.koesters.xyz/timo/conduit/pulls/132
next
Timo Kösters 2020-06-26 19:01:32 +02:00
commit e809d819ac
11 changed files with 1349 additions and 294 deletions

263
Cargo.lock generated
View File

@ -1,5 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c"
dependencies = [
"gimli",
]
[[package]]
name = "adler32"
version = "1.1.0"
@ -26,13 +35,13 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "async-trait"
version = "0.1.35"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89cb5d814ab2a47fd66d3266e9efccb53ca4c740b7451043b8ffcf9a6208f3f8"
checksum = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -43,7 +52,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -52,6 +61,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base16"
version = "0.2.1"
@ -75,9 +98,9 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "base64"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e223af0dc48c96d4f8342ec01a4974f139df863896b316681efd36742f22cc67"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
[[package]]
name = "bitflags"
@ -116,15 +139,18 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bytes"
version = "0.5.4"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b"
dependencies = [
"loom",
]
[[package]]
name = "cc"
version = "1.0.54"
version = "1.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
checksum = "b1be3409f94d7bdceeb5f5fac551039d9b3f00e25da7a74fc4d33400a0d96368"
[[package]]
name = "cfg-if"
@ -151,7 +177,7 @@ checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
name = "conduit"
version = "0.1.0"
dependencies = [
"base64 0.12.2",
"base64 0.12.3",
"directories",
"http",
"image",
@ -273,7 +299,7 @@ dependencies = [
"bitflags",
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -294,14 +320,14 @@ checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
dependencies = [
"libc",
"redox_users",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
name = "dtoa"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
[[package]]
name = "encoding_rs"
@ -340,7 +366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -416,7 +442,7 @@ dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -463,6 +489,19 @@ dependencies = [
"byteorder",
]
[[package]]
name = "generator"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68"
dependencies = [
"cc",
"libc",
"log",
"rustc_version",
"winapi 0.3.9",
]
[[package]]
name = "getrandom"
version = "0.1.14"
@ -484,6 +523,12 @@ dependencies = [
"lzw",
]
[[package]]
name = "gimli"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
[[package]]
name = "h2"
version = "0.2.5"
@ -598,9 +643,9 @@ dependencies = [
[[package]]
name = "image"
version = "0.23.5"
version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d534e95ad8b9d5aa614322d02352b4f1bf962254adcf02ac6f2def8be18498e8"
checksum = "b5b0553fec6407d63fe2975b794dfb099f3f790bdc958823851af37b26404ab4"
dependencies = [
"bytemuck",
"byteorder",
@ -632,9 +677,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.4.5"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "jpeg-decoder"
@ -656,9 +701,9 @@ dependencies = [
[[package]]
name = "js_int"
version = "0.1.5"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ab7bb370a788ad675863e035fd9bfa56a66a030a16a88ab80aeb6b18cbdf31"
checksum = "1b2b63d60564122f2a7d6592c2f1d6c1c60e7a266b4d24715950a1ddad784f66"
dependencies = [
"serde",
]
@ -703,6 +748,17 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "loom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7"
dependencies = [
"cfg-if",
"generator",
"scoped-tls",
]
[[package]]
name = "lzw"
version = "0.10.0"
@ -829,7 +885,7 @@ checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
dependencies = [
"cfg-if",
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -855,9 +911,9 @@ dependencies = [
[[package]]
name = "num-rational"
version = "0.2.4"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138"
dependencies = [
"autocfg",
"num-integer",
@ -883,6 +939,12 @@ dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]]
name = "once_cell"
version = "1.4.0"
@ -891,9 +953,9 @@ checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
[[package]]
name = "openssl"
version = "0.10.29"
version = "0.10.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
dependencies = [
"bitflags",
"cfg-if",
@ -943,7 +1005,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -997,7 +1059,7 @@ checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -1063,7 +1125,7 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid 0.2.0",
"unicode-xid 0.2.1",
]
[[package]]
@ -1148,7 +1210,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -1157,7 +1219,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680"
dependencies = [
"base64 0.12.2",
"base64 0.12.3",
"bytes",
"encoding_rs",
"futures-core",
@ -1187,9 +1249,9 @@ dependencies = [
[[package]]
name = "ring"
version = "0.16.14"
version = "0.16.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06b3fefa4f12272808f809a0af618501fdaba41a58963c5fb72238ab0be09603"
checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4"
dependencies = [
"cc",
"libc",
@ -1197,7 +1259,7 @@ dependencies = [
"spin",
"untrusted",
"web-sys",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -1255,13 +1317,13 @@ dependencies = [
"time",
"tokio",
"tokio-rustls",
"unicode-xid 0.2.0",
"unicode-xid 0.2.1",
]
[[package]]
name = "ruma"
version = "0.1.0"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"ruma-api",
"ruma-client-api",
@ -1275,7 +1337,7 @@ dependencies = [
[[package]]
name = "ruma-api"
version = "0.16.1"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"http",
"percent-encoding 2.1.0",
@ -1290,17 +1352,17 @@ dependencies = [
[[package]]
name = "ruma-api-macros"
version = "0.16.1"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
name = "ruma-client-api"
version = "0.9.0"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"http",
"js_int",
@ -1317,7 +1379,7 @@ dependencies = [
[[package]]
name = "ruma-common"
version = "0.1.3"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"matches",
"ruma-serde",
@ -1348,13 +1410,13 @@ source = "git+https://github.com/ruma/ruma-events?rev=c1ee72d#c1ee72db0f3107a97f
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
name = "ruma-federation-api"
version = "0.0.2"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"js_int",
"matches",
@ -1369,7 +1431,7 @@ dependencies = [
[[package]]
name = "ruma-identifiers"
version = "0.16.2"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"rand",
"serde",
@ -1379,7 +1441,7 @@ dependencies = [
[[package]]
name = "ruma-serde"
version = "0.2.2"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"dtoa",
"itoa",
@ -1392,9 +1454,9 @@ dependencies = [
[[package]]
name = "ruma-signatures"
version = "0.6.0-dev.1"
source = "git+https://github.com/ruma/ruma?rev=baa87104569b45dc07a9a7a16d3c7592ab8f4d6b#baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
source = "git+https://github.com/timokoesters/ruma#5a30f9cfc6c168f25cfcf51f3d80b3594c0f59b1"
dependencies = [
"base64 0.12.2",
"base64 0.12.3",
"ring",
"serde_json",
"untrusted",
@ -1418,12 +1480,27 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19"
dependencies = [
"base64 0.12.2",
"base64 0.12.3",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "rustls"
version = "0.16.0"
@ -1450,9 +1527,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
dependencies = [
"lazy_static",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
name = "scoped-tls"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -1493,23 +1576,38 @@ dependencies = [
]
[[package]]
name = "serde"
version = "1.0.112"
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736aac72d1eafe8e5962d1d1c3d99b0df526015ba40915cb3c49d042e92ec243"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.112"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf0343ce212ac0d3d6afd9391ac8e9c9efe06b533c8d33f660f6390cc4093f57"
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -1553,10 +1651,11 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "sled"
version = "0.31.0"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb6824dde66ad33bf20c6e8476f5b82b871bc8bc3c129a10ea2f7dae5060fa3"
checksum = "cdad3dc85d888056d3bd9954ffdf22d8a22701b6cd3aca4f6df4c436111898c4"
dependencies = [
"backtrace",
"crc32fast",
"crossbeam-epoch",
"crossbeam-utils",
@ -1582,7 +1681,7 @@ dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -1615,7 +1714,7 @@ dependencies = [
"heck",
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -1631,13 +1730,13 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.31"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"unicode-xid 0.2.0",
"unicode-xid 0.2.1",
]
[[package]]
@ -1651,7 +1750,7 @@ dependencies = [
"rand",
"redox_syscall",
"remove_dir_all",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -1671,7 +1770,7 @@ checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -1681,9 +1780,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
name = "tinyvec"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed"
[[package]]
name = "tokio"
version = "0.2.21"
@ -1704,7 +1809,7 @@ dependencies = [
"signal-hook-registry",
"slab",
"tokio-macros",
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]
@ -1715,7 +1820,7 @@ checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
]
[[package]]
@ -1795,11 +1900,11 @@ dependencies = [
[[package]]
name = "unicode-normalization"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
dependencies = [
"smallvec",
"tinyvec",
]
[[package]]
@ -1816,9 +1921,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "untrusted"
@ -1888,7 +1993,7 @@ dependencies = [
"log",
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
"wasm-bindgen-shared",
]
@ -1922,7 +2027,7 @@ checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92"
dependencies = [
"proc-macro2 1.0.18",
"quote 1.0.7",
"syn 1.0.31",
"syn 1.0.33",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -1961,9 +2066,9 @@ checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
@ -1993,7 +2098,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
"winapi 0.3.8",
"winapi 0.3.9",
]
[[package]]

View File

@ -15,7 +15,7 @@ edition = "2018"
rocket = { git = "https://github.com/SergioBenitez/Rocket.git", rev = "4928e35ec5c4b9242f50d644282d9896d0160a10", features = ["tls"] }
http = "0.2.1"
log = "0.4.8"
sled = "0.31.0"
sled = "0.32.0"
directories = "2.0.2"
js_int = "0.1.5"
serde_json = { version = "1.0.53", features = ["raw_value"] }
@ -29,15 +29,16 @@ thiserror = "1.0.19"
image = { version = "0.23.4", default-features = false, features = ["jpeg", "png", "gif"] }
[dependencies.ruma]
git = "https://github.com/ruma/ruma"
rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
git = "https://github.com/timokoesters/ruma"
#rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b"
#path = "../ruma/ruma"
features = ["rand", "client-api", "federation-api"]
# These are required only until ruma-events and ruma-federation-api are merged into ruma/ruma
[patch.crates-io]
ruma-common = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" }
ruma-serde = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" }
ruma-identifiers = { git = "https://github.com/ruma/ruma", rev = "baa87104569b45dc07a9a7a16d3c7592ab8f4d6b" }
ruma-common = { git = "https://github.com/timokoesters/ruma" }
ruma-serde = { git = "https://github.com/timokoesters/ruma" }
ruma-identifiers = { git = "https://github.com/timokoesters/ruma" }
#ruma-common = { path = "../ruma/ruma-common" }
#ruma-serde = { path = "../ruma/ruma-serde" }
#ruma-identifiers = { path = "../ruma/ruma-identifiers" }

View File

@ -5,6 +5,7 @@ use std::{
};
use crate::{utils, ConduitResult, Database, Error, Ruma};
use keys::{upload_signatures, upload_signing_keys};
use log::warn;
use rocket::{delete, get, options, post, put, State};
use ruma::{
@ -13,6 +14,10 @@ use ruma::{
r0::{
account::{get_username_availability, register},
alias::{create_alias, delete_alias, get_alias},
backup::{
add_backup_keys, create_backup, get_backup, get_backup_keys, get_latest_backup,
update_backup,
},
capabilities::get_capabilities,
config::{get_global_account_data, set_global_account_data},
context::get_context,
@ -33,7 +38,7 @@ use ruma::{
profile::{
get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
},
push::{get_pushrules_all, set_pushrule, set_pushrule_enabled},
push::{get_pushers, get_pushrules_all, set_pushrule, set_pushrule_enabled},
read_marker::set_read_marker,
redact::redact_event,
room::{self, create_room},
@ -71,9 +76,13 @@ const SESSION_ID_LENGTH: usize = 256;
#[get("/_matrix/client/versions")]
pub fn get_supported_versions_route() -> ConduitResult<get_supported_versions::Response> {
let mut unstable_features = BTreeMap::new();
unstable_features.insert("org.matrix.e2e_cross_signing".to_owned(), true);
Ok(get_supported_versions::Response {
versions: vec!["r0.5.0".to_owned(), "r0.6.0".to_owned()],
unstable_features: BTreeMap::new(),
unstable_features,
}
.into())
}
@ -204,33 +213,7 @@ pub fn register_route(
&EventType::PushRules,
serde_json::to_value(ruma::events::push_rules::PushRulesEvent {
content: ruma::events::push_rules::PushRulesEventContent {
global: ruma::events::push_rules::Ruleset {
content: vec![],
override_: vec![ruma::events::push_rules::ConditionalPushRule {
actions: vec![ruma::events::push_rules::Action::DontNotify],
default: true,
enabled: false,
rule_id: ".m.rule.master".to_owned(),
conditions: vec![],
}],
room: vec![],
sender: vec![],
underride: vec![ruma::events::push_rules::ConditionalPushRule {
actions: vec![
ruma::events::push_rules::Action::Notify,
ruma::events::push_rules::Action::SetTweak(ruma::push::Tweak::Sound(
"default".to_owned(),
)),
],
default: true,
enabled: true,
rule_id: ".m.rule.message".to_owned(),
conditions: vec![ruma::events::push_rules::PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.room.message".to_owned(),
}],
}],
},
global: crate::push_rules::default_pushrules(&user_id),
},
})
.expect("data is valid, we just created it")
@ -375,11 +358,11 @@ pub fn get_pushrules_all_route(
#[put(
"/_matrix/client/r0/pushrules/<_scope>/<_kind>/<_rule_id>",
data = "<body>"
//data = "<body>"
)]
pub fn set_pushrule_route(
db: State<'_, Database>,
body: Ruma<set_pushrule::Request>,
//db: State<'_, Database>,
//body: Ruma<set_pushrule::Request>,
_scope: String,
_kind: String,
_rule_id: String,
@ -502,8 +485,7 @@ pub fn set_displayname_route(
displayname: body.displayname.clone(),
..serde_json::from_value::<EventJson<_>>(
db.rooms
.room_state(&room_id)?
.get(&(EventType::RoomMember, user_id.to_string()))
.room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())?
.ok_or_else(|| {
Error::bad_database(
"Tried to send displayname update for user not in the room.",
@ -593,8 +575,7 @@ pub fn set_avatar_url_route(
avatar_url: body.avatar_url.clone(),
..serde_json::from_value::<EventJson<_>>(
db.rooms
.room_state(&room_id)?
.get(&(EventType::RoomMember, user_id.to_string()))
.room_state_get(&room_id, &EventType::RoomMember, &user_id.to_string())?
.ok_or_else(|| {
Error::bad_database(
"Tried to send avatar url update for user not in the room.",
@ -722,8 +703,11 @@ pub fn upload_keys_route(
}
if let Some(device_keys) = &body.device_keys {
db.users
.add_device_keys(user_id, device_id, device_keys, &db.globals)?;
// This check is needed to assure that signatures are kept
if db.users.get_device_keys(user_id, device_id)?.is_none() {
db.users
.add_device_keys(user_id, device_id, device_keys, &db.globals)?;
}
}
Ok(upload_keys::Response {
@ -737,33 +721,38 @@ pub fn get_keys_route(
db: State<'_, Database>,
body: Ruma<get_keys::Request>,
) -> ConduitResult<get_keys::Response> {
let sender_id = body.user_id.as_ref().expect("user is authenticated");
let mut master_keys = BTreeMap::new();
let mut self_signing_keys = BTreeMap::new();
let mut user_signing_keys = BTreeMap::new();
let mut device_keys = BTreeMap::new();
for (user_id, device_ids) in &body.device_keys {
if device_ids.is_empty() {
let mut container = BTreeMap::new();
for result in db.users.all_device_keys(&user_id.clone()) {
let (device_id, mut keys) = result?;
for device_id in db.users.all_device_ids(user_id) {
let device_id = device_id?;
if let Some(mut keys) = db.users.get_device_keys(user_id, &device_id)? {
let metadata = db
.users
.get_device_metadata(user_id, &device_id)?
.ok_or_else(|| {
Error::bad_database("all_device_keys contained nonexistent device.")
})?;
let metadata = db
.users
.get_device_metadata(user_id, &device_id)?
.ok_or_else(|| {
Error::bad_database("all_device_keys contained nonexistent device.")
})?;
keys.unsigned = Some(keys::UnsignedDeviceInfo {
device_display_name: metadata.display_name,
});
keys.unsigned = Some(keys::UnsignedDeviceInfo {
device_display_name: metadata.display_name,
});
container.insert(device_id, keys);
container.insert(device_id.to_owned(), keys);
}
}
device_keys.insert(user_id.clone(), container);
} else {
for device_id in device_ids {
let mut container = BTreeMap::new();
for keys in db.users.get_device_keys(&user_id.clone(), &device_id) {
let mut keys = keys?;
if let Some(mut keys) = db.users.get_device_keys(&user_id.clone(), &device_id)? {
let metadata = db.users.get_device_metadata(user_id, &device_id)?.ok_or(
Error::BadRequest(
ErrorKind::InvalidParam,
@ -780,11 +769,26 @@ pub fn get_keys_route(
device_keys.insert(user_id.clone(), container);
}
}
if let Some(master_key) = db.users.get_master_key(user_id, sender_id)? {
master_keys.insert(user_id.clone(), master_key);
}
if let Some(self_signing_key) = db.users.get_self_signing_key(user_id, sender_id)? {
self_signing_keys.insert(user_id.clone(), self_signing_key);
}
if user_id == sender_id {
if let Some(user_signing_key) = db.users.get_user_signing_key(sender_id)? {
user_signing_keys.insert(user_id.clone(), user_signing_key);
}
}
}
Ok(get_keys::Response {
failures: BTreeMap::new(),
master_keys,
self_signing_keys,
user_signing_keys,
device_keys,
failures: BTreeMap::new(),
}
.into())
}
@ -817,6 +821,125 @@ pub fn claim_keys_route(
.into())
}
#[post("/_matrix/client/unstable/room_keys/version", data = "<body>")]
pub fn create_backup_route(
db: State<'_, Database>,
body: Ruma<create_backup::Request>,
) -> ConduitResult<create_backup::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let version = db
.key_backups
.create_backup(&user_id, &body.algorithm, &db.globals)?;
Ok(create_backup::Response { version }.into())
}
#[put(
"/_matrix/client/unstable/room_keys/version/<_version>",
data = "<body>"
)]
pub fn update_backup_route(
db: State<'_, Database>,
body: Ruma<update_backup::Request>,
_version: String,
) -> ConduitResult<update_backup::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
db.key_backups
.update_backup(&user_id, &body.version, &body.algorithm, &db.globals)?;
Ok(update_backup::Response.into())
}
#[get("/_matrix/client/unstable/room_keys/version", data = "<body>")]
pub fn get_latest_backup_route(
db: State<'_, Database>,
body: Ruma<get_latest_backup::Request>,
) -> ConduitResult<get_latest_backup::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let (version, algorithm) =
db.key_backups
.get_latest_backup(&user_id)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"Key backup does not exist.",
))?;
Ok(get_latest_backup::Response {
algorithm,
count: (db.key_backups.count_keys(user_id, &version)? as u32).into(),
etag: db.key_backups.get_etag(user_id, &version)?,
version,
}
.into())
}
#[get(
"/_matrix/client/unstable/room_keys/version/<_version>",
data = "<body>"
)]
pub fn get_backup_route(
db: State<'_, Database>,
body: Ruma<get_backup::Request>,
_version: String,
) -> ConduitResult<get_backup::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let algorithm =
db.key_backups
.get_backup(&user_id, &body.version)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"Key backup does not exist.",
))?;
Ok(get_backup::Response {
algorithm,
count: (db.key_backups.count_keys(user_id, &body.version)? as u32).into(),
etag: db.key_backups.get_etag(user_id, &body.version)?,
version: body.version.clone(),
}
.into())
}
#[put("/_matrix/client/unstable/room_keys/keys", data = "<body>")]
pub fn add_backup_keys_route(
db: State<'_, Database>,
body: Ruma<add_backup_keys::Request>,
) -> ConduitResult<add_backup_keys::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
for (room_id, room) in &body.rooms {
for (session_id, key_data) in &room.sessions {
db.key_backups.add_key(
&user_id,
&body.version,
&room_id,
&session_id,
&key_data,
&db.globals,
)?
}
}
Ok(add_backup_keys::Response {
count: (db.key_backups.count_keys(user_id, &body.version)? as u32).into(),
etag: db.key_backups.get_etag(user_id, &body.version)?,
}
.into())
}
#[get("/_matrix/client/unstable/room_keys/keys", data = "<body>")]
pub fn get_backup_keys_route(
db: State<'_, Database>,
body: Ruma<get_backup_keys::Request>,
) -> ConduitResult<get_backup_keys::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let rooms = db.key_backups.get_all(&user_id, &body.version)?;
Ok(get_backup_keys::Response { rooms }.into())
}
#[post("/_matrix/client/r0/rooms/<_room_id>/read_markers", data = "<body>")]
pub fn set_read_marker_route(
db: State<'_, Database>,
@ -1265,35 +1388,13 @@ pub fn join_room_by_id_route(
// TODO: Ask a remote server if we don't have this room
let event = db
.rooms
.room_state(&body.room_id)?
.get(&(EventType::RoomMember, user_id.to_string()))
.map_or_else(
|| {
// There was no existing membership event
Ok::<_, Error>(member::MemberEventContent {
membership: member::MembershipState::Join,
displayname: db.users.displayname(&user_id)?,
avatar_url: db.users.avatar_url(&user_id)?,
is_direct: None,
third_party_invite: None,
})
},
|pdu| {
// We change the existing membership event
let mut event = serde_json::from_value::<EventJson<member::MemberEventContent>>(
pdu.content.clone(),
)
.map_err(|_| Error::bad_database("Invalid member event in db."))?
.deserialize()
.map_err(|_| Error::bad_database("Invalid member event in db."))?;
event.membership = member::MembershipState::Join;
event.displayname = db.users.displayname(&user_id)?;
event.avatar_url = db.users.avatar_url(&user_id)?;
Ok(event)
},
)?;
let event = member::MemberEventContent {
membership: member::MembershipState::Join,
displayname: db.users.displayname(&user_id)?,
avatar_url: db.users.avatar_url(&user_id)?,
is_direct: None,
third_party_invite: None,
};
db.rooms.append_pdu(
body.room_id.clone(),
@ -1348,11 +1449,10 @@ pub fn leave_room_route(
_room_id: String,
) -> ConduitResult<leave_room::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let state = db.rooms.room_state(&body.room_id)?;
let mut event = serde_json::from_value::<EventJson<member::MemberEventContent>>(
state
.get(&(EventType::RoomMember, user_id.to_string()))
db.rooms
.room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())?
.ok_or(Error::BadRequest(
ErrorKind::BadState,
"Cannot leave a room you are not a member of.",
@ -1387,12 +1487,11 @@ pub fn kick_user_route(
_room_id: String,
) -> ConduitResult<kick_user::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let state = db.rooms.room_state(&body.room_id)?;
let mut event =
serde_json::from_value::<EventJson<ruma::events::room::member::MemberEventContent>>(
state
.get(&(EventType::RoomMember, user_id.to_string()))
db.rooms
.room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())?
.ok_or(Error::BadRequest(
ErrorKind::BadState,
"Cannot kick member that's not in the room.",
@ -1428,12 +1527,12 @@ pub fn ban_user_route(
_room_id: String,
) -> ConduitResult<ban_user::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let state = db.rooms.room_state(&body.room_id)?;
// TODO: reason
let event = state
.get(&(EventType::RoomMember, user_id.to_string()))
let event = db
.rooms
.room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())?
.map_or(
Ok::<_, Error>(member::MemberEventContent {
membership: member::MembershipState::Ban,
@ -1475,12 +1574,11 @@ pub fn unban_user_route(
_room_id: String,
) -> ConduitResult<unban_user::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let state = db.rooms.room_state(&body.room_id)?;
let mut event =
serde_json::from_value::<EventJson<ruma::events::room::member::MemberEventContent>>(
state
.get(&(EventType::RoomMember, user_id.to_string()))
db.rooms
.room_state_get(&body.room_id, &EventType::RoomMember, &user_id.to_string())?
.ok_or(Error::BadRequest(
ErrorKind::BadState,
"Cannot unban a user who is not banned.",
@ -1642,7 +1740,8 @@ pub async fn get_public_rooms_filtered_route(
.map(|room_id| {
let room_id = room_id?;
let state = db.rooms.room_state(&room_id)?;
// TODO: Do not load full state?
let state = db.rooms.room_state_full(&room_id)?;
let chunk = directory::PublicRoomsChunk {
aliases: Vec::new(),
@ -1774,10 +1873,30 @@ pub fn search_users_route(
.into())
}
#[get("/_matrix/client/r0/rooms/<_room_id>/members")]
pub fn get_member_events_route(_room_id: String) -> ConduitResult<get_member_events::Response> {
warn!("TODO: get_member_events_route");
Ok(get_member_events::Response { chunk: Vec::new() }.into())
#[get("/_matrix/client/r0/rooms/<_room_id>/members", data = "<body>")]
pub fn get_member_events_route(
db: State<'_, Database>,
body: Ruma<get_member_events::Request>,
_room_id: String,
) -> ConduitResult<get_member_events::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
if !db.rooms.is_joined(user_id, &body.room_id)? {
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"You don't have permission to view this room.",
));
}
Ok(get_member_events::Response {
chunk: db
.rooms
.room_state_type(&body.room_id, &EventType::RoomMember)?
.values()
.map(|pdu| pdu.to_member_event())
.collect(),
}
.into())
}
#[get("/_matrix/client/r0/thirdparty/protocols")]
@ -1951,7 +2070,7 @@ pub fn get_state_events_route(
Ok(get_state_events::Response {
room_state: db
.rooms
.room_state(&body.room_id)?
.room_state_full(&body.room_id)?
.values()
.map(|pdu| pdu.to_state_event())
.collect(),
@ -1979,10 +2098,9 @@ pub fn get_state_events_for_key_route(
));
}
let state = db.rooms.room_state(&body.room_id)?;
let event = state
.get(&(body.event_type.clone(), body.state_key.clone()))
let event = db
.rooms
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"State event not found.",
@ -2014,17 +2132,16 @@ pub fn get_state_events_for_empty_key_route(
));
}
let state = db.rooms.room_state(&body.room_id)?;
let event = state
.get(&(body.event_type.clone(), "".to_owned()))
let event = db
.rooms
.room_state_get(&body.room_id, &body.event_type, "")?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"State event not found.",
))?;
Ok(get_state_events_for_empty_key::Response {
content: serde_json::value::to_raw_value(event)
content: serde_json::value::to_raw_value(&event)
.map_err(|_| Error::bad_database("Invalid event content in database"))?,
}
.into())
@ -2053,7 +2170,7 @@ pub fn sync_route(
let mut pdus = db
.rooms
.pdus_since(&room_id, since)?
.pdus_since(&user_id, &room_id, since)?
.filter_map(|r| r.ok()) // Filter out buggy events
.collect::<Vec<_>>();
@ -2068,7 +2185,7 @@ pub fn sync_route(
let content = serde_json::from_value::<
EventJson<ruma::events::room::member::MemberEventContent>,
>(pdu.content.clone())
.map_err(|_| Error::bad_database("Invalid PDU in database."))?
.expect("EventJson::from_value always works")
.deserialize()
.map_err(|_| Error::bad_database("Invalid PDU in database."))?;
if content.membership == ruma::events::room::member::MembershipState::Join {
@ -2081,7 +2198,7 @@ pub fn sync_route(
}
}
let state = db.rooms.room_state(&room_id)?;
let members = db.rooms.room_state_type(&room_id, &EventType::RoomMember)?;
let (joined_member_count, invited_member_count, heroes) = if send_member_count {
let joined_member_count = db.rooms.room_members(&room_id).count();
@ -2096,7 +2213,7 @@ pub fn sync_route(
for hero in db
.rooms
.all_pdus(&room_id)?
.all_pdus(&user_id, &room_id)?
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
.filter(|pdu| pdu.kind == EventType::RoomMember)
.map(|pdu| {
@ -2111,8 +2228,8 @@ pub fn sync_route(
let current_content = serde_json::from_value::<
EventJson<ruma::events::room::member::MemberEventContent>,
>(
state
.get(&(EventType::RoomMember, state_key.clone()))
members
.get(state_key)
.ok_or_else(|| {
Error::bad_database(
"A user that joined once has no member event anymore.",
@ -2170,7 +2287,7 @@ pub fn sync_route(
if let Some(last_read) = db.rooms.edus.room_read_get(&room_id, &user_id)? {
Some(
(db.rooms
.pdus_since(&room_id, last_read)?
.pdus_since(&user_id, &room_id, last_read)?
.filter_map(|pdu| pdu.ok()) // Filter out buggy events
.filter(|pdu| {
matches!(
@ -2264,7 +2381,8 @@ pub fn sync_route(
// TODO: state before timeline
state: sync_events::State {
events: if joined_since_last_sync {
state
db.rooms
.room_state_full(&room_id)?
.into_iter()
.map(|(_, pdu)| pdu.to_state_event())
.collect()
@ -2283,7 +2401,7 @@ pub fn sync_route(
let mut left_rooms = BTreeMap::new();
for room_id in db.rooms.rooms_left(&user_id) {
let room_id = room_id?;
let pdus = db.rooms.pdus_since(&room_id, since)?;
let pdus = db.rooms.pdus_since(&user_id, &room_id, since)?;
let room_events = pdus
.filter_map(|pdu| pdu.ok()) // Filter out buggy events
.map(|pdu| pdu.to_room_event())
@ -2337,7 +2455,7 @@ pub fn sync_route(
invite_state: sync_events::InviteState {
events: db
.rooms
.room_state(&room_id)?
.room_state_full(&room_id)?
.into_iter()
.map(|(_, pdu)| pdu.to_stripped_state_event())
.collect(),
@ -2387,7 +2505,7 @@ pub fn sync_route(
device_lists: sync_events::DeviceLists {
changed: if since != 0 {
db.users
.device_keys_changed(since)
.keys_changed(since)
.filter_map(|u| u.ok())
.collect() // Filter out buggy events
} else {
@ -2438,7 +2556,7 @@ pub fn get_context_route(
let events_before = db
.rooms
.pdus_until(&body.room_id, base_token)
.pdus_until(&user_id, &body.room_id, base_token)
.take(
u32::try_from(body.limit).map_err(|_| {
Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.")
@ -2464,7 +2582,7 @@ pub fn get_context_route(
let events_after = db
.rooms
.pdus_after(&body.room_id, base_token)
.pdus_after(&user_id, &body.room_id, base_token)
.take(
u32::try_from(body.limit).map_err(|_| {
Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.")
@ -2496,7 +2614,7 @@ pub fn get_context_route(
events_after,
state: db // TODO: State at event
.rooms
.room_state(&body.room_id)?
.room_state_full(&body.room_id)?
.values()
.map(|pdu| pdu.to_state_event())
.collect(),
@ -2528,7 +2646,7 @@ pub fn get_message_events_route(
get_message_events::Direction::Forward => {
let events_after = db
.rooms
.pdus_after(&body.room_id, from)
.pdus_after(&user_id, &body.room_id, from)
// Use limit or else 10
.take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| {
Ok(u32::try_from(l).map_err(|_| {
@ -2563,7 +2681,7 @@ pub fn get_message_events_route(
get_message_events::Direction::Backward => {
let events_before = db
.rooms
.pdus_until(&body.room_id, from)
.pdus_until(&user_id, &body.room_id, from)
// Use limit or else 10
.take(body.limit.map_or(Ok::<_, Error>(10_usize), |l| {
Ok(u32::try_from(l).map_err(|_| {
@ -2883,6 +3001,122 @@ pub fn delete_devices_route(
Ok(delete_devices::Response.into())
}
#[post("/_matrix/client/unstable/keys/device_signing/upload", data = "<body>")]
pub fn upload_signing_keys_route(
db: State<'_, Database>,
body: Ruma<upload_signing_keys::Request>,
) -> ConduitResult<upload_signing_keys::Response> {
let user_id = body.user_id.as_ref().expect("user is authenticated");
let device_id = body.device_id.as_ref().expect("user is authenticated");
// UIAA
let mut uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
stages: vec!["m.login.password".to_owned()],
}],
completed: Vec::new(),
params: Default::default(),
session: None,
auth_error: None,
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = db.uiaa.try_auth(
&user_id,
&device_id,
auth,
&uiaainfo,
&db.users,
&db.globals,
)?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
}
// Success!
} else {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
db.uiaa.create(&user_id, &device_id, &uiaainfo)?;
return Err(Error::Uiaa(uiaainfo));
}
if let Some(master_key) = &body.master_key {
db.users.add_cross_signing_keys(
user_id,
&master_key,
&body.self_signing_key,
&body.user_signing_key,
&db.globals,
)?;
}
Ok(upload_signing_keys::Response.into())
}
#[post("/_matrix/client/unstable/keys/signatures/upload", data = "<body>")]
pub fn upload_signatures_route(
db: State<'_, Database>,
body: Ruma<upload_signatures::Request>,
) -> ConduitResult<upload_signatures::Response> {
let sender_id = body.user_id.as_ref().expect("user is authenticated");
for (user_id, signed_keys) in &body.signed_keys {
for (key_id, signed_key) in signed_keys {
for signature in signed_key
.get("signatures")
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Missing signatures field.",
))?
.get(sender_id.to_string())
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid user in signatures field.",
))?
.as_object()
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid signature.",
))?
.clone()
.into_iter()
{
// Signature validation?
let signature = (
signature.0,
signature
.1
.as_str()
.ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid signature value.",
))?
.to_owned(),
);
db.users
.sign_key(&user_id, &key_id, signature, &sender_id, &db.globals)?;
}
}
}
Ok(upload_signatures::Response.into())
}
#[get("/_matrix/client/r0/pushers")]
pub fn pushers_route() -> ConduitResult<get_pushers::Response> {
Ok(get_pushers::Response {
pushers: Vec::new(),
}
.into())
}
#[post("/_matrix/client/r0/pushers/set")]
pub fn set_pushers_route() -> ConduitResult<get_pushers::Response> {
Ok(get_pushers::Response {
pushers: Vec::new(),
}
.into())
}
#[options("/<_segments..>")]
pub fn options_route(
_segments: rocket::http::uri::Segments<'_>,

View File

@ -1,6 +1,7 @@
pub(self) mod account_data;
pub(self) mod global_edus;
pub(self) mod globals;
pub(self) mod key_backups;
pub(self) mod media;
pub(self) mod rooms;
pub(self) mod uiaa;
@ -21,6 +22,7 @@ pub struct Database {
pub account_data: account_data::AccountData,
pub global_edus: global_edus::GlobalEdus,
pub media: media::Media,
pub key_backups: key_backups::KeyBackups,
pub _db: sled::Db,
}
@ -73,8 +75,11 @@ impl Database {
userdeviceid_metadata: db.open_tree("userdeviceid_metadata")?,
token_userdeviceid: db.open_tree("token_userdeviceid")?,
onetimekeyid_onetimekeys: db.open_tree("onetimekeyid_onetimekeys")?,
userdeviceid_devicekeys: db.open_tree("userdeviceid_devicekeys")?,
devicekeychangeid_userid: db.open_tree("devicekeychangeid_userid")?,
keychangeid_userid: db.open_tree("devicekeychangeid_userid")?,
keyid_key: db.open_tree("keyid_key")?,
userid_masterkeyid: db.open_tree("userid_masterkeyid")?,
userid_selfsigningkeyid: db.open_tree("userid_selfsigningkeyid")?,
userid_usersigningkeyid: db.open_tree("userid_usersigningkeyid")?,
todeviceid_events: db.open_tree("todeviceid_events")?,
},
uiaa: uiaa::Uiaa {
@ -111,6 +116,11 @@ impl Database {
media: media::Media {
mediaid_file: db.open_tree("mediaid_file")?,
},
key_backups: key_backups::KeyBackups {
backupid_algorithm: db.open_tree("backupid_algorithm")?,
backupid_etag: db.open_tree("backupid_etag")?,
backupkeyid_backup: db.open_tree("backupkeyid_backupmetadata")?,
},
_db: db,
})
}

207
src/database/key_backups.rs Normal file
View File

@ -0,0 +1,207 @@
use crate::{utils, Error, Result};
use ruma::{
api::client::{
error::ErrorKind,
r0::backup::{get_backup_keys::Sessions, BackupAlgorithm, KeyData},
},
identifiers::{RoomId, UserId},
};
use std::{collections::BTreeMap, convert::TryFrom};
pub struct KeyBackups {
pub(super) backupid_algorithm: sled::Tree, // BackupId = UserId + Version(Count)
pub(super) backupid_etag: sled::Tree, // BackupId = UserId + Version(Count)
pub(super) backupkeyid_backup: sled::Tree, // BackupKeyId = UserId + Version + RoomId + SessionId
}
impl KeyBackups {
pub fn create_backup(
&self,
user_id: &UserId,
backup_metadata: &BackupAlgorithm,
globals: &super::globals::Globals,
) -> Result<String> {
let version = globals.next_count()?.to_string();
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(&version.as_bytes());
self.backupid_algorithm.insert(
&key,
&*serde_json::to_string(backup_metadata)
.expect("BackupAlgorithm::to_string always works"),
)?;
self.backupid_etag
.insert(&key, &globals.next_count()?.to_be_bytes())?;
Ok(version)
}
pub fn update_backup(
&self,
user_id: &UserId,
version: &str,
backup_metadata: &BackupAlgorithm,
globals: &super::globals::Globals,
) -> Result<String> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(&version.as_bytes());
if self.backupid_algorithm.get(&key)?.is_none() {
return Err(Error::BadRequest(
ErrorKind::NotFound,
"Tried to update nonexistent backup.",
));
}
self.backupid_algorithm.insert(
&key,
&*serde_json::to_string(backup_metadata)
.expect("BackupAlgorithm::to_string always works"),
)?;
self.backupid_etag
.insert(&key, &globals.next_count()?.to_be_bytes())?;
Ok(version.to_string())
}
pub fn get_latest_backup(&self, user_id: &UserId) -> Result<Option<(String, BackupAlgorithm)>> {
let mut prefix = user_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
self.backupid_algorithm
.scan_prefix(&prefix)
.last()
.map_or(Ok(None), |r| {
let (key, value) = r?;
let version = utils::string_from_bytes(
key.rsplit(|&b| b == 0xff)
.next()
.expect("rsplit always returns an element"),
)
.map_err(|_| Error::bad_database("backupid_algorithm key is invalid."))?;
Ok(Some((
version,
serde_json::from_slice(&value).map_err(|_| {
Error::bad_database("Algorithm in backupid_algorithm is invalid.")
})?,
)))
})
}
pub fn get_backup(&self, user_id: &UserId, version: &str) -> Result<Option<BackupAlgorithm>> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(version.as_bytes());
self.backupid_algorithm.get(key)?.map_or(Ok(None), |bytes| {
Ok(serde_json::from_slice(&bytes)
.map_err(|_| Error::bad_database("Algorithm in backupid_algorithm is invalid."))?)
})
}
pub fn add_key(
&self,
user_id: &UserId,
version: &str,
room_id: &RoomId,
session_id: &str,
key_data: &KeyData,
globals: &super::globals::Globals,
) -> Result<()> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(version.as_bytes());
if self.backupid_algorithm.get(&key)?.is_none() {
return Err(Error::BadRequest(
ErrorKind::NotFound,
"Tried to update nonexistent backup.",
));
}
self.backupid_etag
.insert(&key, &globals.next_count()?.to_be_bytes())?;
key.push(0xff);
key.extend_from_slice(room_id.to_string().as_bytes());
key.push(0xff);
key.extend_from_slice(session_id.as_bytes());
self.backupkeyid_backup.insert(
&key,
&*serde_json::to_string(&key_data).expect("KeyData::to_string always works"),
)?;
Ok(())
}
pub fn count_keys(&self, user_id: &UserId, version: &str) -> Result<usize> {
let mut prefix = user_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
prefix.extend_from_slice(version.as_bytes());
Ok(self.backupkeyid_backup.scan_prefix(&prefix).count())
}
pub fn get_etag(&self, user_id: &UserId, version: &str) -> Result<String> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(&version.as_bytes());
Ok(utils::u64_from_bytes(
&self
.backupid_etag
.get(&key)?
.ok_or_else(|| Error::bad_database("Backup has no etag."))?,
)
.map_err(|_| Error::bad_database("etag in backupid_etag invalid."))?
.to_string())
}
pub fn get_all(&self, user_id: &UserId, version: &str) -> Result<BTreeMap<RoomId, Sessions>> {
let mut prefix = user_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
prefix.extend_from_slice(version.as_bytes());
let mut rooms = BTreeMap::<RoomId, Sessions>::new();
for result in self.backupkeyid_backup.scan_prefix(&prefix).map(|r| {
let (key, value) = r?;
let mut parts = key.rsplit(|&b| b == 0xff);
let session_id = utils::string_from_bytes(
&parts
.next()
.ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?,
)
.map_err(|_| Error::bad_database("backupkeyid_backup session_id is invalid."))?;
let room_id = RoomId::try_from(
utils::string_from_bytes(
&parts
.next()
.ok_or_else(|| Error::bad_database("backupkeyid_backup key is invalid."))?,
)
.map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid."))?,
)
.map_err(|_| Error::bad_database("backupkeyid_backup room_id is invalid room id."))?;
let key_data = serde_json::from_slice(&value)
.map_err(|_| Error::bad_database("KeyData in backupkeyid_backup is invalid."))?;
Ok::<_, Error>((room_id, session_id, key_data))
}) {
let (room_id, session_id, key_data) = result?;
rooms
.entry(room_id)
.or_insert_with(|| Sessions {
sessions: BTreeMap::new(),
})
.sessions
.insert(session_id, key_data);
}
Ok(rooms)
}
}

View File

@ -56,7 +56,10 @@ impl Rooms {
}
/// Returns the full room state.
pub fn room_state(&self, room_id: &RoomId) -> Result<HashMap<(EventType, String), PduEvent>> {
pub fn room_state_full(
&self,
room_id: &RoomId,
) -> Result<HashMap<(EventType, String), PduEvent>> {
let mut hashmap = HashMap::new();
for pdu in self
.roomstateid_pdu
@ -78,6 +81,58 @@ impl Rooms {
Ok(hashmap)
}
/// Returns the full room state.
pub fn room_state_type(
&self,
room_id: &RoomId,
event_type: &EventType,
) -> Result<HashMap<String, PduEvent>> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
prefix.extend_from_slice(&event_type.to_string().as_bytes());
let mut hashmap = HashMap::new();
for pdu in self
.roomstateid_pdu
.scan_prefix(&prefix)
.values()
.map(|value| {
Ok::<_, Error>(
serde_json::from_slice::<PduEvent>(&value?)
.map_err(|_| Error::bad_database("Invalid PDU in db."))?,
)
})
{
let pdu = pdu?;
let state_key = pdu.state_key.clone().ok_or_else(|| {
Error::bad_database("Room state contains event without state_key.")
})?;
hashmap.insert(state_key, pdu);
}
Ok(hashmap)
}
/// Returns the full room state.
pub fn room_state_get(
&self,
room_id: &RoomId,
event_type: &EventType,
state_key: &str,
) -> Result<Option<PduEvent>> {
let mut key = room_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(&event_type.to_string().as_bytes());
key.push(0xff);
key.extend_from_slice(&state_key.as_bytes());
self.roomstateid_pdu.get(&key)?.map_or(Ok(None), |value| {
Ok::<_, Error>(Some(
serde_json::from_slice::<PduEvent>(&value)
.map_err(|_| Error::bad_database("Invalid PDU in db."))?,
))
})
}
/// Returns the `count` of this pdu's id.
pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> {
self.eventid_pduid
@ -212,8 +267,7 @@ impl Rooms {
// Is the event authorized?
if let Some(state_key) = &state_key {
let power_levels = self
.room_state(&room_id)?
.get(&(EventType::RoomPowerLevels, "".to_owned()))
.room_state_get(&room_id, &EventType::RoomPowerLevels, "")?
.map_or_else(
|| {
Ok::<_, Error>(power_levels::PowerLevelsEventContent {
@ -244,8 +298,7 @@ impl Rooms {
},
)?;
let sender_membership = self
.room_state(&room_id)?
.get(&(EventType::RoomMember, sender.to_string()))
.room_state_get(&room_id, &EventType::RoomMember, &sender.to_string())?
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
Ok(
serde_json::from_value::<EventJson<member::MemberEventContent>>(
@ -280,8 +333,11 @@ impl Rooms {
})?;
let current_membership = self
.room_state(&room_id)?
.get(&(EventType::RoomMember, target_user_id.to_string()))
.room_state_get(
&room_id,
&EventType::RoomMember,
&target_user_id.to_string(),
)?
.map_or(Ok::<_, Error>(member::MembershipState::Leave), |pdu| {
Ok(
serde_json::from_value::<EventJson<member::MemberEventContent>>(
@ -315,8 +371,7 @@ impl Rooms {
);
let join_rules =
self.room_state(&room_id)?
.get(&(EventType::RoomJoinRules, "".to_owned()))
self.room_state_get(&room_id, &EventType::RoomJoinRules, "")?
.map_or(Ok::<_, Error>(join_rules::JoinRule::Public), |pdu| {
Ok(serde_json::from_value::<
EventJson<join_rules::JoinRulesEventContent>,
@ -446,13 +501,13 @@ impl Rooms {
+ 1;
let mut unsigned = unsigned.unwrap_or_default();
// TODO: Optimize this to not load the whole room state?
if let Some(state_key) = &state_key {
if let Some(prev_pdu) = self
.room_state(&room_id)?
.get(&(event_type.clone(), state_key.to_owned()))
{
if let Some(prev_pdu) = self.room_state_get(&room_id, &event_type, &state_key)? {
unsigned.insert("prev_content".to_owned(), prev_pdu.content.clone());
unsigned.insert(
"prev_sender".to_owned(),
serde_json::to_value(prev_pdu.sender).expect("UserId::to_value always works"),
);
}
}
@ -551,13 +606,18 @@ impl Rooms {
}
/// Returns an iterator over all PDUs in a room.
pub fn all_pdus(&self, room_id: &RoomId) -> Result<impl Iterator<Item = Result<PduEvent>>> {
self.pdus_since(room_id, 0)
pub fn all_pdus(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<impl Iterator<Item = Result<PduEvent>>> {
self.pdus_since(user_id, room_id, 0)
}
/// Returns an iterator over all events in a room that happened after the event with id `since`.
pub fn pdus_since(
&self,
user_id: &UserId,
room_id: &RoomId,
since: u64,
) -> Result<impl Iterator<Item = Result<PduEvent>>> {
@ -566,12 +626,13 @@ impl Rooms {
pdu_id.push(0xff);
pdu_id.extend_from_slice(&(since).to_be_bytes());
self.pdus_since_pduid(room_id, &pdu_id)
self.pdus_since_pduid(user_id, room_id, &pdu_id)
}
/// Returns an iterator over all events in a room that happened after the event with id `since`.
pub fn pdus_since_pduid(
&self,
user_id: &UserId,
room_id: &RoomId,
pdu_id: &[u8],
) -> Result<impl Iterator<Item = Result<PduEvent>>> {
@ -579,6 +640,7 @@ impl Rooms {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
let user_id = user_id.clone();
Ok(self
.pduid_pdu
.range(pdu_id..)
@ -590,9 +652,13 @@ impl Rooms {
})
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::bad_database("PDU in db is invalid."))?)
.map(move |(_, v)| {
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
.map_err(|_| Error::bad_database("PDU in db is invalid."))?;
if pdu.sender != user_id {
pdu.unsigned.remove("transaction_id");
}
Ok(pdu)
}))
}
@ -600,6 +666,7 @@ impl Rooms {
/// `until` in reverse-chronological order.
pub fn pdus_until(
&self,
user_id: &UserId,
room_id: &RoomId,
until: u64,
) -> impl Iterator<Item = Result<PduEvent>> {
@ -612,14 +679,19 @@ impl Rooms {
let current: &[u8] = &current;
let user_id = user_id.clone();
self.pduid_pdu
.range(..current)
.rev()
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::bad_database("PDU in db is invalid."))?)
.map(move |(_, v)| {
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
.map_err(|_| Error::bad_database("PDU in db is invalid."))?;
if pdu.sender != user_id {
pdu.unsigned.remove("transaction_id");
}
Ok(pdu)
})
}
@ -627,6 +699,7 @@ impl Rooms {
/// `from` in chronological order.
pub fn pdus_after(
&self,
user_id: &UserId,
room_id: &RoomId,
from: u64,
) -> impl Iterator<Item = Result<PduEvent>> {
@ -639,13 +712,18 @@ impl Rooms {
let current: &[u8] = &current;
let user_id = user_id.clone();
self.pduid_pdu
.range(current..)
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| {
Ok(serde_json::from_slice(&v)
.map_err(|_| Error::bad_database("PDU in db is invalid."))?)
.map(move |(_, v)| {
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
.map_err(|_| Error::bad_database("PDU in db is invalid."))?;
if pdu.sender != user_id {
pdu.unsigned.remove("transaction_id");
}
Ok(pdu)
})
}

View File

@ -1,9 +1,12 @@
use crate::{utils, Error, Result};
use js_int::UInt;
use ruma::{
api::client::r0::{
device::Device,
keys::{AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm, OneTimeKey},
api::client::{
error::ErrorKind,
r0::{
device::Device,
keys::{AlgorithmAndDeviceId, CrossSigningKey, DeviceKeys, KeyAlgorithm, OneTimeKey},
},
},
events::{to_device::AnyToDeviceEvent, EventJson, EventType},
identifiers::UserId,
@ -19,8 +22,11 @@ pub struct Users {
pub(super) token_userdeviceid: sled::Tree,
pub(super) onetimekeyid_onetimekeys: sled::Tree, // OneTimeKeyId = UserId + AlgorithmAndDeviceId
pub(super) userdeviceid_devicekeys: sled::Tree,
pub(super) devicekeychangeid_userid: sled::Tree, // DeviceKeyChangeId = Count
pub(super) keychangeid_userid: sled::Tree, // KeyChangeId = Count
pub(super) keyid_key: sled::Tree, // KeyId = UserId + KeyId (depends on key type)
pub(super) userid_masterkeyid: sled::Tree,
pub(super) userid_selfsigningkeyid: sled::Tree,
pub(super) userid_usersigningkeyid: sled::Tree,
pub(super) todeviceid_events: sled::Tree, // ToDeviceId = UserId + DeviceId + Count
}
@ -171,9 +177,6 @@ impl Users {
userdeviceid.push(0xff);
userdeviceid.extend_from_slice(device_id.as_bytes());
// Remove device keys
self.userdeviceid_devicekeys.remove(&userdeviceid)?;
// Remove tokens
if let Some(old_token) = self.userdeviceid_token.remove(&userdeviceid)? {
self.token_userdeviceid.remove(&old_token)?;
@ -350,38 +353,163 @@ impl Users {
userdeviceid.push(0xff);
userdeviceid.extend_from_slice(device_id.as_bytes());
self.userdeviceid_devicekeys.insert(
self.keyid_key.insert(
&userdeviceid,
&*serde_json::to_string(&device_keys).expect("DeviceKeys::to_string always works"),
)?;
self.devicekeychangeid_userid
self.keychangeid_userid
.insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?;
Ok(())
}
pub fn get_device_keys(
pub fn add_cross_signing_keys(
&self,
user_id: &UserId,
device_id: &str,
) -> impl Iterator<Item = Result<DeviceKeys>> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(device_id.as_bytes());
master_key: &CrossSigningKey,
self_signing_key: &Option<CrossSigningKey>,
user_signing_key: &Option<CrossSigningKey>,
globals: &super::globals::Globals,
) -> Result<()> {
// TODO: Check signatures
self.userdeviceid_devicekeys
.scan_prefix(key)
.values()
.map(|bytes| {
Ok(serde_json::from_slice(&bytes?)
.map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?)
})
let mut prefix = user_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
// Master key
let mut master_key_ids = master_key.keys.values();
let master_key_id = master_key_ids.next().ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Master key contained no key.",
))?;
if master_key_ids.next().is_some() {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Master key contained more than one key.",
));
}
let mut master_key_key = prefix.clone();
master_key_key.extend_from_slice(master_key_id.as_bytes());
self.keyid_key.insert(
&master_key_key,
&*serde_json::to_string(&master_key).expect("CrossSigningKey::to_string always works"),
)?;
self.userid_masterkeyid
.insert(&*user_id.to_string(), master_key_key)?;
// Self-signing key
if let Some(self_signing_key) = self_signing_key {
let mut self_signing_key_ids = self_signing_key.keys.values();
let self_signing_key_id = self_signing_key_ids.next().ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"Self signing key contained no key.",
))?;
if self_signing_key_ids.next().is_some() {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Self signing key contained more than one key.",
));
}
let mut self_signing_key_key = prefix.clone();
self_signing_key_key.extend_from_slice(self_signing_key_id.as_bytes());
self.keyid_key.insert(
&self_signing_key_key,
&*serde_json::to_string(&self_signing_key)
.expect("CrossSigningKey::to_string always works"),
)?;
self.userid_selfsigningkeyid
.insert(&*user_id.to_string(), self_signing_key_key)?;
}
// User-signing key
if let Some(user_signing_key) = user_signing_key {
let mut user_signing_key_ids = user_signing_key.keys.values();
let user_signing_key_id = user_signing_key_ids.next().ok_or(Error::BadRequest(
ErrorKind::InvalidParam,
"User signing key contained no key.",
))?;
if user_signing_key_ids.next().is_some() {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"User signing key contained more than one key.",
));
}
let mut user_signing_key_key = prefix.clone();
user_signing_key_key.extend_from_slice(user_signing_key_id.as_bytes());
self.keyid_key.insert(
&user_signing_key_key,
&*serde_json::to_string(&user_signing_key)
.expect("CrossSigningKey::to_string always works"),
)?;
self.userid_usersigningkeyid
.insert(&*user_id.to_string(), user_signing_key_key)?;
}
self.keychangeid_userid
.insert(globals.next_count()?.to_be_bytes(), &*user_id.to_string())?;
Ok(())
}
pub fn device_keys_changed(&self, since: u64) -> impl Iterator<Item = Result<UserId>> {
self.devicekeychangeid_userid
.range(since.to_be_bytes()..)
pub fn sign_key(
&self,
target_id: &UserId,
key_id: &str,
signature: (String, String),
sender_id: &UserId,
globals: &super::globals::Globals,
) -> Result<()> {
let mut key = target_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(key_id.to_string().as_bytes());
let mut cross_signing_key =
serde_json::from_slice::<serde_json::Value>(&self.keyid_key.get(&key)?.ok_or(
Error::BadRequest(ErrorKind::InvalidParam, "Tried to sign nonexistent key."),
)?)
.map_err(|_| Error::bad_database("key in keyid_key is invalid."))?;
let signatures = cross_signing_key
.get_mut("signatures")
.ok_or_else(|| Error::bad_database("key in keyid_key has no signatures field."))?
.as_object_mut()
.ok_or_else(|| Error::bad_database("key in keyid_key has invalid signatures field."))?
.entry(sender_id.clone())
.or_insert_with(|| serde_json::Map::new().into());
signatures
.as_object_mut()
.ok_or_else(|| Error::bad_database("signatures in keyid_key for a user is invalid."))?
.insert(signature.0, signature.1.into());
self.keyid_key.insert(
&key,
&*serde_json::to_string(&cross_signing_key)
.expect("CrossSigningKey::to_string always works"),
)?;
self.keychangeid_userid
.insert(globals.next_count()?.to_be_bytes(), &*target_id.to_string())?;
Ok(())
}
pub fn keys_changed(&self, since: u64) -> impl Iterator<Item = Result<UserId>> {
self.keychangeid_userid
.range((since + 1).to_be_bytes()..)
.values()
.map(|bytes| {
Ok(
@ -397,29 +525,85 @@ impl Users {
})
}
pub fn all_device_keys(
&self,
user_id: &UserId,
) -> impl Iterator<Item = Result<(String, DeviceKeys)>> {
pub fn get_device_keys(&self, user_id: &UserId, device_id: &str) -> Result<Option<DeviceKeys>> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(device_id.as_bytes());
self.userdeviceid_devicekeys.scan_prefix(key).map(|r| {
let (key, value) = r?;
let userdeviceid = utils::string_from_bytes(
key.rsplit(|&b| b == 0xff)
.next()
.ok_or_else(|| Error::bad_database("UserDeviceID in db is invalid."))?,
)
.map_err(|_| Error::bad_database("UserDeviceId in db is invalid."))?;
Ok((
userdeviceid,
serde_json::from_slice(&*value)
.map_err(|_| Error::bad_database("DeviceKeys in db are invalid."))?,
))
self.keyid_key.get(key)?.map_or(Ok(None), |bytes| {
Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
Error::bad_database("DeviceKeys in db are invalid.")
})?))
})
}
pub fn get_master_key(
&self,
user_id: &UserId,
sender_id: &UserId,
) -> Result<Option<CrossSigningKey>> {
// TODO: hide some signatures
self.userid_masterkeyid
.get(user_id.to_string())?
.map_or(Ok(None), |key| {
self.keyid_key.get(key)?.map_or(Ok(None), |bytes| {
let mut cross_signing_key = serde_json::from_slice::<CrossSigningKey>(&bytes)
.map_err(|_| {
Error::bad_database("CrossSigningKey in db is invalid.")
})?;
// A user is not allowed to see signatures from users other than himself and
// the target user
cross_signing_key.signatures = cross_signing_key
.signatures
.into_iter()
.filter(|(user, _)| user == user_id || user == sender_id)
.collect();
Ok(Some(cross_signing_key))
})
})
}
pub fn get_self_signing_key(
&self,
user_id: &UserId,
sender_id: &UserId,
) -> Result<Option<CrossSigningKey>> {
self.userid_selfsigningkeyid
.get(user_id.to_string())?
.map_or(Ok(None), |key| {
self.keyid_key.get(key)?.map_or(Ok(None), |bytes| {
let mut cross_signing_key = serde_json::from_slice::<CrossSigningKey>(&bytes)
.map_err(|_| {
Error::bad_database("CrossSigningKey in db is invalid.")
})?;
// A user is not allowed to see signatures from users other than himself and
// the target user
cross_signing_key.signatures = cross_signing_key
.signatures
.into_iter()
.filter(|(user, _)| user == user_id || user == sender_id)
.collect();
Ok(Some(cross_signing_key))
})
})
}
pub fn get_user_signing_key(&self, user_id: &UserId) -> Result<Option<CrossSigningKey>> {
self.userid_usersigningkeyid
.get(user_id.to_string())?
.map_or(Ok(None), |key| {
self.keyid_key.get(key)?.map_or(Ok(None), |bytes| {
Ok(Some(serde_json::from_slice(&bytes).map_err(|_| {
Error::bad_database("CrossSigningKey in db is invalid.")
})?))
})
})
}
pub fn add_to_device_event(
&self,
sender: &UserId,

View File

@ -1,6 +1,8 @@
#![feature(proc_macro_hygiene, decl_macro)]
#![warn(rust_2018_idioms)]
pub mod push_rules;
mod client_server;
mod database;
mod error;
@ -44,6 +46,12 @@ fn setup_rocket() -> rocket::Rocket {
client_server::upload_keys_route,
client_server::get_keys_route,
client_server::claim_keys_route,
client_server::create_backup_route,
client_server::update_backup_route,
client_server::get_latest_backup_route,
client_server::get_backup_route,
client_server::add_backup_keys_route,
client_server::get_backup_keys_route,
client_server::set_read_marker_route,
client_server::create_typing_event_route,
client_server::create_room_route,
@ -88,6 +96,10 @@ fn setup_rocket() -> rocket::Rocket {
client_server::delete_device_route,
client_server::delete_devices_route,
client_server::options_route,
client_server::upload_signing_keys_route,
client_server::upload_signatures_route,
client_server::pushers_route,
client_server::set_pushers_route,
//server_server::well_known_server,
//server_server::get_server_version,
//server_server::get_server_keys,

View File

@ -4,6 +4,7 @@ use ruma::{
api::federation::EventHash,
events::{
collections::all::{RoomEvent, StateEvent},
room::member::MemberEvent,
stripped::AnyStrippedStateEvent,
EventJson, EventType,
},
@ -95,4 +96,9 @@ impl PduEvent {
serde_json::from_str::<EventJson<AnyStrippedStateEvent>>(&json)
.expect("EventJson::from_str always works")
}
pub fn to_member_event(&self) -> EventJson<MemberEvent> {
let json = serde_json::to_string(&self).expect("PDUs are always valid");
serde_json::from_str::<EventJson<MemberEvent>>(&json)
.expect("EventJson::from_str always works")
}
}

230
src/push_rules.rs Normal file
View File

@ -0,0 +1,230 @@
use ruma::{
events::push_rules::{ConditionalPushRule, PatternedPushRule, PushCondition, Ruleset},
identifiers::UserId,
push::{Action, Tweak},
};
pub fn default_pushrules(user_id: &UserId) -> Ruleset {
Ruleset {
content: vec![contains_user_name_rule(&user_id)],
override_: vec![
master_rule(),
suppress_notices_rule(),
invite_for_me_rule(),
member_event_rule(),
contains_display_name_rule(),
tombstone_rule(),
roomnotif_rule(),
],
room: vec![],
sender: vec![],
underride: vec![
call_rule(),
encrypted_room_one_to_one_rule(),
room_one_to_one_rule(),
message_rule(),
encrypted_rule(),
],
}
}
pub fn master_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::DontNotify],
default: true,
enabled: false,
rule_id: ".m.rule.master".to_owned(),
conditions: vec![],
}
}
pub fn suppress_notices_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::DontNotify],
default: true,
enabled: true,
rule_id: ".m.rule.suppress_notices".to_owned(),
conditions: vec![PushCondition::EventMatch {
key: "content.msgtype".to_owned(),
pattern: "m.notice".to_owned(),
}],
}
}
pub fn invite_for_me_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![
Action::Notify,
Action::SetTweak(Tweak::Sound("default".to_owned())),
Action::SetTweak(Tweak::Highlight(false)),
],
default: true,
enabled: true,
rule_id: ".m.rule.invite_for_me".to_owned(),
conditions: vec![PushCondition::EventMatch {
key: "content.membership".to_owned(),
pattern: "m.invite".to_owned(),
}],
}
}
pub fn member_event_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::DontNotify],
default: true,
enabled: true,
rule_id: ".m.rule.member_event".to_owned(),
conditions: vec![PushCondition::EventMatch {
key: "content.membership".to_owned(),
pattern: "type".to_owned(),
}],
}
}
pub fn contains_display_name_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![
Action::Notify,
Action::SetTweak(Tweak::Sound("default".to_owned())),
Action::SetTweak(Tweak::Highlight(true)),
],
default: true,
enabled: true,
rule_id: ".m.rule.contains_display_name".to_owned(),
conditions: vec![PushCondition::ContainsDisplayName],
}
}
pub fn tombstone_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(true))],
default: true,
enabled: true,
rule_id: ".m.rule.tombstone".to_owned(),
conditions: vec![
PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.room.tombstone".to_owned(),
},
PushCondition::EventMatch {
key: "state_key".to_owned(),
pattern: "".to_owned(),
},
],
}
}
pub fn roomnotif_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(true))],
default: true,
enabled: true,
rule_id: ".m.rule.roomnotif".to_owned(),
conditions: vec![
PushCondition::EventMatch {
key: "content.body".to_owned(),
pattern: "@room".to_owned(),
},
PushCondition::SenderNotificationPermission {
key: "room".to_owned(),
},
],
}
}
pub fn contains_user_name_rule(user_id: &UserId) -> PatternedPushRule {
PatternedPushRule {
actions: vec![
Action::Notify,
Action::SetTweak(Tweak::Sound("default".to_owned())),
Action::SetTweak(Tweak::Highlight(true)),
],
default: true,
enabled: true,
rule_id: ".m.rule.contains_user_name".to_owned(),
pattern: user_id.localpart().to_owned(),
}
}
pub fn call_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![
Action::Notify,
Action::SetTweak(Tweak::Sound("ring".to_owned())),
Action::SetTweak(Tweak::Highlight(false)),
],
default: true,
enabled: true,
rule_id: ".m.rule.call".to_owned(),
conditions: vec![PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.call.invite".to_owned(),
}],
}
}
pub fn encrypted_room_one_to_one_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![
Action::Notify,
Action::SetTweak(Tweak::Sound("default".to_owned())),
Action::SetTweak(Tweak::Highlight(false)),
],
default: true,
enabled: true,
rule_id: ".m.rule.encrypted_room_one_to_one".to_owned(),
conditions: vec![
PushCondition::RoomMemberCount { is: "2".to_owned() },
PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.room.encrypted".to_owned(),
},
],
}
}
pub fn room_one_to_one_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![
Action::Notify,
Action::SetTweak(Tweak::Sound("default".to_owned())),
Action::SetTweak(Tweak::Highlight(false)),
],
default: true,
enabled: true,
rule_id: ".m.rule.room_one_to_one".to_owned(),
conditions: vec![
PushCondition::RoomMemberCount { is: "2".to_owned() },
PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.room.message".to_owned(),
},
],
}
}
pub fn message_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(false))],
default: true,
enabled: true,
rule_id: ".m.rule.message".to_owned(),
conditions: vec![PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.room.message".to_owned(),
}],
}
}
pub fn encrypted_rule() -> ConditionalPushRule {
ConditionalPushRule {
actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(false))],
default: true,
enabled: true,
rule_id: ".m.rule.encrypted".to_owned(),
conditions: vec![PushCondition::EventMatch {
key: "type".to_owned(),
pattern: "m.room.encrypted".to_owned(),
}],
}
}

View File

@ -1,4 +1,3 @@
# Register endpoints implemented
GET /register yields a set of flows
POST /register can create a user
POST /register downcases capitals in usernames
@ -17,14 +16,12 @@ POST /register rejects registration of usernames with '£'
POST /register rejects registration of usernames with 'é'
POST /register rejects registration of usernames with '\n'
POST /register rejects registration of usernames with '''
# Login endpoints implemented
GET /login yields a set of flows
POST /login can log in as a user
POST /login returns the same device_id as that in the request
POST /login can log in as a user with just the local part of the id
POST /login as non-existing user is rejected
POST /login wrong password is rejected
# Room creation endpoints implemented
POST /createRoom makes a private room
POST /createRoom makes a private room with invites
GET /rooms/:room_id/state/m.room.member/:user_id fetches my membership
@ -49,15 +46,6 @@ Can read configuration endpoint
AS cannot create users outside its own namespace
Changing the actions of an unknown default rule fails with 404
Changing the actions of an unknown rule fails with 404
Trying to add push rule with invalid scope fails with 400
Trying to add push rule with invalid template fails with 400
Trying to add push rule with rule_id with slashes fails with 400
Trying to add push rule with override rule without conditions fails with 400
Trying to add push rule with underride rule without conditions fails with 400
Trying to add push rule with condition without kind fails with 400
Trying to add push rule with content rule without pattern fails with 400
Trying to add push rule with no actions fails with 400
Trying to add push rule with invalid action fails with 400
Trying to get push rules with unknown rule_id fails with 404
GET /events with non-numeric 'limit'
GET /events with negative 'limit'