mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add user registration
This commit is contained in:
231
Cargo.lock
generated
231
Cargo.lock
generated
@@ -216,6 +216,8 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rc_core",
|
"rc_core",
|
||||||
"rocket",
|
"rocket",
|
||||||
|
"rocket_dyn_templates",
|
||||||
|
"serde",
|
||||||
"steamworks",
|
"steamworks",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
@@ -562,6 +564,15 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.12"
|
version = "0.3.12"
|
||||||
@@ -809,6 +820,18 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.35"
|
version = "1.0.35"
|
||||||
@@ -851,6 +874,15 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuchsia-cprng"
|
name = "fuchsia-cprng"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1049,6 +1081,20 @@ dependencies = [
|
|||||||
"crunchy",
|
"crunchy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "handlebars"
|
||||||
|
version = "5.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pest",
|
||||||
|
"pest_derive",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -1491,6 +1537,26 @@ version = "0.1.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.9.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
@@ -1554,6 +1620,26 @@ dependencies = [
|
|||||||
"simple_asn1",
|
"simple_asn1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a"
|
||||||
|
dependencies = [
|
||||||
|
"kqueue-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue-sys"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1606,6 +1692,17 @@ version = "0.2.13"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
|
checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.8.0",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.30.1"
|
version = "0.30.1"
|
||||||
@@ -1706,6 +1803,18 @@ version = "0.5.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
|
checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.8.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@@ -1736,6 +1845,34 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "normpath"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "6.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.8.0",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"filetime",
|
||||||
|
"fsevent-sys",
|
||||||
|
"inotify",
|
||||||
|
"kqueue",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"mio 0.8.11",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
@@ -2031,6 +2168,51 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"thiserror 2.0.11",
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_derive"
|
||||||
|
version = "2.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_generator",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_generator"
|
||||||
|
version = "2.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
"pest_meta",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.96",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest_meta"
|
||||||
|
version = "2.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"pest",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pgvector"
|
name = "pgvector"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -2765,6 +2947,19 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rocket_dyn_templates"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bbab919c9e67df3f7ac6624a32ef897df4cd61c0969f4d66f3ced0534660d7a"
|
||||||
|
dependencies = [
|
||||||
|
"handlebars",
|
||||||
|
"normpath",
|
||||||
|
"notify",
|
||||||
|
"rocket",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rocket_http"
|
name = "rocket_http"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@@ -2909,6 +3104,15 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -3734,7 +3938,7 @@ dependencies = [
|
|||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio 1.0.3",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
@@ -3942,6 +4146,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uncased"
|
name = "uncased"
|
||||||
version = "0.9.10"
|
version = "0.9.10"
|
||||||
@@ -4067,6 +4277,16 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -4223,6 +4443,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|||||||
171
assets/templates/rc_register.html.hbs
Normal file
171
assets/templates/rc_register.html.hbs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenJam Registration</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: lightgrey;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.form-stripe {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
height: auto;
|
||||||
|
min-height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: grey;
|
||||||
|
padding: 25px 0;
|
||||||
|
box-shadow: 0px 0px 50px 5px grey;
|
||||||
|
}
|
||||||
|
.form-banner {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: dimgrey;
|
||||||
|
color: black;
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
.form-padded {
|
||||||
|
padding: 0 25px;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
a { text-decoration-line: none; }
|
||||||
|
a:link { color: darkgrey; }
|
||||||
|
a:hover { color: white; }
|
||||||
|
a:visited { color: lightgrey; }
|
||||||
|
a:active { color: white; }
|
||||||
|
form {
|
||||||
|
padding-top: 25px;
|
||||||
|
padding-left: 25px;
|
||||||
|
padding-right: 25px;
|
||||||
|
padding-bottom: 250px;
|
||||||
|
}
|
||||||
|
.form-error {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: orange;
|
||||||
|
}
|
||||||
|
.form-error-message {
|
||||||
|
padding: 25px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
.form-input {
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
.form-input-label { display: none; }
|
||||||
|
input:required:invalid.form-input-box {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
input:required:invalid.form-input-box::after {
|
||||||
|
content: "*";
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
border: thin solid white;
|
||||||
|
background-color: lightgrey;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.form-disclaimer {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
.form-submit {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.form-submit-button {
|
||||||
|
background-color: black;
|
||||||
|
color: lightgrey;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
width: 95%;
|
||||||
|
color: lightgrey;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="form-stripe">
|
||||||
|
<h1 class="form-banner">Create an account for <a href="https://git.ngram.ca/OpenJam">OpenJam</a> servers</h1>
|
||||||
|
<h3 class="form-padded">FOSSifying proprietary games since 2025</h3>
|
||||||
|
{{#if error}}
|
||||||
|
<div class="form-error">
|
||||||
|
<p class="form-error-message">{{error}}</p>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<form action="/register" method="post">
|
||||||
|
<div class="form-input">
|
||||||
|
<div class="form-input-label">
|
||||||
|
<label for="display_name">Username</label>
|
||||||
|
</div>
|
||||||
|
<input class="form-input-box" type="text" id="display_name" name="display_name" value="{{form.display_name}}" placeholder="username" required/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-input">
|
||||||
|
<div class="form-input-label">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
</div>
|
||||||
|
<input class="form-input-box" type="password" id="password" name="password" value="{{form.password}}" placeholder="password" required/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-input">
|
||||||
|
<div class="form-input-label">
|
||||||
|
<label for="password_c">Password (again)</label>
|
||||||
|
</div>
|
||||||
|
<input class="form-input-box" type="password" id="password_c" name="password_c" value="{{form.password_c}}" placeholder="password (again)" required/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-input">
|
||||||
|
<div class="form-input-label">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
</div>
|
||||||
|
<input class="form-input-box" type="email" id="email" name="email" value="{{form.email}}" placeholder="email@domain.tld"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-input">
|
||||||
|
<div class="form-input-label">
|
||||||
|
<label for="steam_id">SteamID</label>
|
||||||
|
</div>
|
||||||
|
<input class="form-input-box" type="text" id="steam_id" name="steam_id" value="{{form.steam_id}}" placeholder="7656119XXXXXXXXXX"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-disclaimer">
|
||||||
|
<p>By submitting, you agree to the <a href="https://git.ngram.ca/OpenJam/servers#privacy">privacy policy</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-submit">
|
||||||
|
<input class="form-submit-button" type="submit" value="Submit">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-disclaimer">
|
||||||
|
<p>This project is not affiliated with FreeJam, Cardlife or Robocraft. We're just fixing their """sunset""".</p>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<footer>
|
||||||
|
<div class="footer">
|
||||||
|
{{version}}
|
||||||
|
| <a href="{{source_url}}#readme">About</a>
|
||||||
|
| <a href="https://liberapay.com/NGram">Support Me</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
100
assets/templates/rc_register_success.html.hbs
Normal file
100
assets/templates/rc_register_success.html.hbs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenJam Success</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: lightgrey;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.form-stripe {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
height: auto;
|
||||||
|
min-height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: grey;
|
||||||
|
padding: 25px 0;
|
||||||
|
box-shadow: 0px 0px 50px 5px grey;
|
||||||
|
}
|
||||||
|
.form-banner {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: dimgrey;
|
||||||
|
color: black;
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
.form-padded {
|
||||||
|
padding: 0 25px;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
a { text-decoration-line: none; }
|
||||||
|
a:link { color: darkgrey; }
|
||||||
|
a:hover { color: white; }
|
||||||
|
a:visited { color: lightgrey; }
|
||||||
|
a:active { color: white; }
|
||||||
|
footer {
|
||||||
|
position: relative;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
width: 95%;
|
||||||
|
color: lightgrey;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 25px;
|
||||||
|
}
|
||||||
|
.display-name { display: inline-block; }
|
||||||
|
.display-name:hover {
|
||||||
|
/* Place silly things in here */
|
||||||
|
animation: rotate-clk 1s linear infinite;
|
||||||
|
-webkit-animation: rotate-clk 1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes rotate-clk {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.display-id { display: inline-block; }
|
||||||
|
.display-id:hover {
|
||||||
|
/* Place silly things in here */
|
||||||
|
animation: rotate-cclk .1s linear infinite;
|
||||||
|
-webkit-animation: rotate-cclk .1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes rotate-cclk {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="form-stripe">
|
||||||
|
<h1 class="form-padded">Account '<span class="display-name">{{display_name}}</span>' created!</h1>
|
||||||
|
<h3 class="form-padded">Welcome user #<span class="display-id">{{id}}</span> to Robocraft!</h3>
|
||||||
|
<h5 class="form-padded" style="padding-bottom:250px;">Let the FOSS flow through you...</h5>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="footer">
|
||||||
|
{{version}}
|
||||||
|
| <a href="{{source_url}}#readme">About</a>
|
||||||
|
| <a href="https://liberapay.com/NGram">Support Me</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -15,6 +15,7 @@ default = ["robocraft", "cardlife"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket.workspace = true
|
rocket.workspace = true
|
||||||
|
rocket_dyn_templates = { version = "0.2", features = [ "handlebars" ] }
|
||||||
libfj.workspace = true
|
libfj.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
@@ -24,3 +25,4 @@ hex = "0.4"
|
|||||||
jsonwebtoken = "9"
|
jsonwebtoken = "9"
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
rc_core = { version = "*", optional = true, path = "../rc_core" }
|
rc_core = { version = "*", optional = true, path = "../rc_core" }
|
||||||
|
serde.workspace = true
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
[default]
|
[default]
|
||||||
address = "127.0.0.1"
|
address = "127.0.0.1"
|
||||||
port = 8001
|
port = 8001
|
||||||
|
template_dir = "../assets/templates/"
|
||||||
|
|
||||||
## set only when compiled in debug mode, i.e, `cargo build`
|
## set only when compiled in debug mode, i.e, `cargo build`
|
||||||
[debug]
|
[debug]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ mod debug;
|
|||||||
mod username;
|
mod username;
|
||||||
mod steam;
|
mod steam;
|
||||||
mod email;
|
mod email;
|
||||||
|
mod registration;
|
||||||
|
|
||||||
pub fn stage() -> rocket::fairing::AdHoc {
|
pub fn stage() -> rocket::fairing::AdHoc {
|
||||||
rocket::fairing::AdHoc::on_ignite("robocraft", |rocket| async {
|
rocket::fairing::AdHoc::on_ignite("robocraft", |rocket| async {
|
||||||
@@ -9,6 +10,7 @@ pub fn stage() -> rocket::fairing::AdHoc {
|
|||||||
.attach(email::stage())
|
.attach(email::stage())
|
||||||
.attach(steam::stage())
|
.attach(steam::stage())
|
||||||
.attach(debug::stage())
|
.attach(debug::stage())
|
||||||
|
.attach(registration::stage())
|
||||||
.register("/", rocket::catchers![unauthorized])
|
.register("/", rocket::catchers![unauthorized])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
189
auth/src/robocraft/registration.rs
Normal file
189
auth/src/robocraft/registration.rs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
use rc_core::UserAuthenticator;
|
||||||
|
use rocket::{post, get, form::{Form, FromForm}, routes, http::Status, State};
|
||||||
|
use rocket_dyn_templates::{Template, context};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
const FORM_NAME: &str = "rc_register";
|
||||||
|
const FORM_NAME_SUCCESS: &str = "rc_register_success";
|
||||||
|
|
||||||
|
const VALID_CHARS: &[char] = &[
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'_',
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(FromForm, Serialize)]
|
||||||
|
struct RegisterForm {
|
||||||
|
display_name: String,
|
||||||
|
password: String,
|
||||||
|
password_c: String,
|
||||||
|
email: Option<String>,
|
||||||
|
steam_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Context {
|
||||||
|
form: RegisterForm,
|
||||||
|
error: Option<String>,
|
||||||
|
version: String,
|
||||||
|
source_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version_string() -> String {
|
||||||
|
let name = env!("CARGO_PKG_NAME");
|
||||||
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
//let license = env!("CARGO_PKG_LICENSE");
|
||||||
|
//let repo = env!("CARGO_PKG_REPOSITORY");
|
||||||
|
format!("OpenJam {} {}", name, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_valid_chars(s: &str) -> bool {
|
||||||
|
for c in s.chars() {
|
||||||
|
if !VALID_CHARS.contains(&c) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn registration_ok(form: RegisterForm) -> Template {
|
||||||
|
Template::render(FORM_NAME, Context {
|
||||||
|
form,
|
||||||
|
error: None,
|
||||||
|
version: version_string(),
|
||||||
|
source_url: env!("CARGO_PKG_REPOSITORY").to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn registration_err(form: RegisterForm, error: String) -> Template {
|
||||||
|
Template::render(FORM_NAME, Context {
|
||||||
|
form,
|
||||||
|
error: Some(error),
|
||||||
|
version: version_string(),
|
||||||
|
source_url: env!("CARGO_PKG_REPOSITORY").to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/register", data = "<form>")]
|
||||||
|
async fn form_submit(form: Form<RegisterForm>, config: &State<crate::common::cli::Config>) -> Result<Template, Status> {
|
||||||
|
// password confirmation validation
|
||||||
|
if form.password != form.password_c {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Passwords do not match".to_owned()));
|
||||||
|
}
|
||||||
|
if form.password.len() < 8 {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Password too short (minimum 8 characters)".to_owned()));
|
||||||
|
}
|
||||||
|
if form.password.len() > 128 {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Password too long (maximum 128 characters)".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// email validation
|
||||||
|
let actual_email: Option<String>;
|
||||||
|
if let Some(email) = &form.email {
|
||||||
|
if email.is_empty() {
|
||||||
|
actual_email = None;
|
||||||
|
} else {
|
||||||
|
if !email.contains('@') {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Email must contain @".to_owned()));
|
||||||
|
}
|
||||||
|
let email_exists = config.robocraft.account_provider.user_exists(rc_core::persist::user::UserId::Email(email.to_owned()))
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to check if user email {} exists: {}", email, e);
|
||||||
|
Status { code: 500 }
|
||||||
|
})?;
|
||||||
|
if email_exists {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Email already registered".to_owned()));
|
||||||
|
}
|
||||||
|
actual_email = Some(email.to_owned());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actual_email = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// steam id validation
|
||||||
|
let actual_steam_id: Option<u64>;
|
||||||
|
if let Some(steam_id) = &form.steam_id {
|
||||||
|
if steam_id.is_empty() {
|
||||||
|
actual_steam_id = None;
|
||||||
|
} else {
|
||||||
|
let steam_id = match steam_id.parse() {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(_e) => return Ok(registration_err(form.into_inner(), "Invalid SteamID (not an integer)".to_owned())),
|
||||||
|
};
|
||||||
|
if steam_id >= 7656120_0000000000 || steam_id < 7656119_0000000000 {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Invalid SteamID (should be like 7656119XXXXXXXXXX)".to_owned()));
|
||||||
|
}
|
||||||
|
let steam_exists = config.robocraft.account_provider.user_exists(rc_core::persist::user::UserId::SteamId(steam_id))
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to check if user steam id {} exists: {}", steam_id, e);
|
||||||
|
Status { code: 500 }
|
||||||
|
})?;
|
||||||
|
if steam_exists {
|
||||||
|
return Ok(registration_err(form.into_inner(), "SteamID already registered".to_owned()));
|
||||||
|
}
|
||||||
|
actual_steam_id = Some(steam_id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actual_steam_id = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// username validation
|
||||||
|
if form.display_name.len() < 4 {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Username too short (minimum 4 characters)".to_owned()));
|
||||||
|
}
|
||||||
|
if form.display_name.len() > 32 {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Username too long (maximum 32 characters)".to_owned()));
|
||||||
|
}
|
||||||
|
if !all_valid_chars(&form.display_name.to_lowercase()) {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Invalid username (only alphanumerics and _ allowed)".to_owned()));
|
||||||
|
}
|
||||||
|
let username_exists = config.robocraft.account_provider.user_exists(rc_core::persist::user::UserId::Username(form.display_name.to_owned()))
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Failed to check if user name {} exists: {}", form.display_name, e);
|
||||||
|
Status { code: 500 }
|
||||||
|
})?;
|
||||||
|
if username_exists {
|
||||||
|
return Ok(registration_err(form.into_inner(), "Username already registered".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let user_id = match config.robocraft.account_provider.register(rc_core::persist::user::RegistrationInfo {
|
||||||
|
display_name: form.display_name.clone(),
|
||||||
|
password: form.password.clone(),
|
||||||
|
email: actual_email,
|
||||||
|
steam_id: actual_steam_id,
|
||||||
|
}).await {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(e) => {
|
||||||
|
return Ok(registration_err(form.into_inner(), format!("Registration failed: {}", e)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Template::render(FORM_NAME_SUCCESS, context! {
|
||||||
|
display_name: form.display_name.clone(),
|
||||||
|
id: user_id,
|
||||||
|
version: version_string(),
|
||||||
|
source_url: env!("CARGO_PKG_REPOSITORY").to_owned(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/register")]
|
||||||
|
async fn form_load() -> Template {
|
||||||
|
registration_ok(RegisterForm {
|
||||||
|
display_name: "".to_owned(),
|
||||||
|
password: "".to_owned(),
|
||||||
|
password_c: "".to_owned(),
|
||||||
|
email: None,
|
||||||
|
steam_id: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn stage() -> rocket::fairing::AdHoc {
|
||||||
|
rocket::fairing::AdHoc::on_ignite("Robocraft Steam", |rocket| async {
|
||||||
|
rocket.mount("/", routes![form_load, form_submit])
|
||||||
|
.attach(Template::fairing())
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -259,6 +259,7 @@ impl <C: Clone> super::ConfigProvider<C> for CubeConfig {
|
|||||||
fn server_config(&self) -> super::ServerConfig {
|
fn server_config(&self) -> super::ServerConfig {
|
||||||
super::ServerConfig {
|
super::ServerConfig {
|
||||||
database: self.settings.server.database.clone(),
|
database: self.settings.server.database.clone(),
|
||||||
|
auto_signup: self.settings.server.auto_signup,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ pub struct TypedDevMessage<C> {
|
|||||||
|
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
pub database: String,
|
pub database: String,
|
||||||
|
pub auto_signup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ fn default_slot_upgrades() -> Vec<GarageSlotUpgrade> {
|
|||||||
pub struct ServerSettings {
|
pub struct ServerSettings {
|
||||||
#[serde(default = "default_db_conn")]
|
#[serde(default = "default_db_conn")]
|
||||||
pub database: String,
|
pub database: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub auto_signup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_db_conn() -> String {
|
fn default_db_conn() -> String {
|
||||||
@@ -81,5 +83,6 @@ fn default_db_conn() -> String {
|
|||||||
fn default_server_conf() -> ServerSettings {
|
fn default_server_conf() -> ServerSettings {
|
||||||
ServerSettings {
|
ServerSettings {
|
||||||
database: default_db_conn(),
|
database: default_db_conn(),
|
||||||
|
auto_signup: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::persist::config::ConfigProvider;
|
|||||||
pub struct AccountProvider {
|
pub struct AccountProvider {
|
||||||
cubes: std::sync::Arc<Vec<u32>>,
|
cubes: std::sync::Arc<Vec<u32>>,
|
||||||
garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
garage_upgrades: std::sync::Arc<crate::persist::config::GarageUpgrades>,
|
||||||
|
auto_signups: bool,
|
||||||
secret: Vec<u8>,
|
secret: Vec<u8>,
|
||||||
db: std::sync::Arc<rc_database::Database>,
|
db: std::sync::Arc<rc_database::Database>,
|
||||||
}
|
}
|
||||||
@@ -12,13 +13,15 @@ pub struct AccountProvider {
|
|||||||
impl AccountProvider {
|
impl AccountProvider {
|
||||||
pub async fn load(root: impl AsRef<std::path::Path>, conf: &crate::persist::config::ConfigImpl) -> std::io::Result<Self> {
|
pub async fn load(root: impl AsRef<std::path::Path>, conf: &crate::persist::config::ConfigImpl) -> std::io::Result<Self> {
|
||||||
let token_path = root.as_ref().join(super::TOKEN_SECRET_FILENAME);
|
let token_path = root.as_ref().join(super::TOKEN_SECRET_FILENAME);
|
||||||
let database_uri = <crate::persist::config::ConfigImpl as ConfigProvider<()>>::server_config(conf).database;
|
let server_settings = <crate::persist::config::ConfigImpl as ConfigProvider<()>>::server_config(conf);
|
||||||
|
let database_uri = server_settings.database;
|
||||||
log::debug!("Connecting to user database URI: {}", database_uri);
|
log::debug!("Connecting to user database URI: {}", database_uri);
|
||||||
let db = rc_database::Database::init(&database_uri).await
|
let db = rc_database::Database::init(&database_uri).await
|
||||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotConnected, e))?;
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotConnected, e))?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(conf)),
|
cubes: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::ids(conf)),
|
||||||
garage_upgrades: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::garage_upgrades(conf)),
|
garage_upgrades: std::sync::Arc::new(<crate::persist::config::ConfigImpl as ConfigProvider<()>>::garage_upgrades(conf)),
|
||||||
|
auto_signups: server_settings.auto_signup,
|
||||||
secret: std::fs::read(&token_path)?,
|
secret: std::fs::read(&token_path)?,
|
||||||
db: std::sync::Arc::new(db),
|
db: std::sync::Arc::new(db),
|
||||||
})
|
})
|
||||||
@@ -72,9 +75,14 @@ impl super::UserAuthenticator for AccountProvider {
|
|||||||
user_info
|
user_info
|
||||||
} else {
|
} else {
|
||||||
is_new_user = true;
|
is_new_user = true;
|
||||||
log::info!("New user {}", info.payload.public_id);
|
if self.auto_signups {
|
||||||
super::setup_new_user(&info, &self.db).await.map_err(|e| e.to_string())?;
|
log::info!("New user {}", info.payload.public_id);
|
||||||
self.db.user_by_display_name(info.payload.display_name.clone()).await.map_err(|e| e.to_string())?.unwrap()
|
super::setup_new_user(&info, &self.db).await.map_err(|e| e.to_string())?;
|
||||||
|
self.db.user_by_display_name(info.payload.display_name.clone()).await.map_err(|e| e.to_string())?.unwrap()
|
||||||
|
} else {
|
||||||
|
log::info!("Rejecting user sign-in for `{}` (set settings.server.auto_signup=true to disable this behaviour)", info.payload.public_id);
|
||||||
|
return Err(format!("User does not exist"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let override_password = user_info.password.is_empty() && user_info.steam_id.is_none();
|
let override_password = user_info.password.is_empty() && user_info.steam_id.is_none();
|
||||||
match info.extra {
|
match info.extra {
|
||||||
@@ -128,6 +136,24 @@ impl super::UserAuthenticator for AccountProvider {
|
|||||||
is_new: is_new_user,
|
is_new: is_new_user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn user_exists(&self, user: super::UserId) -> Result<bool, String> {
|
||||||
|
Ok(match user {
|
||||||
|
super::UserId::SteamId(steam_id) => {
|
||||||
|
self.db.user_by_steam_id(steam_id).await.map_err(|e| e.to_string())?.is_some()
|
||||||
|
},
|
||||||
|
super::UserId::Email(email) => {
|
||||||
|
self.db.user_by_email(email).await.map_err(|e| e.to_string())?.is_some()
|
||||||
|
},
|
||||||
|
super::UserId::Username(display_name) => {
|
||||||
|
self.db.user_by_display_name(display_name).await.map_err(|e| e.to_string())?.is_some()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn register(&self, info: super::RegistrationInfo) -> Result<u32, String> {
|
||||||
|
super::register_new_user(&info, &self.db).await.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ fn current_unix_time() -> i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn build_new_account_data(user: &super::UserInfo, db: &rc_database::Database) -> Result<(), rc_database::sea_orm::DbErr> {
|
async fn build_new_account_data(user: &super::UserInfo, db: &rc_database::Database) -> Result<(), rc_database::sea_orm::DbErr> {
|
||||||
//std::fs::create_dir(&root)?;
|
let reg_info = super::RegistrationInfo {
|
||||||
//let garage_dir = root.as_ref().join(super::GARAGE_DIR);
|
display_name: user.payload.display_name.clone(),
|
||||||
//std::fs::create_dir(&garage_dir)?;
|
password: if let super::ExtraUserInfo::Email { password } | super::ExtraUserInfo::Username { password } = &user.extra {
|
||||||
let user_data = db.insert_user(default_user_data(user)).await?;
|
password.to_owned()
|
||||||
db.insert_perms(default_user_perms(user_data.id)).await?;
|
} else {
|
||||||
db.insert_user_aux(default_user_aux_data(user_data.id)).await?;
|
"".to_owned()
|
||||||
db.insert_garages(default_garage_slots(user_data.id)).await?;
|
},
|
||||||
/*for slot in default_garage_slots() {
|
email: None,
|
||||||
let filepath = garage_dir.join(format!("{}.json", slot.slot));
|
steam_id: if let super::ExtraUserInfo::Steam { id } = &user.extra { Some(*id) } else { None },
|
||||||
slot.save(filepath)?;
|
};
|
||||||
}*/
|
register_new_user(®_info, db).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,23 +31,28 @@ pub async fn setup_new_user(user: &super::UserInfo, db: &rc_database::Database)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_user_data(user: &super::UserInfo) -> rc_database::schema::user::ActiveModel {
|
pub async fn register_new_user(info: &super::RegistrationInfo, db: &rc_database::Database) -> Result<u32, rc_database::sea_orm::DbErr> {
|
||||||
let password = if let super::ExtraUserInfo::Email { password } | super::ExtraUserInfo::Username { password } = &user.extra {
|
let user_data = db.insert_user(default_user_data(info)).await?;
|
||||||
//password.to_owned()
|
db.insert_perms(default_user_perms(user_data.id)).await?;
|
||||||
|
db.insert_user_aux(default_user_aux_data(user_data.id)).await?;
|
||||||
|
db.insert_garages(default_garage_slots(user_data.id)).await?;
|
||||||
|
Ok(user_data.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_user_data(info: &super::RegistrationInfo) -> rc_database::schema::user::ActiveModel {
|
||||||
|
let password = {
|
||||||
use argon2::password_hash::PasswordHasher;
|
use argon2::password_hash::PasswordHasher;
|
||||||
let argon2_algo = argon2::Argon2::default();
|
let argon2_algo = argon2::Argon2::default();
|
||||||
let salt = argon2::password_hash::SaltString::generate(&mut argon2::password_hash::rand_core::OsRng);
|
let salt = argon2::password_hash::SaltString::generate(&mut argon2::password_hash::rand_core::OsRng);
|
||||||
match argon2_algo.hash_password(password.as_bytes(), &salt) {
|
match argon2_algo.hash_password(info.password.as_bytes(), &salt) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to hash password for user {}/{}: {}", user.payload.public_id, user.payload.display_name, e);
|
log::error!("Failed to hash password for user {}: {}", info.display_name, e);
|
||||||
"".to_owned()
|
"".to_owned()
|
||||||
},
|
},
|
||||||
Ok(password) => password.to_string(),
|
Ok(password) => password.to_string(),
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
};
|
};
|
||||||
let steam_id = if let super::ExtraUserInfo::Steam { id } = &user.extra {
|
let steam_id = if let Some(id) = info.steam_id {
|
||||||
Some(id.to_string())
|
Some(id.to_string())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -55,28 +60,12 @@ fn default_user_data(user: &super::UserInfo) -> rc_database::schema::user::Activ
|
|||||||
rc_database::schema::user::ActiveModel {
|
rc_database::schema::user::ActiveModel {
|
||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()),
|
creation_time: rc_database::sea_orm::ActiveValue::Set(current_unix_time()),
|
||||||
public_id: rc_database::sea_orm::ActiveValue::Set(user.payload.public_id.clone()),
|
public_id: rc_database::sea_orm::ActiveValue::Set(info.display_name.clone()),
|
||||||
display_name: rc_database::sea_orm::ActiveValue::Set(user.payload.display_name.clone()),
|
display_name: rc_database::sea_orm::ActiveValue::Set(info.display_name.clone()),
|
||||||
password: rc_database::sea_orm::ActiveValue::Set(password),
|
password: rc_database::sea_orm::ActiveValue::Set(password),
|
||||||
email: rc_database::sea_orm::ActiveValue::Set("//TODO".to_owned()),
|
email: rc_database::sea_orm::ActiveValue::Set(info.email.clone().unwrap_or_else(|| "".to_owned())),
|
||||||
steam_id: rc_database::sea_orm::ActiveValue::Set(steam_id),
|
steam_id: rc_database::sea_orm::ActiveValue::Set(steam_id),
|
||||||
}
|
}
|
||||||
|
|
||||||
/*super::AccountInfo {
|
|
||||||
is_mod: false,
|
|
||||||
is_admin: false,
|
|
||||||
is_dev: false,
|
|
||||||
steam_id: None,
|
|
||||||
password: None,
|
|
||||||
inventory: super::UnlockedParts {
|
|
||||||
unlocked: vec![],
|
|
||||||
override_: super::inventory::UnlockOverride::UnlockAll,
|
|
||||||
},
|
|
||||||
garage: super::SelectedGarage {
|
|
||||||
uuid: (0, 0),
|
|
||||||
slot: 0,
|
|
||||||
},
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_user_aux_data(user_id: u32) -> Vec<rc_database::schema::user_aux::ActiveModel> {
|
fn default_user_aux_data(user_id: u32) -> Vec<rc_database::schema::user_aux::ActiveModel> {
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ mod garage_data;
|
|||||||
pub use garage_data::SelectedGarage;
|
pub use garage_data::SelectedGarage;
|
||||||
|
|
||||||
mod initial_data;
|
mod initial_data;
|
||||||
pub use initial_data::setup_new_user;
|
pub use initial_data::{setup_new_user, register_new_user};
|
||||||
|
|
||||||
mod inventory;
|
mod inventory;
|
||||||
pub use inventory::UnlockedParts;
|
pub use inventory::UnlockedParts;
|
||||||
|
|
||||||
mod traits;
|
mod traits;
|
||||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData};
|
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo};
|
||||||
|
|
||||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,24 @@ pub enum ExtraUserInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum UserId {
|
||||||
|
SteamId(u64),
|
||||||
|
Email(String),
|
||||||
|
Username(String),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct UserLoginInfo {
|
pub struct UserLoginInfo {
|
||||||
pub response: libfj::robocraft::AuthenticationResponseInfo,
|
pub response: libfj::robocraft::AuthenticationResponseInfo,
|
||||||
pub is_new: bool,
|
pub is_new: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RegistrationInfo {
|
||||||
|
pub display_name: String,
|
||||||
|
pub password: String,
|
||||||
|
pub email: Option<String>,
|
||||||
|
pub steam_id: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait UserProvider<C> {
|
pub trait UserProvider<C> {
|
||||||
async fn authenticate(&self, user: UserToken, ext: std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync + 'static>>) -> Result<Box<dyn User<C> + Send + Sync>, String>;
|
async fn authenticate(&self, user: UserToken, ext: std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync + 'static>>) -> Result<Box<dyn User<C> + Send + Sync>, String>;
|
||||||
@@ -36,6 +49,8 @@ pub trait UserProvider<C> {
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait UserAuthenticator {
|
pub trait UserAuthenticator {
|
||||||
async fn login(&self, info: UserInfo) -> Result<UserLoginInfo, String>;
|
async fn login(&self, info: UserInfo) -> Result<UserLoginInfo, String>;
|
||||||
|
async fn user_exists(&self, user: UserId) -> Result<bool, String>;
|
||||||
|
async fn register(&self, info: RegistrationInfo) -> Result<u32, String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|||||||
Reference in New Issue
Block a user