Compare commits
No commits in common. "aecf9509f52e12b32e6d88582fae0a7c3e8daf68" and "3c0db5f1b5d6084dbae409c071d73dd6132cd0f7" have entirely different histories.
aecf9509f5
...
3c0db5f1b5
9 changed files with 51 additions and 548 deletions
305
Cargo.lock
generated
305
Cargo.lock
generated
|
@ -178,10 +178,10 @@ dependencies = [
|
|||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper 1.1.0",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
|
@ -211,8 +211,8 @@ dependencies = [
|
|||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
|
@ -529,15 +529,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -595,7 +586,6 @@ dependencies = [
|
|||
"ldap3",
|
||||
"md-5",
|
||||
"rand",
|
||||
"reqwest",
|
||||
"rustls",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
|
@ -610,7 +600,6 @@ dependencies = [
|
|||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -642,21 +631,6 @@ 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.2.1"
|
||||
|
@ -793,25 +767,6 @@ version = "0.28.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 0.2.11",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.2"
|
||||
|
@ -823,7 +778,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http 1.0.0",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
|
@ -901,17 +856,6 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.0.0"
|
||||
|
@ -923,17 +867,6 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 0.2.11",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.0"
|
||||
|
@ -941,7 +874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 1.0.0",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -952,8 +885,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
|
@ -969,30 +902,6 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.24",
|
||||
"http 0.2.11",
|
||||
"http-body 0.4.6",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.1.0"
|
||||
|
@ -1002,9 +911,9 @@ dependencies = [
|
|||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"h2 0.4.2",
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
|
@ -1012,19 +921,6 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper 0.14.28",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.3"
|
||||
|
@ -1033,9 +929,9 @@ checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.0.0",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.1.0",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
|
@ -1067,12 +963,6 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
@ -1251,24 +1141,6 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
|
@ -1398,50 +1270,12 @@ version = "1.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae94056a791d0e1217d18b6cbdccb02c61e3054fc69893607f4067e3bb0b1fd1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
@ -1670,46 +1504,6 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2 0.3.24",
|
||||
"http 0.2.11",
|
||||
"http-body 0.4.6",
|
||||
"hyper 0.14.28",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
|
@ -2317,27 +2111,6 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.0"
|
||||
|
@ -2454,16 +2227,6 @@ dependencies = [
|
|||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.1"
|
||||
|
@ -2633,12 +2396,6 @@ dependencies = [
|
|||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
|
@ -2699,7 +2456,6 @@ dependencies = [
|
|||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2732,15 +2488,6 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -2772,18 +2519,6 @@ dependencies = [
|
|||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.91"
|
||||
|
@ -2998,16 +2733,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x509-parser"
|
||||
version = "0.15.1"
|
||||
|
|
|
@ -14,7 +14,6 @@ categories = ["authentication", "games"]
|
|||
opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.77"
|
||||
axum = "0.7.4"
|
||||
base64 = "0.21.7"
|
||||
clap = { version = "4.5.0", features = ["derive"] }
|
||||
|
@ -24,7 +23,6 @@ hmac = "0.12.1"
|
|||
ldap3 = { version = "0.11.3", default-features = false, features = ["tls-rustls"] }
|
||||
md-5 = "0.10.6"
|
||||
rand = "0.8.5"
|
||||
reqwest = { version = "0.11.24", features = ["json"] }
|
||||
secrecy = { version = "0.8.0", features = ["serde"] }
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
sha2 = "0.10.8"
|
||||
|
@ -36,11 +34,9 @@ toml = "0.8.10"
|
|||
tracing = "0.1.40"
|
||||
tracing-error = "0.2.0"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
url = { version = "2.5.0", features = ["serde"] }
|
||||
|
||||
# latest versions with ring 0.16
|
||||
rustls = "=0.21.7"
|
||||
rustls-webpki = "=0.101.6"
|
||||
sct = "=0.7.0"
|
||||
# end of overrides
|
||||
|
||||
async-trait = "0.1.77"
|
||||
|
|
21
README.md
21
README.md
|
@ -25,12 +25,12 @@ these custom users are properly authenticated.
|
|||
- [user token generation](https://wiki.factorio.com/Web_authentication_API) and storage (`POST
|
||||
/api-login`)
|
||||
- LDAP authentication backend
|
||||
- server padlock proxying (to allow e.g. factorio.com users to join servers using a custom auth
|
||||
server)
|
||||
|
||||
### Planned
|
||||
|
||||
- more authentication backends: user file, PAM(?)
|
||||
- server padlock proxying (to allow e.g. factorio.com users to join servers using a custom auth
|
||||
server)
|
||||
|
||||
### Unplanned
|
||||
|
||||
|
@ -42,20 +42,9 @@ these custom users are properly authenticated.
|
|||
|
||||
### Configuring factoriauth
|
||||
|
||||
Copy `config.toml.example` to `config.toml` and adjust as necessary.
|
||||
|
||||
#### Padlock source
|
||||
|
||||
There are two possible sources for server padlock: either generated standalone by factoriauth for
|
||||
completely self-contained setups, or through a padlock proxy.
|
||||
|
||||
For the standalone deployment, `padlock.secret` needs to be a hex-encoded binary string of at least
|
||||
32 bytes - either generate your own, or attempt to start factoriauth once and copy the freshly
|
||||
generated secret from the error message.
|
||||
|
||||
To use the padlock proxy, `padlock.proxy` needs to be set to the base URL of another factorio auth
|
||||
server, e.g. `https://auth.factorio.com`. Server padlocks will be obtained from this auth server,
|
||||
allowing users from both factoriauth and the upstream auth server to join the game server.
|
||||
Copy `config.toml.example` to `config.toml` and adjust as necessary. `padlock-secret` needs to be a
|
||||
hex-encoded binary string of at least 32 bytes - either generate your own, or attempt to start
|
||||
factoriauth once and copy the freshly generated secret from the error message.
|
||||
|
||||
#### LDAP authentication backend notes
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
padlock-secret = ""
|
||||
listen = "[::]:80"
|
||||
|
||||
[padlock]
|
||||
secret = ""
|
||||
#proxy = "https://some-other-auth-server.example"
|
||||
|
||||
[database]
|
||||
connection-string = "sqlite://sqlite.db"
|
||||
|
||||
|
|
137
src/auth/mod.rs
137
src/auth/mod.rs
|
@ -8,16 +8,14 @@ use rand::{
|
|||
thread_rng,
|
||||
};
|
||||
use secrecy::ExposeSecret;
|
||||
use serde::Deserialize;
|
||||
use sha2::Sha256;
|
||||
use thiserror::Error;
|
||||
use time::{macros::format_description, OffsetDateTime};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{event, instrument, Level};
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
config::{AuthBackendConfig, PadlockConfig},
|
||||
config::AuthBackendConfig,
|
||||
db::{Database, UserTokenEntry},
|
||||
secrets::{
|
||||
PadlockGenerationSecret, Password, ServerHash, ServerPadlock, UserServerKey, UserToken,
|
||||
|
@ -40,8 +38,6 @@ pub enum AuthenticationError {
|
|||
Database(#[from] sqlx::Error),
|
||||
#[error("Authentication backend error")]
|
||||
Backend(#[from] ldap3::LdapError),
|
||||
#[error("Padlock proxy error")]
|
||||
PadlockProxy(#[from] PadlockProxyError),
|
||||
#[error("No authentication backends available")]
|
||||
NoBackends,
|
||||
}
|
||||
|
@ -116,15 +112,14 @@ impl UserAuthenticator {
|
|||
};
|
||||
|
||||
let mut db = self.db.lock().await;
|
||||
let token = if let Some(UserTokenEntry::Valid(old_token, _, _)) =
|
||||
db.get_user_token(&username).await?
|
||||
{
|
||||
let token =
|
||||
if let Some(UserTokenEntry::Valid(old_token, _, _)) = db.get_token(&username).await? {
|
||||
old_token
|
||||
} else {
|
||||
let new_token =
|
||||
UserToken::from(Alphanumeric.sample_string(&mut thread_rng(), Self::TOKEN_LEN));
|
||||
|
||||
db.save_user_token(&username, &new_token).await?;
|
||||
db.save_token(&username, &new_token).await?;
|
||||
|
||||
new_token
|
||||
};
|
||||
|
@ -143,9 +138,9 @@ impl UserAuthenticator {
|
|||
) -> Result<(), AuthenticationError> {
|
||||
let mut db = self.db.lock().await;
|
||||
|
||||
if let Some(UserTokenEntry::Valid(user_token, ..)) = &db.get_user_token(username).await? {
|
||||
if let Some(UserTokenEntry::Valid(user_token, ..)) = &db.get_token(username).await? {
|
||||
if token == user_token {
|
||||
db.update_user_token_last_used(username, token).await?;
|
||||
db.update_token_last_used(username, token).await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
@ -155,11 +150,11 @@ impl UserAuthenticator {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServerPadlockSecret {
|
||||
pub struct ServerPadlockGenerator {
|
||||
secret: PadlockGenerationSecret,
|
||||
}
|
||||
|
||||
impl ServerPadlockSecret {
|
||||
impl ServerPadlockGenerator {
|
||||
const HASH_LEN: usize = 32;
|
||||
|
||||
pub fn new(secret: PadlockGenerationSecret) -> Self {
|
||||
|
@ -183,120 +178,6 @@ impl ServerPadlockSecret {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PadlockProxyError {
|
||||
#[error("upstream responded with error")]
|
||||
Upstream(#[from] reqwest::Error),
|
||||
#[error("database error")]
|
||||
Database(#[from] sqlx::Error),
|
||||
#[error("invalid upstream URL")]
|
||||
InvalidUrl(#[from] url::ParseError),
|
||||
#[error("unknown server_hash")]
|
||||
UnknownHash(ServerHash),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServerPadlockProxy {
|
||||
client: reqwest::Client,
|
||||
upstream: Url,
|
||||
database: Arc<Mutex<Box<dyn Database + Send>>>,
|
||||
}
|
||||
|
||||
impl ServerPadlockProxy {
|
||||
pub fn new(
|
||||
upstream: &Url,
|
||||
database: Arc<Mutex<Box<dyn Database + Send>>>,
|
||||
) -> Result<Self, PadlockProxyError> {
|
||||
let client = reqwest::Client::new();
|
||||
let upstream = upstream.join("generate-server-padlock-2")?;
|
||||
|
||||
Ok(Self {
|
||||
client,
|
||||
upstream,
|
||||
database,
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn generate_hash(&self) -> Result<ServerHash, PadlockProxyError> {
|
||||
#[derive(Deserialize)]
|
||||
struct ServerPadlockResponse {
|
||||
server_hash: ServerHash,
|
||||
server_padlock: ServerPadlock,
|
||||
}
|
||||
|
||||
let response: ServerPadlockResponse = self
|
||||
.client
|
||||
.post(self.upstream.clone())
|
||||
.query(&[("api_version", 6)])
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
self.database
|
||||
.lock()
|
||||
.await
|
||||
.save_server_padlock(&response.server_hash, &response.server_padlock)
|
||||
.await?;
|
||||
|
||||
Ok(response.server_hash)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn generate_padlock(
|
||||
&self,
|
||||
server_hash: &ServerHash,
|
||||
) -> Result<ServerPadlock, PadlockProxyError> {
|
||||
let Some(padlock) = self
|
||||
.database
|
||||
.lock()
|
||||
.await
|
||||
.get_server_padlock(server_hash)
|
||||
.await?
|
||||
else {
|
||||
return Err(PadlockProxyError::UnknownHash(server_hash.clone()));
|
||||
};
|
||||
|
||||
Ok(padlock)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ServerPadlockGenerator {
|
||||
Secret(ServerPadlockSecret),
|
||||
Proxy(ServerPadlockProxy),
|
||||
}
|
||||
|
||||
impl ServerPadlockGenerator {
|
||||
pub fn new(
|
||||
config: PadlockConfig,
|
||||
db: Arc<Mutex<Box<dyn Database + Send>>>,
|
||||
) -> Result<Self, PadlockProxyError> {
|
||||
match config {
|
||||
PadlockConfig::Secret(s) => Ok(Self::Secret(ServerPadlockSecret::new(s))),
|
||||
PadlockConfig::Proxy(u) => ServerPadlockProxy::new(&u, db).map(Self::Proxy),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn generate_hash(&self) -> Result<ServerHash, PadlockProxyError> {
|
||||
match self {
|
||||
ServerPadlockGenerator::Secret(_) => Ok(ServerPadlockSecret::generate_hash()),
|
||||
ServerPadlockGenerator::Proxy(p) => p.generate_hash().await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn generate_padlock(
|
||||
&self,
|
||||
server_hash: &ServerHash,
|
||||
) -> Result<ServerPadlock, PadlockProxyError> {
|
||||
match self {
|
||||
ServerPadlockGenerator::Secret(s) => Ok(s.generate_padlock(server_hash)),
|
||||
ServerPadlockGenerator::Proxy(p) => p.generate_padlock(server_hash).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserServerKeyGenerator {
|
||||
user_authenticator: Arc<UserAuthenticator>,
|
||||
|
@ -325,7 +206,7 @@ impl UserServerKeyGenerator {
|
|||
.verify_user_token(username, token)
|
||||
.await?;
|
||||
|
||||
let padlock = self.padlock_generator.generate_padlock(server_hash).await?;
|
||||
let padlock = self.padlock_generator.generate_padlock(server_hash);
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let timestamp = OffsetDateTime::now_utc()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
|
||||
|
||||
use serde::Deserialize;
|
||||
use url::Url;
|
||||
|
||||
use crate::secrets::PadlockGenerationSecret;
|
||||
|
||||
|
@ -12,23 +11,16 @@ fn default_listen_addr() -> SocketAddr {
|
|||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Config {
|
||||
#[serde(with = "hex::serde")]
|
||||
pub padlock_secret: PadlockGenerationSecret,
|
||||
#[serde(default = "default_listen_addr")]
|
||||
pub listen: SocketAddr,
|
||||
|
||||
pub padlock: PadlockConfig,
|
||||
pub database: DatabaseConfig,
|
||||
#[serde(default)]
|
||||
pub auth_backends: Vec<AuthBackendConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub enum PadlockConfig {
|
||||
Secret(#[serde(with = "hex::serde")] PadlockGenerationSecret),
|
||||
Proxy(Url),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
|
|
87
src/db.rs
87
src/db.rs
|
@ -5,7 +5,7 @@ use secrecy::ExposeSecret;
|
|||
use sqlx::{query, query_as, sqlite::SqliteConnectOptions, Connection, SqliteConnection};
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::secrets::{ServerHash, ServerPadlock, UserToken};
|
||||
use crate::secrets::UserToken;
|
||||
|
||||
// TODO: check again if it's possible to pass this as a parameter to query!
|
||||
// const TABLE_USER_TOKENS: &str = "user_tokens";
|
||||
|
@ -25,33 +25,15 @@ pub enum UserTokenEntry {
|
|||
|
||||
#[async_trait]
|
||||
pub trait Database: Debug {
|
||||
async fn get_user_token(
|
||||
&mut self,
|
||||
username: &str,
|
||||
) -> Result<Option<UserTokenEntry>, sqlx::Error>;
|
||||
async fn get_token(&mut self, username: &str) -> Result<Option<UserTokenEntry>, sqlx::Error>;
|
||||
|
||||
async fn save_user_token(
|
||||
async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn update_token_last_used(
|
||||
&mut self,
|
||||
username: &str,
|
||||
token: &UserToken,
|
||||
) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn update_user_token_last_used(
|
||||
&mut self,
|
||||
username: &str,
|
||||
token: &UserToken,
|
||||
) -> Result<(), sqlx::Error>;
|
||||
|
||||
async fn get_server_padlock(
|
||||
&mut self,
|
||||
server_hash: &ServerHash,
|
||||
) -> Result<Option<ServerPadlock>, sqlx::Error>;
|
||||
|
||||
async fn save_server_padlock(
|
||||
&mut self,
|
||||
server_hash: &ServerHash,
|
||||
server_padlock: &ServerPadlock,
|
||||
) -> Result<(), sqlx::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -88,15 +70,6 @@ impl SqliteDatabase {
|
|||
.execute(&mut self.conn)
|
||||
.await?;
|
||||
|
||||
query!(
|
||||
"CREATE TABLE IF NOT EXISTS server_padlocks (
|
||||
hash TEXT NOT NULL UNIQUE,
|
||||
padlock TEXT NOT NULL
|
||||
) STRICT"
|
||||
)
|
||||
.execute(&mut self.conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -104,10 +77,7 @@ impl SqliteDatabase {
|
|||
#[async_trait]
|
||||
impl Database for SqliteDatabase {
|
||||
#[instrument]
|
||||
async fn get_user_token(
|
||||
&mut self,
|
||||
username: &str,
|
||||
) -> Result<Option<UserTokenEntry>, sqlx::Error> {
|
||||
async fn get_token(&mut self, username: &str) -> Result<Option<UserTokenEntry>, sqlx::Error> {
|
||||
struct TokenRow {
|
||||
token: String,
|
||||
valid: bool,
|
||||
|
@ -140,11 +110,7 @@ impl Database for SqliteDatabase {
|
|||
}
|
||||
|
||||
#[instrument]
|
||||
async fn save_user_token(
|
||||
&mut self,
|
||||
username: &str,
|
||||
token: &UserToken,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error> {
|
||||
let token_inner = token.0.expose_secret();
|
||||
query!(
|
||||
"INSERT INTO user_tokens
|
||||
|
@ -162,7 +128,7 @@ impl Database for SqliteDatabase {
|
|||
}
|
||||
|
||||
#[instrument]
|
||||
async fn update_user_token_last_used(
|
||||
async fn update_token_last_used(
|
||||
&mut self,
|
||||
username: &str,
|
||||
token: &UserToken,
|
||||
|
@ -180,41 +146,4 @@ impl Database for SqliteDatabase {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn get_server_padlock(
|
||||
&mut self,
|
||||
server_hash: &ServerHash,
|
||||
) -> Result<Option<ServerPadlock>, sqlx::Error> {
|
||||
let server_hash = &server_hash.0;
|
||||
|
||||
let padlock = query!(
|
||||
"SELECT padlock FROM server_padlocks WHERE hash = $1",
|
||||
server_hash
|
||||
)
|
||||
.fetch_optional(&mut self.conn)
|
||||
.await?;
|
||||
|
||||
Ok(padlock.map(|d| ServerPadlock::from(d.padlock)))
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn save_server_padlock(
|
||||
&mut self,
|
||||
server_hash: &ServerHash,
|
||||
server_padlock: &ServerPadlock,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
let server_hash = &server_hash.0;
|
||||
let server_padlock = server_padlock.0.expose_secret();
|
||||
|
||||
query!(
|
||||
"INSERT INTO server_padlocks (hash, padlock) VALUES ($1, $2)",
|
||||
server_hash,
|
||||
server_padlock
|
||||
)
|
||||
.execute(&mut self.conn)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,8 +129,8 @@ async fn main() -> Result<()> {
|
|||
);
|
||||
}
|
||||
|
||||
let user_authenticator = Arc::new(UserAuthenticator::new(Arc::clone(&database), auth_backends));
|
||||
let padlock_generator = Arc::new(ServerPadlockGenerator::new(config.padlock, database)?);
|
||||
let user_authenticator = Arc::new(UserAuthenticator::new(database, auth_backends));
|
||||
let padlock_generator = Arc::new(ServerPadlockGenerator::new(config.padlock_secret));
|
||||
let user_server_key_generator = Arc::new(UserServerKeyGenerator::new(
|
||||
Arc::clone(&user_authenticator),
|
||||
Arc::clone(&padlock_generator),
|
||||
|
|
|
@ -180,14 +180,8 @@ async fn generate_server_padlock_2(
|
|||
) -> ApiResult<Json<ServerPadlockResponse>> {
|
||||
event!(Level::INFO, "Creating server padlock");
|
||||
|
||||
let server_hash = server_padlock_generator
|
||||
.generate_hash()
|
||||
.await
|
||||
.map_err(AuthenticationError::from)?;
|
||||
let server_padlock = server_padlock_generator
|
||||
.generate_padlock(&server_hash)
|
||||
.await
|
||||
.map_err(AuthenticationError::from)?;
|
||||
let server_hash = ServerPadlockGenerator::generate_hash();
|
||||
let server_padlock = server_padlock_generator.generate_padlock(&server_hash);
|
||||
|
||||
Ok(Json(ServerPadlockResponse {
|
||||
server_hash,
|
||||
|
|
Loading…
Reference in a new issue