Merge pull request 'feature-security' (#15) from feature-security into develop
Reviewed-on: ERRMS/ERRMS#15
This commit is contained in:
commit
0e42c63d7a
|
@ -76,6 +76,12 @@ version = "0.5.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
|
||||
[[package]]
|
||||
name = "ascii_utils"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
@ -87,6 +93,12 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -103,6 +115,15 @@ dependencies = [
|
|||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
|
@ -156,6 +177,12 @@ dependencies = [
|
|||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bufstream"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
|
@ -168,12 +195,24 @@ version = "1.3.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.15"
|
||||
|
@ -186,6 +225,15 @@ dependencies = [
|
|||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "0.10.1"
|
||||
|
@ -193,7 +241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"nom",
|
||||
"nom 5.1.2",
|
||||
"rust-ini",
|
||||
"serde 1.0.115",
|
||||
"serde-hjson",
|
||||
|
@ -219,19 +267,35 @@ dependencies = [
|
|||
"hkdf",
|
||||
"hmac",
|
||||
"percent-encoding 2.1.0",
|
||||
"rand",
|
||||
"rand 0.7.3",
|
||||
"sha2",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"autocfg 1.0.1",
|
||||
"cfg-if 0.1.10",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
|
@ -288,7 +352,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"diesel_derives",
|
||||
"pq-sys",
|
||||
"uuid",
|
||||
"uuid 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -321,6 +385,85 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email"
|
||||
version = "0.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91549a51bb0241165f13d57fc4c72cef063b4088fb078b019ecbf464a45f22e4"
|
||||
dependencies = [
|
||||
"base64 0.9.3",
|
||||
"chrono",
|
||||
"encoding",
|
||||
"lazy_static",
|
||||
"rand 0.4.6",
|
||||
"time",
|
||||
"version_check 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding"
|
||||
version = "0.2.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
|
||||
dependencies = [
|
||||
"encoding-index-japanese",
|
||||
"encoding-index-korean",
|
||||
"encoding-index-simpchinese",
|
||||
"encoding-index-singlebyte",
|
||||
"encoding-index-tradchinese",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-japanese"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-korean"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-simpchinese"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-singlebyte"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding-index-tradchinese"
|
||||
version = "1.20141219.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
|
||||
dependencies = [
|
||||
"encoding_index_tests",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_index_tests"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
|
@ -344,15 +487,17 @@ dependencies = [
|
|||
"diesel_geometry",
|
||||
"env_logger",
|
||||
"iban_validate",
|
||||
"lettre",
|
||||
"lettre_email",
|
||||
"log 0.4.11",
|
||||
"rand",
|
||||
"rand 0.7.3",
|
||||
"rocket",
|
||||
"rocket_contrib",
|
||||
"rust-argon2",
|
||||
"serde 1.0.115",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
"uuid 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -361,18 +506,42 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fast_chemail"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4"
|
||||
dependencies = [
|
||||
"ascii_utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[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 = "fsevent"
|
||||
version = "0.4.0"
|
||||
|
@ -392,6 +561,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-cprng"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
|
@ -423,7 +598,7 @@ version = "0.1.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
@ -466,7 +641,7 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -498,6 +673,16 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winutil",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.3.4"
|
||||
|
@ -523,7 +708,7 @@ dependencies = [
|
|||
"httparse",
|
||||
"language-tags",
|
||||
"log 0.3.9",
|
||||
"mime",
|
||||
"mime 0.2.6",
|
||||
"num_cpus",
|
||||
"time",
|
||||
"traitobject",
|
||||
|
@ -558,7 +743,7 @@ version = "1.5.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"autocfg 1.0.1",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
|
@ -625,6 +810,38 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "338d9a248c4b3ef60c51941c678bb8f64e244c0a98f1eb71db027d1e777a5700"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"bufstream",
|
||||
"fast_chemail",
|
||||
"hostname",
|
||||
"log 0.4.11",
|
||||
"native-tls",
|
||||
"nom 4.2.3",
|
||||
"serde 1.0.115",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lettre_email"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd02480f8dcf48798e62113974d6ccca2129a51d241fa20f1ea349c8a42559d5"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"email",
|
||||
"lettre",
|
||||
"mime 0.3.16",
|
||||
"time",
|
||||
"uuid 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.4"
|
||||
|
@ -633,7 +850,7 @@ checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
|
|||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -675,7 +892,7 @@ version = "0.4.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -705,13 +922,19 @@ dependencies = [
|
|||
"log 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"fuchsia-zircon",
|
||||
"fuchsia-zircon-sys",
|
||||
"iovec",
|
||||
|
@ -748,17 +971,45 @@ dependencies = [
|
|||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "4.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
|
@ -794,7 +1045,7 @@ version = "0.1.43"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"autocfg 1.0.1",
|
||||
"num-traits 0.2.12",
|
||||
]
|
||||
|
||||
|
@ -813,7 +1064,7 @@ version = "0.2.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -832,6 +1083,39 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"foreign-types",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
|
||||
dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pear"
|
||||
version = "0.1.4"
|
||||
|
@ -909,13 +1193,19 @@ dependencies = [
|
|||
"sha-1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ec3341498978de3bfd12d1b22f1af1de22818f5473a11e8a6ef997989e3a212"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 0.1.10",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
|
@ -976,6 +1266,38 @@ dependencies = [
|
|||
"proc-macro2 1.0.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
dependencies = [
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"rdrand",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"libc",
|
||||
"rand_chacha 0.1.1",
|
||||
"rand_core 0.4.2",
|
||||
"rand_hc 0.1.0",
|
||||
"rand_isaac",
|
||||
"rand_jitter",
|
||||
"rand_os",
|
||||
"rand_pcg",
|
||||
"rand_xorshift",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
|
@ -984,9 +1306,19 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
|||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -996,9 +1328,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
|
@ -1008,13 +1355,84 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_isaac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_jitter"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_os"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
dependencies = [
|
||||
"cloudabi",
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"rdrand",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
dependencies = [
|
||||
"autocfg 0.1.7",
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rdrand"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1041,6 +1459,15 @@ version = "0.6.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket"
|
||||
version = "0.4.5"
|
||||
|
@ -1148,6 +1575,39 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "0.8.23"
|
||||
|
@ -1289,6 +1749,20 @@ dependencies = [
|
|||
"unicode-xid 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"rand 0.7.3",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.0"
|
||||
|
@ -1425,12 +1899,22 @@ dependencies = [
|
|||
"percent-encoding 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
|
||||
dependencies = [
|
||||
"rand 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
|
||||
dependencies = [
|
||||
"rand 0.7.3",
|
||||
"serde 1.0.115",
|
||||
]
|
||||
|
||||
|
@ -1512,6 +1996,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "winutil"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
|
|
|
@ -16,11 +16,13 @@ env_logger = "0.7.1"
|
|||
rocket = "0.4.5"
|
||||
diesel = { version = "1.4.5", features = ["postgres", "uuidv07", "chrono"] } #uuidv07 vs uuid to use uuid >= 0.7
|
||||
diesel_geometry = "1.4.0"
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
rust-argon2 = "0.8.2"
|
||||
chrono = { version = "0.4.15", features = ["serde"] }
|
||||
rand = "0.7.3"
|
||||
iban_validate = "4"
|
||||
lettre = "0.9"
|
||||
lettre_email = "0.9"
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
version = "0.4.5"
|
||||
|
|
|
@ -2,9 +2,21 @@
|
|||
connection_string = "postgresql://postgres:qwertz@localhost:5432/postgres"
|
||||
|
||||
[application]
|
||||
url = "http://localhost:8000/"
|
||||
name = "einsatz.online"
|
||||
default_language = "de-DE"
|
||||
fallback_language = "en-US"
|
||||
loglevel = "debug"
|
||||
#Session timeout in seconds. Default is 15 minutes
|
||||
session_timeout = 900
|
||||
upload_path = "uploads/"
|
||||
upload_path = "uploads/"
|
||||
#Maximum login attempts until email is locked for login
|
||||
max_login_attempts = 6
|
||||
#Duration of email lock after max_login_attempts in seconds. Default is 30 minutes
|
||||
login_lock_duration = 1800
|
||||
#How long does it take until tokens expire?
|
||||
reset_password_token_valid_duration = 3600
|
||||
|
||||
[mail]
|
||||
from = "No Reply <noreply@localhost>"
|
||||
reply_to = "support@localhost"
|
|
@ -1,9 +1,13 @@
|
|||
-- Your SQL goes here
|
||||
create table users
|
||||
(
|
||||
id uuid default uuid_generate_v1() not null,
|
||||
id uuid default uuid_generate_v1() not null
|
||||
constraint pk___users___id
|
||||
primary key,
|
||||
password text,
|
||||
email text,
|
||||
constraint pk___users___id
|
||||
primary key (id)
|
||||
);
|
||||
email text not null
|
||||
);
|
||||
|
||||
create unique index users_email_uindex
|
||||
on users (email);
|
||||
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
create table members
|
||||
(
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
entity_id uuid default uuid_generate_v1() not null
|
||||
constraint pk___members___id
|
||||
primary key
|
||||
constraint members_entities_entity_id_fk
|
||||
references entities
|
||||
on update cascade on delete cascade,
|
||||
users_id uuid
|
||||
users_id uuid
|
||||
constraint fk___members___users_id___users
|
||||
references users,
|
||||
firstname text not null,
|
||||
lastname text not null,
|
||||
date_of_birth date,
|
||||
sex smallint,
|
||||
salutation text,
|
||||
place_of_birth text,
|
||||
academic_titles text,
|
||||
references users
|
||||
on update cascade on delete set null,
|
||||
firstname text not null,
|
||||
lastname text not null,
|
||||
date_of_birth date,
|
||||
sex smallint,
|
||||
salutation text,
|
||||
place_of_birth text,
|
||||
academic_titles text,
|
||||
personnel_number integer,
|
||||
ui_language text,
|
||||
nationality text,
|
||||
entrance_date date,
|
||||
birth_name text,
|
||||
iban text,
|
||||
bic text
|
||||
ui_language text,
|
||||
nationality text,
|
||||
entrance_date date,
|
||||
birth_name text,
|
||||
iban text,
|
||||
bic text
|
||||
);
|
||||
|
||||
create unique index members_personnel_number_uindex
|
||||
on members (personnel_number);
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE login_attempts;
|
|
@ -0,0 +1,7 @@
|
|||
-- Your SQL goes here
|
||||
create table login_attempts
|
||||
(
|
||||
id uuid default uuid_generate_v1() primary key,
|
||||
email text not null,
|
||||
timestamp timestamp default now() not null
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
drop table password_resets;
|
|
@ -0,0 +1,17 @@
|
|||
-- Your SQL goes here
|
||||
create table password_resets
|
||||
(
|
||||
token text not null
|
||||
constraint password_resets_pk
|
||||
primary key,
|
||||
user_id uuid not null
|
||||
constraint password_resets_users_id_fk
|
||||
references users
|
||||
on update cascade on delete cascade,
|
||||
issued timestamp default CURRENT_TIMESTAMP not null
|
||||
);
|
||||
|
||||
create unique index password_resets_token_uindex
|
||||
on password_resets (token);
|
||||
|
||||
|
|
@ -20,6 +20,9 @@ $( document ).ready(function() {
|
|||
UnitModule.get_units();
|
||||
$("#add_operation_unit_submit").on("click", UnitModule.add_unit_submit_listener);
|
||||
$(".delete_unit_from_member_button").on("click", UnitModule.delete_unit);
|
||||
|
||||
$("#login_allowed").on("change", LoginModule.login_allowed_checkbox_listener);
|
||||
$("#login_save_button").on("click", LoginModule.login_submit);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -419,4 +422,88 @@ UnitModule = (
|
|||
delete_unit: delete_unit,
|
||||
}
|
||||
}
|
||||
)();
|
||||
)();
|
||||
|
||||
LoginModule = (function(){
|
||||
var login_allowed_checkbox_listener = function(){
|
||||
var ro = $("#login_email").prop('readonly');
|
||||
if(ro){
|
||||
$("#login_email").prop('readonly', false);
|
||||
}else{
|
||||
$("#login_email").prop('readonly', true);
|
||||
}
|
||||
};
|
||||
var login_submit = function(){
|
||||
var login_allowed = $("#login_allowed").prop('checked');
|
||||
|
||||
if(login_allowed){
|
||||
var email = $("#login_email").val();
|
||||
if(email.trim() === ""){
|
||||
alert("Bitte eine Email-Adresse für den Login angeben!");
|
||||
}else{
|
||||
var user_id = $("#login_save_button").data("user-id");
|
||||
if(user_id === ""){//New entry
|
||||
var user = $();
|
||||
user.email = email.trim();
|
||||
user.member_id = member_id;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/users/",
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(user),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$("#login_save_button").data("user-id", data.user_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else{ //Update existing entry
|
||||
var user = $();
|
||||
user.user_id = user_id;
|
||||
user.email = email.trim();
|
||||
user.member_id = member_id;
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: "/api/users/"+user_id,
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(user),
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//Delete user for member
|
||||
$.ajax({
|
||||
type: "DELETE",
|
||||
url: "/api/users/"+$("#login_save_button").data("user-id"),
|
||||
contentType: 'application/json',
|
||||
timeout: 3000,
|
||||
error: function () {
|
||||
alert("Verbindung zum Server unterbrochen!");
|
||||
},
|
||||
success: function (data) {
|
||||
if(is_ok(data)) {
|
||||
$("#login_email").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return{
|
||||
login_allowed_checkbox_listener: login_allowed_checkbox_listener,
|
||||
login_submit: login_submit,
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,7 @@
|
|||
Jemand hat das Zurücksetzen des Passworts auf {{frontpage}} für die Email
|
||||
{{email}}
|
||||
angefordert.
|
||||
Falls dies nicht beabsichtigt war, ignoriere einfach diese E-Mail. Dein altes Passwort bleibt wirksam.
|
||||
|
||||
Um dein Passwort zurückzusetzen, besuche folgende Adresse:
|
||||
{{reset_url}}
|
|
@ -251,6 +251,24 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
{{#if_in_list ../../caller_permissions "modules.member_management.profile.login.view"}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card-header">Login</div>
|
||||
<div class="card-body">
|
||||
<div class="col">
|
||||
<p>Achtung: Wird der Login deaktiviert, werden Email-Adresse und Passwort dauerhaft gelöscht und müssen nach dann ggf. neu gesetzt werden.</p>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" {{#if login.login_allowed}}checked{{/if}} id="login_allowed" {{#if ../readonly}}disabled{{/if}}>
|
||||
<label class="form-check-label" for="login_allowed">Login aktiviert</label>
|
||||
</div><hr>
|
||||
<div class="form-group row">
|
||||
<label for="login_email" class="col-sm-3">Email-Adresse</label>
|
||||
<input type="email" class="form-control col-sm-9" id="login_email" value="{{login.email}}" {{#if (not login.login_allowed)}}readonly{{/if}}{{#if ../readonly}}readonly{{/if}}>
|
||||
</div>
|
||||
{{#if (not ../readonly)}}{{#if_in_list ../../caller_permissions "modules.member_management.profile.login.edit"}}<button type="button" id="login_save_button" class="btn btn-primary" data-user-id="{{login.user_id}}" style="float: right">Speichern</button>{{/if_in_list}}{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>{{/if_in_list}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card bg-light mb-3">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-lq">
|
||||
<h1>Welcome to ERMS!</h1>
|
||||
<h1>Willkommen bei einsatz.online!</h1>
|
||||
</div>
|
||||
{{#if logo_path}}
|
||||
<div class="col">
|
||||
|
@ -17,25 +17,26 @@
|
|||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<h3>Login</h3>
|
||||
<h3>Anmelden</h3>
|
||||
<div class="form-group">
|
||||
<label for="login_email">Email address</label>
|
||||
<label for="login_email">E-Mail Adresse</label>
|
||||
<input type="email" class="form-control" name="login_email" id="login_email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="login_password">Password</label>
|
||||
<label for="login_password">Passwort</label>
|
||||
<input type="password" class="form-control" name="login_password" id="login_password" required>
|
||||
</div>
|
||||
<button type="submit" class="login_submit btn btn-primary">Login</button>
|
||||
<button type="submit" class="login_submit btn btn-primary">Anmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col">
|
||||
<form>
|
||||
<h3>Register</h3>
|
||||
<form method="post" action="/password_reset">
|
||||
<h3>Passwort vergessen?</h3>
|
||||
<div class="form-group">
|
||||
<label for="register_email">Email address</label>
|
||||
<input type="email" class="form-control" name="register_email" id="register_email" required>
|
||||
<label for="email">E-Mail Adresse</label>
|
||||
<input type="email" class="form-control" name="email" id="email" required>
|
||||
</div>
|
||||
<button type="submit" class="login_submit btn btn-secondary">Passwort zurücksetzen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{{> header }}
|
||||
<div class="jumbotron">
|
||||
<div class="row">
|
||||
<div class="col-lq">
|
||||
<h1>Willkommen bei einsatz.online!</h1>
|
||||
</div>
|
||||
{{#if logo_path}}
|
||||
<div class="col">
|
||||
<img class="welcome_logo" src="{{logo_path}}">
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
<form method="post">
|
||||
{{#if alert}}
|
||||
{{> alert}}
|
||||
{{/if}}
|
||||
<h3>Neues Passwort festlegen</h3>
|
||||
<p>Ihr Passwort muss mindestens 10 Zeichen lang sein und sollte Buchstaben, Zahlen und Sonderzeichen enthalten.</p>
|
||||
<div class="form-group">
|
||||
<label for="password">Passwort</label>
|
||||
<input type="password" class="form-control" name="password" id="password" required>
|
||||
</div>
|
||||
<button type="submit" class="login_submit btn btn-primary">Passwort ändern</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{> footer }}
|
|
@ -0,0 +1,47 @@
|
|||
use rocket::State;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::database::controller::connector::establish_connection;
|
||||
use diesel::{sql_query, RunQueryDsl, ExpressionMethods};
|
||||
use diesel::sql_types::{Integer, Text};
|
||||
use crate::database::model::login_protection::LoginAttemptsResult;
|
||||
use chrono::{NaiveDateTime, Duration};
|
||||
use std::ops::Add;
|
||||
use crate::schema::login_attempts::dsl::login_attempts;
|
||||
|
||||
/// Checks if maximum login attempts exceeded. Locks account if exceeded
|
||||
/// Returns:
|
||||
/// * Ok(true) if login attempts exceeded and account got locked
|
||||
/// * Ok(false) if login attempts not exceeded
|
||||
/// * Err(diesel::result::Error) if database error occured
|
||||
pub fn login_attempts_exceeded(settings: &State<Settings>, email: String) -> Result<bool, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let result : Result<LoginAttemptsResult, diesel::result::Error> = sql_query("SELECT COUNT(*) AS count, MAX(timestamp) AS last_timestamp FROM login_attempts WHERE email=$1 AND timestamp > (now() - interval '1800 seconds')").bind::<Text, _>(email.clone()).get_result(&connection);
|
||||
let result = match result{
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
error!("Couldn't check for login attempts: {}", e);
|
||||
return Err(e)
|
||||
}
|
||||
};
|
||||
|
||||
if result.count > settings.application.max_login_attempts as i64 {
|
||||
Ok(true)
|
||||
}else{
|
||||
add_login_attempt(settings, email)?;
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_login_attempt(settings: &State<Settings>, email2: String) -> Result<(), diesel::result::Error>{
|
||||
use crate::schema::login_attempts::dsl::{login_attempts, email};
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::insert_into(login_attempts).values(email.eq(email2)).execute(&connection){
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Couldn't write login attempt into DB! {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,4 +15,6 @@ pub mod roles;
|
|||
pub mod users;
|
||||
pub mod units;
|
||||
pub mod units_members;
|
||||
pub mod create_member;
|
||||
pub mod create_member;
|
||||
pub mod login_protection;
|
||||
pub mod password_resets;
|
|
@ -0,0 +1,109 @@
|
|||
use crate::helper::settings::Settings;
|
||||
use rocket::State;
|
||||
use crate::database::controller::connector::establish_connection;
|
||||
use crate::schema::password_resets::dsl::{password_resets};
|
||||
use rand::{thread_rng, Rng};
|
||||
use rand::distributions::Alphanumeric;
|
||||
use diesel::{ExpressionMethods, RunQueryDsl};
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use diesel::query_dsl::filter_dsl::FilterDsl;
|
||||
use diesel::query_dsl::select_dsl::SelectDsl;
|
||||
use argon2::Config;
|
||||
use crate::schema::users::dsl::users;
|
||||
use diesel::result::Error;
|
||||
|
||||
/// Adds password reset token to database and returns it
|
||||
pub fn add_token(settings: &State<Settings>, user_id: uuid::Uuid) -> Result<String, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let token : String = thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(60)
|
||||
.collect();
|
||||
|
||||
|
||||
match diesel::insert_into(password_resets).values((crate::schema::password_resets::token.eq(token.clone()), crate::schema::password_resets::user_id.eq(user_id))).execute(&connection){
|
||||
Ok(_) => Ok(token),
|
||||
|
||||
Err(e) => {
|
||||
error!("Couldn't create password reset token: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_token(settings: &State<Settings>, token2: String) -> Result<(), diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::delete(password_resets).filter(crate::schema::password_resets::dsl::token.eq(token2)).execute(&connection){
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Couldn't delete token: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_token(settings: &State<Settings>, token2: String) -> Result<uuid::Uuid, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match password_resets.select((crate::schema::password_resets::dsl::user_id, crate::schema::password_resets::issued)).filter(crate::schema::password_resets::dsl::token.eq(token2.clone())).get_result::<(uuid::Uuid, NaiveDateTime)>(&connection){
|
||||
Ok(data) => {
|
||||
let issued = data.1;
|
||||
let now = Utc::now().naive_local();
|
||||
let duration = now.signed_duration_since(issued).num_seconds();
|
||||
if duration > settings.application.reset_password_token_valid_duration{
|
||||
remove_token(settings, token2);
|
||||
return Err(diesel::result::Error::NotFound)
|
||||
}
|
||||
Ok(data.0)
|
||||
},
|
||||
Err(e) => match e{
|
||||
diesel::result::Error::NotFound => {
|
||||
return Err(e)
|
||||
},
|
||||
_ => {
|
||||
error!("Couldn't validate token: {}", e);
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_password_with_token(settings: &State<Settings>, token2: String, password: String) -> Result<(), ()>{
|
||||
let user_id = match validate_token(settings, token2.clone()){
|
||||
Ok(user_id) => user_id,
|
||||
Err(e) => return Err(())
|
||||
};
|
||||
|
||||
let connection =establish_connection(settings);
|
||||
let salt = rand::thread_rng().gen::<[u8; 32]>();
|
||||
let hashed_password = match argon2::hash_encoded(password.as_bytes(), &salt, &Config::default()){
|
||||
Ok(pw) => pw,
|
||||
Err(e) => {
|
||||
error!("Couldn't hash password: {}", e);
|
||||
return Err(())
|
||||
}
|
||||
};
|
||||
|
||||
match diesel::update(users).filter(crate::schema::users::dsl::id.eq(user_id)).set(crate::schema::users::dsl::password.eq(hashed_password)).execute(&connection){
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
error!("Couldn't set new password: {}", e);
|
||||
return Err(())
|
||||
}
|
||||
};
|
||||
|
||||
match remove_token(settings, token2){
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_password(password: String) -> bool{
|
||||
if password.len() < 10{
|
||||
return false
|
||||
}
|
||||
|
||||
true
|
||||
}
|
|
@ -5,6 +5,11 @@ use crate::diesel::QueryDsl;
|
|||
use crate::helper::settings::Settings;
|
||||
use diesel::ExpressionMethods;
|
||||
use rocket::State;
|
||||
use crate::schema::members::columns::entity_id;
|
||||
use diesel::result::Error;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use crate::schema::users::dsl::users;
|
||||
use crate::schema::members::dsl::members;
|
||||
|
||||
pub fn get_user_by_email(email: String, settings: &State<Settings>) -> Option<User> {
|
||||
use crate::schema::users::dsl::email as email1;
|
||||
|
@ -17,3 +22,77 @@ pub fn get_user_by_email(email: String, settings: &State<Settings>) -> Option<Us
|
|||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to get user associated to member entity id
|
||||
/// Returns:
|
||||
/// * Ok<Some<User>> if user found
|
||||
/// * Ok<None> if no user found for member entity id
|
||||
/// * Err<diesel::result::Error> if database error occured
|
||||
pub fn get_user_by_member(settings: &State<Settings>, member_uuid: uuid::Uuid) -> Result<Option<User>, diesel::result::Error>{
|
||||
use crate::schema::members::dsl::members;
|
||||
use crate::schema::users::dsl::*;
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let user : Result<User, diesel::result::Error> = members.inner_join(crate::schema::users::table).filter(entity_id.eq(member_uuid)).select((id, password, email)).first(&connection);
|
||||
match user{
|
||||
Ok(user) => Ok(Some(user)),
|
||||
Err(e) => {
|
||||
match e{
|
||||
Error::NotFound => {Ok(None)}
|
||||
_ => {
|
||||
error!("Couldn't get user for member: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_user_to_member(settings: &State<Settings>, member_uuid: uuid::Uuid, email: String) -> Result<User, diesel::result::Error>{
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
let user : Result<User, diesel::result::Error> = diesel::insert_into(users).values(crate::schema::users::email.eq(email)).get_result(&connection);
|
||||
|
||||
match user{
|
||||
Ok(user) => {
|
||||
match diesel::update(members.filter(crate::schema::members::entity_id.eq(member_uuid))).set(crate::schema::members::users_id.eq(user.id)).execute(&connection){
|
||||
Ok(_) => Ok(user),
|
||||
Err(e) => {
|
||||
error!("Couldn't add user to member: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Couldn't create new user: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_user(settings: &State<Settings>, user_id: uuid::Uuid) -> Result<(), diesel::result::Error>{
|
||||
use crate::schema::users::id;
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::delete(users.filter(id.eq(user_id))).execute(&connection){
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Couldn't delete user: {}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_user_email(settings: &State<Settings>, user_id: uuid::Uuid, email: String) -> Result<User, diesel::result::Error>{
|
||||
|
||||
let connection = establish_connection(settings);
|
||||
|
||||
match diesel::update(users.filter(crate::schema::users::id.eq(user_id))).set(crate::schema::users::email.eq(email)).get_result(&connection){
|
||||
Ok(user) => Ok(user),
|
||||
Err(e) => {
|
||||
error!("Couldn't update user (id: {}) email: {}", user_id, e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
use chrono::NaiveDateTime;
|
||||
use crate::schema::login_attempts;
|
||||
use diesel::sql_types::*;
|
||||
|
||||
#[derive(QueryableByName)]
|
||||
pub struct LoginAttemptsResult{
|
||||
#[sql_type = "BigInt"]
|
||||
pub count : i64,
|
||||
#[sql_type = "Nullable<Timestamp>"]
|
||||
pub last_timestamp: Option<NaiveDateTime>,
|
||||
}
|
|
@ -8,4 +8,5 @@ pub mod member_qualifications;
|
|||
pub mod members;
|
||||
pub mod roles;
|
||||
pub mod users;
|
||||
pub mod units;
|
||||
pub mod units;
|
||||
pub mod login_protection;
|
|
@ -2,5 +2,5 @@
|
|||
pub struct User {
|
||||
pub(crate) id: uuid::Uuid,
|
||||
pub(crate) password: Option<String>,
|
||||
pub(crate) email: Option<String>,
|
||||
pub(crate) email: String,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
pub mod queue;
|
||||
pub mod worker;
|
|
@ -0,0 +1,102 @@
|
|||
use std::sync::RwLock;
|
||||
use std::collections::VecDeque;
|
||||
use chrono::NaiveDateTime;
|
||||
use std::io::{Error, Write, Read};
|
||||
use std::fs::File;
|
||||
use crate::helper::mail_queue::worker::send_mail;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct MailQueue{
|
||||
pub(crate) mails: RwLock<VecDeque<Mail>>
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct Mail{
|
||||
pub(crate) uuid: uuid::Uuid,
|
||||
pub(crate) from: String,
|
||||
pub(crate) to: Vec<String>,
|
||||
pub(crate) subject: String,
|
||||
pub(crate) cc: Vec<String>,
|
||||
pub(crate) bcc: Vec<String>,
|
||||
pub(crate) reply_to: Option<String>,
|
||||
pub(crate) body: String,
|
||||
pub(crate) deliver_until: Option<NaiveDateTime>
|
||||
}
|
||||
|
||||
impl MailQueue{
|
||||
pub fn add_mail(&self, mail: Mail) -> Result<(), std::sync::PoisonError<std::sync::RwLockWriteGuard<'_, std::collections::VecDeque<Mail>>>>{
|
||||
self.mails.write()?.push_back(mail);
|
||||
match self.save_to_disk(){
|
||||
Ok(_) => {}
|
||||
Err(e) => error!("Couldn't save mail_queue to disk: {}", e)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_next(&self) -> Result<Option<Mail>, std::sync::PoisonError<std::sync::RwLockWriteGuard<'_, std::collections::VecDeque<Mail>>>>{
|
||||
// TODO: Check if mail expired (deliver_until check)
|
||||
Ok(self.mails.write()?.pop_front())
|
||||
}
|
||||
pub fn process_next(&self) -> Result<(), std::sync::PoisonError<std::sync::RwLockWriteGuard<'_, std::collections::VecDeque<Mail>>>>{
|
||||
let mail = self.get_next()?;
|
||||
match mail{
|
||||
Some(mail) => {
|
||||
match send_mail(mail.clone()){
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
return self.add_mail(mail)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
pub fn load_or_create_new() -> MailQueue {
|
||||
match std::fs::File::open("mail_queue.txt"){
|
||||
Ok(mut file) => {
|
||||
let mut contents = String::new();
|
||||
match file.read_to_string(&mut contents){
|
||||
Ok(_) => {
|
||||
match serde_json::from_str::<MailQueue>(&contents){
|
||||
Ok(queue) => return queue,
|
||||
Err(e) => {
|
||||
panic!("Couldn't parse mail queue file: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Couldn't read mail_queue.txt! {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Couldn't read mail queue from disk. Creating new empty queue.");
|
||||
MailQueue{
|
||||
mails: RwLock::new(VecDeque::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn save_to_disk(&self) -> std::io::Result<()> {
|
||||
let serialized = serde_json::to_string(&self)?;
|
||||
let mut file = std::fs::File::create("mail_queue.txt")?;
|
||||
file.write_all(serialized.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mail{
|
||||
/// Create Mail with from, to, subject, cc, bcc, reply_to, deliver_until
|
||||
pub(crate) fn new(from: String, to: Vec<String>, subject: String, cc: Vec<String>, bcc: Vec<String>, reply_to: Option<String>, body: String, deliver_until: Option<NaiveDateTime>) -> Mail {
|
||||
Mail{
|
||||
uuid: uuid::Uuid::new_v4(),
|
||||
from,
|
||||
to,
|
||||
subject,
|
||||
cc,
|
||||
bcc,
|
||||
reply_to,
|
||||
body,
|
||||
deliver_until
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
use crate::helper::mail_queue::queue::Mail;
|
||||
use lettre_email::EmailBuilder;
|
||||
use std::process::{Command, Output};
|
||||
use std::io::Error;
|
||||
|
||||
pub fn send_mail(mail: Mail) -> Result<(), ()> {
|
||||
if mail.to.is_empty() || mail.subject.is_empty() || mail.from.is_empty() || mail.body.is_empty() {
|
||||
error!("Couldn't deliver mail {} because to, subject, from or body missing! Deleting mail!", mail.uuid);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let body = mail.body.escape_default().to_string();
|
||||
let subject = mail.subject.escape_default().to_string();
|
||||
|
||||
let mut arg = String::from("echo \"");
|
||||
arg.push_str(&body);
|
||||
arg.push_str("\" | mailx --append='FROM: ");
|
||||
arg.push_str(&mail.from);
|
||||
arg.push_str("' ");
|
||||
if !mail.cc.is_empty(){
|
||||
arg.push_str("--append='CC:");
|
||||
for mail in mail.cc{
|
||||
arg.push_str(&(mail+","));
|
||||
}
|
||||
arg.push_str("' ")
|
||||
}
|
||||
if !mail.bcc.is_empty(){
|
||||
arg.push_str("--append='BCC:");
|
||||
for mail in mail.bcc{
|
||||
arg.push_str(&(mail+","));
|
||||
}
|
||||
arg.push_str("' ")
|
||||
}
|
||||
match mail.reply_to{
|
||||
Some(reply_to) => {
|
||||
arg.push_str(&format!("--append='Reply-To: {} ' ", reply_to))
|
||||
},
|
||||
None => ()
|
||||
}
|
||||
arg.push_str("-s \"");
|
||||
arg.push_str(&subject);
|
||||
arg.push_str("\" ");
|
||||
for receiver in mail.to{
|
||||
arg.push_str(&(receiver+" "));
|
||||
}
|
||||
|
||||
match Command::new("sh").arg("-c").arg(arg).output(){
|
||||
Ok(output) => {
|
||||
if !output.status.success(){
|
||||
error!("Couldn't send mail: {} {} {}", output.status, String::from_utf8_lossy(&output.stderr), String::from_utf8_lossy(&output.stdout));
|
||||
Err(())
|
||||
}else {
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Couldn't send mail: {}", e);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
use rocket_contrib::templates::handlebars::Handlebars;
|
||||
|
||||
pub struct MailTemplates{
|
||||
pub registry: Handlebars,
|
||||
}
|
|
@ -11,3 +11,5 @@ pub mod settings;
|
|||
pub mod sitebuilder;
|
||||
pub mod translate_diesel_error;
|
||||
pub mod user_request_guard;
|
||||
pub mod mail_queue;
|
||||
pub mod mail_templates;
|
|
@ -130,7 +130,7 @@ mod tests {
|
|||
let user = User {
|
||||
id: uuid,
|
||||
password: None,
|
||||
email: None,
|
||||
email: "test@test.de".to_string(),
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
@ -155,7 +155,7 @@ mod tests {
|
|||
let user = User {
|
||||
id: uuid,
|
||||
password: None,
|
||||
email: None,
|
||||
email: "test@test.de".to_string(),
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
@ -181,7 +181,7 @@ mod tests {
|
|||
let user = User {
|
||||
id: uuid,
|
||||
password: None,
|
||||
email: None,
|
||||
email: "test@test.de".to_string(),
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
@ -204,7 +204,7 @@ mod tests {
|
|||
let user = User {
|
||||
id: uuid,
|
||||
password: None,
|
||||
email: None,
|
||||
email: "test@test.de".to_string(),
|
||||
};
|
||||
|
||||
let inserted_cookie = storage.add(
|
||||
|
|
|
@ -8,17 +8,29 @@ pub struct Database {
|
|||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Application {
|
||||
pub url: String,
|
||||
pub default_language: String,
|
||||
pub fallback_language: String,
|
||||
pub loglevel: String,
|
||||
pub session_timeout: i64,
|
||||
pub upload_path: String,
|
||||
pub max_login_attempts: i32,
|
||||
pub login_lock_duration: i32,
|
||||
pub name: String,
|
||||
pub reset_password_token_valid_duration: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Mail{
|
||||
pub from : String,
|
||||
pub reply_to : String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
pub struct Settings {
|
||||
pub database: Database,
|
||||
pub application: Application,
|
||||
pub mail: Mail
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
|
|
47
src/main.rs
47
src/main.rs
|
@ -20,6 +20,13 @@ use crate::helper::session_cookies::model::SessionCookieStorage;
|
|||
use helper::settings::Settings;
|
||||
use rocket_contrib::serve::StaticFiles;
|
||||
use rocket_contrib::templates::Template;
|
||||
use std::{thread, time};
|
||||
use chrono::{DateTime, Utc};
|
||||
use crate::helper::mail_queue::queue::{MailQueue, Mail};
|
||||
use std::sync::{PoisonError, RwLockWriteGuard, Arc};
|
||||
use std::collections::VecDeque;
|
||||
use rocket_contrib::templates::handlebars::{Handlebars, TemplateFileError};
|
||||
use crate::helper::mail_templates::MailTemplates;
|
||||
|
||||
pub mod database;
|
||||
pub mod helper;
|
||||
|
@ -38,16 +45,38 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
// Initialize storage for session cookies
|
||||
let cookie_storage = SessionCookieStorage::new();
|
||||
|
||||
debug!(
|
||||
"Hello, world! Default Language: {}",
|
||||
settings.application.default_language
|
||||
);
|
||||
// Initialize mail queue for second thread handling outgoing mails
|
||||
// We are using Arc to access mail queue in all threads
|
||||
let mail_queue = Arc::new(MailQueue::load_or_create_new());
|
||||
let c_lock = Arc::clone(&mail_queue);
|
||||
|
||||
thread::spawn(move ||{
|
||||
loop {
|
||||
match c_lock.process_next(){
|
||||
Ok(_) => {}
|
||||
Err(e) => {error!("MailQueue poisoned: {}", e)}
|
||||
}
|
||||
thread::sleep(time::Duration::from_millis(500)) // Only check for new mails ever 500 ms
|
||||
}
|
||||
});
|
||||
|
||||
let mut mail_templates = MailTemplates{ registry: Handlebars::new() };
|
||||
match mail_templates.registry.register_templates_directory(".hbs", "resources/mail_templates"){
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("Couldn't register mail templates: {}",e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
rocket::ignite()
|
||||
.manage(settings)
|
||||
.manage(cookie_storage)
|
||||
.manage(mail_queue)
|
||||
.manage(mail_templates)
|
||||
.register(catchers![helper::server_errors::unauthorized, helper::server_errors::forbidden, helper::server_errors::notfound, helper::server_errors::notimplemented])
|
||||
.mount(
|
||||
"/",
|
||||
|
@ -55,12 +84,15 @@ fn main() {
|
|||
modules::dashboard::view::dashboard,
|
||||
modules::welcome::view::welcome_get::welcome_get,
|
||||
modules::welcome::view::welcome_post::welcome_post,
|
||||
modules::welcome::view::welcome_post::password_reset_post,
|
||||
modules::welcome::view::welcome_post::password_change_post,
|
||||
modules::welcome::view::welcome_post::password_change_get,
|
||||
modules::welcome::view::login_select_get::login_select_get,
|
||||
modules::welcome::view::logout::logout_get,
|
||||
modules::member_management::view::selection_get::member_management_selection_get,
|
||||
modules::member_management::view::selection_post::member_management_selection_post,
|
||||
modules::member_management::view::core_data_get::member_management_core_data_get,
|
||||
modules::member_management::view::core_data_post::member_management_core_data_post,
|
||||
modules::member_management::view::profile_get::member_management_core_data_get,
|
||||
modules::member_management::view::profile_post::member_management_core_data_post,
|
||||
modules::member_management::view::groups_get::member_management_groups_get,
|
||||
modules::api::member_management::view::member_qualifications::api_member_remove_qualification,
|
||||
modules::api::member_management::view::member_qualifications::api_member_get_qualifications,
|
||||
|
@ -85,6 +117,9 @@ fn main() {
|
|||
modules::api::units::read::read_unit_list,
|
||||
modules::api::units::update::put_member_in_unit,
|
||||
modules::api::units::delete::delete_member_from_unit,
|
||||
modules::api::users::create::create_user,
|
||||
modules::api::users::delete::delete_user,
|
||||
modules::api::users::update::update_user,
|
||||
modules::member_management::view::personal_profile::member_management_personal_profile_get,
|
||||
modules::member_management::view::personal_profile::member_management_personal_profile_post,
|
||||
modules::member_management::view::create_member::member_management_add_member,
|
||||
|
|
|
@ -2,4 +2,5 @@ pub mod groups;
|
|||
pub mod member_management;
|
||||
pub mod members;
|
||||
pub mod model;
|
||||
pub mod units;
|
||||
pub mod units;
|
||||
pub mod users;
|
|
@ -14,7 +14,7 @@ pub fn read_unit_list(
|
|||
settings: State<Settings>,
|
||||
cookie: SessionCookie,
|
||||
) -> Result<Json<Vec<RawUnit>>, Json<ApiErrorWrapper>> {
|
||||
let caller = parse_member_cookie(cookie.member)?;
|
||||
let _caller = parse_member_cookie(cookie.member)?;
|
||||
|
||||
match get_units(&settings){
|
||||
Ok(units) => Ok(Json(units)),
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
use crate::helper::settings::Settings;
|
||||
use rocket::State;
|
||||
use crate::helper::session_cookies::model::SessionCookie;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
|
||||
use rocket_contrib::json::Json;
|
||||
use crate::modules::api::member_management::controller::parser::parse_member_cookie;
|
||||
use crate::helper::check_access::check_access;
|
||||
use crate::database::controller::groups::get_groups_for_member;
|
||||
use rocket::request::Form;
|
||||
use crate::database::controller::users::add_user_to_member;
|
||||
use crate::helper::translate_diesel_error::translate_diesel;
|
||||
|
||||
#[derive(Queryable, Clone, Deserialize, Serialize)]
|
||||
pub struct CreateUserData{
|
||||
pub(crate) email: String,
|
||||
pub(crate) member_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[post("/api/users", format = "json", data = "<create_user_data>")]
|
||||
pub fn create_user(settings: State<Settings>, cookie: SessionCookie, create_user_data: Json<CreateUserData>) -> Result<Json<Login>, Json<ApiErrorWrapper>>{
|
||||
let caller = parse_member_cookie(cookie.member)?;
|
||||
|
||||
let data = create_user_data.into_inner();
|
||||
let member_groups = get_groups_for_member(&settings, data.member_id);
|
||||
|
||||
if caller.entity_id != data.member_id { //Skip permission check if user edits own login
|
||||
if !check_access(&settings, data.member_id, member_groups, caller.entity_id, "modules.member_management.profile.login.edit".to_string()) {
|
||||
return Err(Json(ApiError::new(401, "Keine Rechte Login für dieses Mitglied anzulegen!".to_string()).to_wrapper()))
|
||||
}
|
||||
}
|
||||
|
||||
match add_user_to_member(&settings, data.member_id, data.email){
|
||||
Ok(user) => Ok(Json(Login{
|
||||
user_id: Some(user.id),
|
||||
email: Some(user.email),
|
||||
login_allowed: true
|
||||
})),
|
||||
Err(e) => Err(translate_diesel(e))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
use crate::helper::settings::Settings;
|
||||
use rocket::State;
|
||||
use crate::helper::session_cookies::model::SessionCookie;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
|
||||
use rocket_contrib::json::Json;
|
||||
use crate::modules::api::member_management::controller::parser::{parse_member_cookie, parse_uuid};
|
||||
use crate::helper::check_access::check_access;
|
||||
use crate::database::controller::groups::get_groups_for_member;
|
||||
use rocket::request::Form;
|
||||
use crate::database::controller::users::{add_user_to_member, remove_user};
|
||||
use crate::helper::translate_diesel_error::translate_diesel;
|
||||
use crate::database::controller::members::get_members_by_user_uuid;
|
||||
|
||||
#[delete("/api/users/<user_id>", format = "json")]
|
||||
pub fn delete_user(settings: State<Settings>, cookie: SessionCookie, user_id: String) -> Result<(), Json<ApiErrorWrapper>>{
|
||||
let caller = parse_member_cookie(cookie.member)?;
|
||||
|
||||
let user_id = parse_uuid(user_id)?;
|
||||
let member = get_members_by_user_uuid(user_id, &settings);
|
||||
let member = match member.first(){
|
||||
Some(member) => member,
|
||||
None => return Err(Json(ApiError::new(404, "Nicht gefunden.".to_string()).to_wrapper()))
|
||||
};
|
||||
|
||||
let member_groups = get_groups_for_member(&settings, member.entity_id);
|
||||
|
||||
if caller.entity_id != member.entity_id { //Skip permission check if user edits own login
|
||||
if !check_access(&settings, member.entity_id, member_groups, caller.entity_id, "modules.member_management.profile.login.edit".to_string()) {
|
||||
return Err(Json(ApiError::new(401, "Keine Rechte Login für dieses Mitglied anzulegen!".to_string()).to_wrapper()))
|
||||
}
|
||||
}
|
||||
|
||||
match remove_user(&settings, user_id){
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(translate_diesel(e))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
pub mod create;
|
||||
pub mod delete;
|
||||
pub mod update;
|
||||
pub mod read;
|
|
@ -0,0 +1,48 @@
|
|||
use crate::helper::settings::Settings;
|
||||
use rocket::State;
|
||||
use crate::helper::session_cookies::model::SessionCookie;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use crate::modules::api::model::api_outcome::{ApiErrorWrapper, ApiError};
|
||||
use rocket_contrib::json::Json;
|
||||
use crate::modules::api::member_management::controller::parser::{parse_member_cookie, parse_uuid};
|
||||
use crate::helper::check_access::check_access;
|
||||
use crate::database::controller::groups::get_groups_for_member;
|
||||
use rocket::request::Form;
|
||||
use crate::database::controller::users::{add_user_to_member, update_user_email};
|
||||
use crate::helper::translate_diesel_error::translate_diesel;
|
||||
|
||||
#[derive(Queryable, Clone, Deserialize, Serialize)]
|
||||
pub struct UpdateUserData{
|
||||
pub(crate) user_id: uuid::Uuid,
|
||||
pub(crate) email: String,
|
||||
pub(crate) member_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[put("/api/users/<user_id>", format = "json", data = "<update_user_data>")]
|
||||
pub fn update_user(settings: State<Settings>, cookie: SessionCookie, user_id: String, update_user_data: Json<UpdateUserData>) -> Result<Json<Login>, Json<ApiErrorWrapper>>{
|
||||
let caller = parse_member_cookie(cookie.member)?;
|
||||
let data = update_user_data.into_inner();
|
||||
|
||||
let user_id = parse_uuid(user_id)?;
|
||||
|
||||
if user_id != data.user_id{
|
||||
return Err(Json(ApiError::new(400, "User id's doesn't match".to_string()).to_wrapper()))
|
||||
}
|
||||
|
||||
let member_groups = get_groups_for_member(&settings, data.member_id);
|
||||
|
||||
if caller.entity_id != data.member_id { //Skip permission check if user edits own login
|
||||
if !check_access(&settings, data.member_id, member_groups, caller.entity_id, "modules.member_management.profile.login.edit".to_string()) {
|
||||
return Err(Json(ApiError::new(401, "Keine Rechte Login für dieses Mitglied zu verändern!".to_string()).to_wrapper()))
|
||||
}
|
||||
}
|
||||
|
||||
match update_user_email(&settings, user_id, data.email){
|
||||
Ok(user) => Ok(Json(Login{
|
||||
user_id: Some(user.id),
|
||||
email: Some(user.email),
|
||||
login_allowed: true
|
||||
})),
|
||||
Err(e) => Err(translate_diesel(e))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
use crate::database::model::users::User;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use diesel::result::Error;
|
||||
|
||||
impl From<Result<Option<User>, diesel::result::Error>> for Login{
|
||||
fn from(u: Result<Option<User>, Error>) -> Self {
|
||||
match u{
|
||||
Ok(user_option) => match user_option{
|
||||
Some(user) => {
|
||||
Login{
|
||||
user_id: Some(user.id),
|
||||
email: Some(user.email),
|
||||
login_allowed: true
|
||||
}
|
||||
},
|
||||
None => {
|
||||
Login{
|
||||
user_id: None,
|
||||
email: None,
|
||||
login_allowed: false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => Login{
|
||||
user_id: None,
|
||||
email: None,
|
||||
login_allowed: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ use crate::database::controller::member_qualifications::get_qualifcation_categor
|
|||
use crate::helper::age_calculator::calculate_age;
|
||||
use crate::helper::caller_permissions::get_permissions_for_context;
|
||||
use crate::helper::check_access::check_access_legacy;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::helper::sitebuilder::model::general::{Footer, Header, Script, Stylesheet};
|
||||
use crate::helper::sitebuilder::model::sidebar::Sidebar;
|
||||
|
@ -16,6 +17,7 @@ use rocket::http::Status;
|
|||
use rocket::State;
|
||||
use rocket_contrib::templates::Template;
|
||||
use crate::database::controller::units_members::get_members_units;
|
||||
use crate::database::controller::users::get_user_by_member;
|
||||
|
||||
pub fn handle_view(
|
||||
settings: &State<Settings>,
|
||||
|
@ -35,7 +37,7 @@ pub fn handle_view(
|
|||
},
|
||||
],
|
||||
};
|
||||
let mut sidebar = Sidebar::new(member.clone());
|
||||
let sidebar = Sidebar::new(member.clone());
|
||||
|
||||
let age = match member.date_of_birth {
|
||||
Some(date) => Some(calculate_age(date)),
|
||||
|
@ -49,9 +51,11 @@ pub fn handle_view(
|
|||
let caller_permissions = get_permissions_for_context(settings, &member, &member);
|
||||
let units = match get_members_units(settings, member.entity_id){
|
||||
Ok(units) => units,
|
||||
Err(e) => return Err(Status::InternalServerError)
|
||||
Err(_) => return Err(Status::InternalServerError)
|
||||
};
|
||||
|
||||
let login = Login::from(get_user_by_member(settings, member.entity_id));
|
||||
|
||||
return Ok(Template::render(
|
||||
"module_member_management_personal_profile",
|
||||
MemberModuleProfile {
|
||||
|
@ -67,6 +71,7 @@ pub fn handle_view(
|
|||
sidebar,
|
||||
caller_permissions,
|
||||
units,
|
||||
login
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -89,7 +94,7 @@ pub fn handle_edit(
|
|||
},
|
||||
],
|
||||
};
|
||||
let mut sidebar = Sidebar::new(member.clone());
|
||||
let sidebar = Sidebar::new(member.clone());
|
||||
|
||||
let age = match member.date_of_birth {
|
||||
Some(date) => Some(calculate_age(date)),
|
||||
|
@ -104,9 +109,11 @@ pub fn handle_edit(
|
|||
|
||||
let units = match get_members_units(settings, member.entity_id){
|
||||
Ok(units) => units,
|
||||
Err(e) => return Err(Status::InternalServerError)
|
||||
Err(_) => return Err(Status::InternalServerError)
|
||||
};
|
||||
|
||||
let login = Login::from(get_user_by_member(settings, member.entity_id));
|
||||
|
||||
return Ok(Template::render(
|
||||
"module_member_management_personal_profile",
|
||||
MemberModuleProfile {
|
||||
|
@ -121,7 +128,8 @@ pub fn handle_edit(
|
|||
readonly: false,
|
||||
sidebar,
|
||||
caller_permissions,
|
||||
units
|
||||
units,
|
||||
login
|
||||
},
|
||||
));
|
||||
}
|
|
@ -9,6 +9,7 @@ use crate::helper::settings::Settings;
|
|||
use crate::helper::sitebuilder::model::general::{Footer, Header, Script, Stylesheet};
|
||||
use crate::helper::sitebuilder::model::sidebar::Sidebar;
|
||||
use crate::modules::member_management::model::member::Member;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
use crate::modules::member_management::model::member_module::{
|
||||
EmptyMemberModuleProfile, MemberModuleProfile,
|
||||
};
|
||||
|
@ -16,6 +17,7 @@ use rocket::http::Status;
|
|||
use rocket::State;
|
||||
use rocket_contrib::templates::Template;
|
||||
use crate::database::controller::units_members::get_members_units;
|
||||
use crate::database::controller::users::get_user_by_member;
|
||||
|
||||
/// No member submitted, only show searchbar
|
||||
pub fn handle_empty(caller: Member) -> Result<Template, Status> {
|
||||
|
@ -86,9 +88,11 @@ pub fn handle_view(
|
|||
|
||||
let units = match get_members_units(settings, member.entity_id){
|
||||
Ok(units) => units,
|
||||
Err(e) => return Err(Status::InternalServerError)
|
||||
Err(_) => return Err(Status::InternalServerError)
|
||||
};
|
||||
|
||||
let login = Login::from(get_user_by_member(settings, member.entity_id));
|
||||
|
||||
return Ok(Template::render(
|
||||
"module_member_management_profile",
|
||||
MemberModuleProfile {
|
||||
|
@ -104,6 +108,7 @@ pub fn handle_view(
|
|||
sidebar,
|
||||
caller_permissions,
|
||||
units,
|
||||
login,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -155,9 +160,11 @@ pub fn handle_edit(
|
|||
|
||||
let units = match get_members_units(settings, member.entity_id){
|
||||
Ok(units) => units,
|
||||
Err(e) => return Err(Status::InternalServerError)
|
||||
Err(_) => return Err(Status::InternalServerError)
|
||||
};
|
||||
|
||||
let login = Login::from(get_user_by_member(settings, member.entity_id));
|
||||
|
||||
return Ok(Template::render(
|
||||
"module_member_management_profile",
|
||||
MemberModuleProfile {
|
||||
|
@ -172,7 +179,8 @@ pub fn handle_edit(
|
|||
readonly: false,
|
||||
sidebar,
|
||||
caller_permissions,
|
||||
units
|
||||
units,
|
||||
login
|
||||
},
|
||||
));
|
||||
}
|
|
@ -6,4 +6,5 @@ pub mod member_profile;
|
|||
pub mod member_trait;
|
||||
pub mod render;
|
||||
pub mod member_personal_profile;
|
||||
pub mod create_member;
|
||||
pub mod create_member;
|
||||
pub mod login_trait;
|
|
@ -0,0 +1,6 @@
|
|||
#[derive(Serialize)]
|
||||
pub struct Login{
|
||||
pub(crate) user_id: Option<uuid::Uuid>,
|
||||
pub(crate) email: Option<String>,
|
||||
pub(crate) login_allowed: bool,
|
||||
}
|
|
@ -8,6 +8,7 @@ use crate::modules::member_management::model::member::{Member, MemberWithAccess}
|
|||
use crate::modules::member_management::model::qualifications::QualificationCategory;
|
||||
use std::collections::HashMap;
|
||||
use crate::database::model::units::Unit;
|
||||
use crate::modules::member_management::model::login::Login;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct MemberModuleSelection {
|
||||
|
@ -39,6 +40,7 @@ pub struct MemberModuleProfile {
|
|||
pub sidebar: Sidebar,
|
||||
pub caller_permissions: Vec<String>,
|
||||
pub units: Vec<Unit>,
|
||||
pub login: Login,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
@ -4,3 +4,4 @@ pub mod licenses;
|
|||
pub mod member;
|
||||
pub mod member_module;
|
||||
pub mod qualifications;
|
||||
pub mod login;
|
|
@ -40,16 +40,12 @@ pub fn member_management_add_member_post(
|
|||
None => return Err(Status::Unauthorized),
|
||||
};
|
||||
|
||||
debug!("Test");
|
||||
|
||||
if !caller.has_permission("modules.member_management.profile.create".to_string()){
|
||||
return Err(Status::Unauthorized)
|
||||
}
|
||||
|
||||
debug!("Test2");
|
||||
|
||||
match create_member(&settings, create_member_form.into_inner()){
|
||||
Ok(entity_id) => Ok(Redirect::to(format!("/portal/mm/profile?action=view&id={}",entity_id))),
|
||||
Err(e) => Err(Status::InternalServerError),
|
||||
Err(_) => Err(Status::InternalServerError),
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
pub mod core_data_get;
|
||||
pub mod core_data_post;
|
||||
pub mod profile_get;
|
||||
pub mod profile_post;
|
||||
pub mod groups_get;
|
||||
pub mod selection_get;
|
||||
pub mod selection_post;
|
||||
|
|
|
@ -1,20 +1,53 @@
|
|||
use crate::database::controller::users::get_user_by_email;
|
||||
use crate::database::model::users::User;
|
||||
use crate::helper::session_cookies::model::SessionCookieStorage;
|
||||
use crate::modules::welcome::model::login_error_type::LoginError;
|
||||
use crate::helper::settings::Settings;
|
||||
use crate::modules::welcome::model::login_form::LoginForm;
|
||||
use chrono::{Duration, Utc};
|
||||
use rocket::http::{Cookie, Cookies};
|
||||
use rocket::State;
|
||||
use crate::database::controller::login_protection::login_attempts_exceeded;
|
||||
|
||||
pub fn check_login(login_form: LoginForm, settings: &State<Settings>) -> Result<User, LoginError> {
|
||||
let user: User = match get_user_by_email(login_form.login_email.clone(), &settings){
|
||||
Some(user) => match login_attempts_exceeded(settings, login_form.login_email){
|
||||
Ok(result) => {
|
||||
if result{
|
||||
return Err(LoginError::MaxLoginAttemptsExceeded)
|
||||
}else{
|
||||
user
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(LoginError::DatabaseError)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
match login_attempts_exceeded(settings, login_form.login_email){
|
||||
Ok(result) => {
|
||||
if result{
|
||||
return Err(LoginError::MaxLoginAttemptsExceeded)
|
||||
}else{
|
||||
return Err(LoginError::UserNotFound)
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(LoginError::DatabaseError)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let password_hash = match user.password.clone(){
|
||||
None => {return Err(LoginError::NoPassword);}
|
||||
Some(pw) => pw
|
||||
};
|
||||
|
||||
pub fn check_login(login_form: LoginForm, settings: &State<Settings>) -> Option<User> {
|
||||
let user: User = get_user_by_email(login_form.login_email, &settings)?;
|
||||
let password_hash = user.password.clone()?;
|
||||
trace!("Comparing password hash for {}", user.id);
|
||||
if argon2::verify_encoded(&password_hash, login_form.login_password.as_ref()).unwrap() {
|
||||
Some(user)
|
||||
Ok(user)
|
||||
} else {
|
||||
None
|
||||
Err(LoginError::PasswordIncorrect)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod login;
|
||||
pub mod render;
|
||||
pub mod password_reset;
|
|
@ -0,0 +1,42 @@
|
|||
use crate::helper::settings::Settings;
|
||||
use rocket::State;
|
||||
use crate::database::controller::users::get_user_by_email;
|
||||
use crate::database::controller::password_resets::add_token;
|
||||
use crate::helper::mail_templates::MailTemplates;
|
||||
use crate::helper::mail_queue::queue::{Mail, MailQueue};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PasswortResetMail{
|
||||
frontpage: String,
|
||||
email: String,
|
||||
reset_url: String,
|
||||
}
|
||||
|
||||
/// Checks if email belongs to user, if so resets password
|
||||
pub fn request_password_reset(settings: &State<Settings>, mt: &State<MailTemplates>, mq: State<Arc<MailQueue>>, email: String) -> Result<(), ()>{
|
||||
let user = match get_user_by_email(email.clone(), settings){ //Check if email belongs to user, if not return
|
||||
Some(user) => user,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let token = match add_token(settings, user.id){
|
||||
Ok(token) => token,
|
||||
Err(e) => return Err(()),
|
||||
};
|
||||
let pwrm = PasswortResetMail{
|
||||
frontpage: settings.application.url.clone(),
|
||||
email: email.clone(),
|
||||
reset_url: format!("{}password_reset?token={}", settings.application.url.clone(), token)
|
||||
};
|
||||
let body = match mt.registry.render("password-reset-de", &pwrm){
|
||||
Ok(body) => body,
|
||||
Err(e) => return Err(()),
|
||||
};
|
||||
let mail = Mail::new(settings.mail.from.clone(), vec![email], format!("[{}] - Passwort Zurücksetzen", settings.application.name.clone()), vec![], vec![], Some(settings.mail.reply_to.clone()), body, None); //TODO: Add deliver_until
|
||||
|
||||
match mq.add_mail(mail){
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(())
|
||||
}
|
||||
}
|
|
@ -4,8 +4,8 @@ use crate::modules::welcome::model::welcome_module::WelcomeModule;
|
|||
|
||||
pub fn get_context(alert: Option<Alert>) -> WelcomeModule {
|
||||
let header = Header {
|
||||
html_language: "en".to_string(),
|
||||
site_title: "ERRMS".to_string(),
|
||||
html_language: "de".to_string(),
|
||||
site_title: "einsatz.online".to_string(),
|
||||
stylesheets: vec![Stylesheet {
|
||||
path: "/css/errms.css".to_string(),
|
||||
}],
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
use std::error;
|
||||
use std::error::Error as _;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoginError {
|
||||
UserNotFound,
|
||||
MaxLoginAttemptsExceeded,
|
||||
PasswordIncorrect,
|
||||
DatabaseError,
|
||||
NoPassword
|
||||
}
|
||||
|
||||
impl fmt::Display for LoginError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
LoginError::UserNotFound =>
|
||||
write!(f, "User not found"),
|
||||
LoginError::MaxLoginAttemptsExceeded =>
|
||||
write!(f, "Maximum login attempts exceeded"),
|
||||
LoginError::PasswordIncorrect =>
|
||||
write!(f, "Password incorrect"),
|
||||
LoginError::DatabaseError => write!(f, "Database Error occured!"),
|
||||
LoginError::NoPassword => write!(f, "User missing password! No login possible."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for LoginError {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod login_form;
|
||||
pub mod welcome_module;
|
||||
pub mod login_error_type;
|
|
@ -2,6 +2,8 @@ use crate::helper::sitebuilder::model::alerts::{Alert, AlertClass};
|
|||
use crate::modules::welcome::controller::render::get_context;
|
||||
use rocket::http::Status;
|
||||
use rocket_contrib::templates::Template;
|
||||
use rocket::State;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[get("/?<error>")]
|
||||
pub fn welcome_get(error: Option<String>) -> Result<Template, Status> {
|
||||
|
@ -15,6 +17,11 @@ pub fn welcome_get(error: Option<String>) -> Result<Template, Status> {
|
|||
AlertClass::Success,
|
||||
"Sie wurden erfolgreich abgemeldet!".to_string(),
|
||||
)),
|
||||
"password_reset_success" => Some(Alert::new(
|
||||
AlertClass::Success,
|
||||
"Ihr Passwort wurde erfolgreich zurückgesetzt!".to_string(),
|
||||
)),
|
||||
"password_reset_token_invalid" => Some(Alert::new(AlertClass::Danger, "Der Passwort zurücksetzen Token ist ungültig! Bitte einen neuen Anfordern!".to_string())),
|
||||
"notimplemented" => Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Fehler: Diese Funktion wurde noch nicht implementiert!".to_string(),
|
||||
|
|
|
@ -9,6 +9,13 @@ use rocket::request::Form;
|
|||
use rocket::response::Redirect;
|
||||
use rocket::State;
|
||||
use rocket_contrib::templates::Template;
|
||||
use crate::modules::welcome::model::login_error_type::LoginError;
|
||||
use crate::modules::welcome::controller::password_reset::request_password_reset;
|
||||
use crate::helper::mail_templates::MailTemplates;
|
||||
use crate::helper::mail_queue::queue::MailQueue;
|
||||
use std::sync::Arc;
|
||||
use crate::helper::sitebuilder::model::general::{Footer, Header, Stylesheet};
|
||||
use crate::database::controller::password_resets::{validate_token, change_password_with_token, validate_password};
|
||||
|
||||
#[post("/", data = "<login_form>")]
|
||||
pub fn welcome_post(
|
||||
|
@ -19,22 +26,149 @@ pub fn welcome_post(
|
|||
) -> Result<Redirect, Template> {
|
||||
let user = check_login(login_form.into_inner(), &settings);
|
||||
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
match user.clone() {
|
||||
Some(user) => trace!("LOGIN: {}", user.id),
|
||||
None => trace!("LOGIN FAILED"),
|
||||
}
|
||||
}
|
||||
let alert: Option<Alert> = match user {
|
||||
Some(user) => {
|
||||
Ok(user) => {
|
||||
add_session_cookie(user, &settings, cookie_storage, cookies);
|
||||
return Ok(Redirect::to("/loginselect"));
|
||||
}
|
||||
None => Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Login failed. Incorrect email or password!".to_string(),
|
||||
)),
|
||||
Err(e) => match e{
|
||||
LoginError::UserNotFound => {
|
||||
Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Anmelden fehlgeschlagen! Email oder Passwort falsch!".to_string()))
|
||||
},
|
||||
LoginError::MaxLoginAttemptsExceeded => {
|
||||
Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
format!("Es wurden zu viele Anmeldeversuche durchgeführt. Der Account wurde für {} Minuten gesperrt! Bitte nutzen Sie ggf. die Passwort vergessen Funktion!", settings.application.login_lock_duration/60)))
|
||||
},
|
||||
LoginError::PasswordIncorrect => {
|
||||
Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Anmelden fehlgeschlagen! Email oder Passwort falsch!".to_string()))
|
||||
},
|
||||
LoginError::DatabaseError => {
|
||||
Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Es konnte keine Datenbankverbindung hergestellt werden!".to_string()))
|
||||
}
|
||||
LoginError::NoPassword => {
|
||||
Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Anmelden fehlgeschlagen! Email oder Passwort falsch!".to_string()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Err(Template::render("module_welcome", &get_context(alert)))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromForm)]
|
||||
pub struct PasswordResetForm{
|
||||
pub(crate) email: String,
|
||||
}
|
||||
|
||||
#[post("/password_reset", data = "<password_reset_form>")]
|
||||
pub fn password_reset_post(
|
||||
password_reset_form: Form<PasswordResetForm>,
|
||||
settings: State<Settings>,
|
||||
mt: State<MailTemplates>,
|
||||
mq: State<Arc<MailQueue>>,
|
||||
) -> Template {
|
||||
let mut alert : Option<Alert> = None;
|
||||
|
||||
match request_password_reset(&settings, &mt, mq, password_reset_form.email.clone()){
|
||||
Ok(_) => {
|
||||
alert = Some(Alert::new(
|
||||
AlertClass::Success,
|
||||
"Falls ein Benutzer mit der angegebenen Email existiert wurde ein Link zum Zurücksetzen des Passwortes per Email verschickt!".to_string()))
|
||||
}
|
||||
Err(_) => {
|
||||
alert = Some(Alert::new(
|
||||
AlertClass::Danger,
|
||||
"Das Passwort konnte nicht zurückgesetzt werden. Bitte versuchen Sie es später erneut.".to_string()))
|
||||
|
||||
}
|
||||
}
|
||||
Template::render("module_welcome", &get_context(alert))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PasswordResetPage{
|
||||
header: Header,
|
||||
footer: Footer,
|
||||
logo_path: String,
|
||||
alert: Option<Alert>,
|
||||
}
|
||||
|
||||
#[get("/password_reset?<token>")]
|
||||
pub fn password_change_get(
|
||||
settings: State<Settings>,
|
||||
token: String,
|
||||
) -> Result<Template, Redirect> {
|
||||
match validate_token(&settings, token){
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
return Err(Redirect::to("/?error=password_reset_token_invalid"))
|
||||
}
|
||||
}
|
||||
let header = Header {
|
||||
html_language: "de".to_string(),
|
||||
site_title: "einsatz.online".to_string(),
|
||||
stylesheets: vec![Stylesheet {
|
||||
path: "/css/errms.css".to_string(),
|
||||
}],
|
||||
};
|
||||
let footer = Footer { scripts: vec![] };
|
||||
let render = PasswordResetPage{
|
||||
header,
|
||||
footer,
|
||||
logo_path: "/img/logo.jpg".to_string(),
|
||||
alert: None
|
||||
};
|
||||
|
||||
Ok(Template::render("password_reset", &render))
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromForm)]
|
||||
pub struct PasswordChangeForm{
|
||||
pub(crate) password: String,
|
||||
}
|
||||
|
||||
#[post("/password_reset?<token>", data = "<password_change_form>")]
|
||||
pub fn password_change_post(
|
||||
settings: State<Settings>,
|
||||
password_change_form: Form<PasswordChangeForm>,
|
||||
token: String,
|
||||
) -> Result<Redirect, Template> {
|
||||
let mut alert = None;
|
||||
let password_change_form = password_change_form.into_inner();
|
||||
if !validate_password(password_change_form.password.clone()){
|
||||
alert = Some(Alert::new(AlertClass::Danger, "Das Passwort muss mindestens 10 Zeichen lang sein!".to_string()));
|
||||
}else {
|
||||
match change_password_with_token(&settings, token, password_change_form.password) {
|
||||
Ok(_) => {
|
||||
return Ok(Redirect::to("/?error=password_reset_success"))
|
||||
},
|
||||
Err(_) => {
|
||||
alert = Some(Alert::new(AlertClass::Danger, "Es ist ein interner Fehler aufgetreten, das Passwort konnte nicht geändert werden.".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let header = Header {
|
||||
html_language: "de".to_string(),
|
||||
site_title: "einsatz.online".to_string(),
|
||||
stylesheets: vec![Stylesheet {
|
||||
path: "/css/errms.css".to_string(),
|
||||
}],
|
||||
};
|
||||
let footer = Footer { scripts: vec![] };
|
||||
let render = PasswordResetPage{
|
||||
header,
|
||||
footer,
|
||||
logo_path: "/img/logo.jpg".to_string(),
|
||||
alert,
|
||||
};
|
||||
|
||||
Err(Template::render("password_reset", &render))
|
||||
}
|
|
@ -57,6 +57,26 @@ table! {
|
|||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
cost_centres (short_id) {
|
||||
short_id -> Int4,
|
||||
description -> Nullable<Text>,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
cost_centres_members (member_entity_id, cost_centre_shortid) {
|
||||
member_entity_id -> Uuid,
|
||||
cost_centre_shortid -> Int4,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
@ -108,6 +128,17 @@ table! {
|
|||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
login_attempts (id) {
|
||||
id -> Uuid,
|
||||
email -> Text,
|
||||
timestamp -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
@ -142,6 +173,17 @@ table! {
|
|||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
||||
password_resets (token) {
|
||||
token -> Text,
|
||||
user_id -> Uuid,
|
||||
issued -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
use diesel::sql_types::*;
|
||||
use diesel_geometry::sql_types::*;
|
||||
|
@ -244,7 +286,7 @@ table! {
|
|||
users (id) {
|
||||
id -> Uuid,
|
||||
password -> Nullable<Text>,
|
||||
email -> Nullable<Text>,
|
||||
email -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,6 +311,8 @@ joinable!(addresses_entities -> entities (entitiy_id));
|
|||
joinable!(buildings -> entities (entity_id));
|
||||
joinable!(communication_targets -> communication_types (com_type));
|
||||
joinable!(communication_targets -> entities (entity_id));
|
||||
joinable!(cost_centres_members -> cost_centres (cost_centre_shortid));
|
||||
joinable!(cost_centres_members -> members (member_entity_id));
|
||||
joinable!(groups -> entities (entity_id));
|
||||
joinable!(groups_entities -> entities (entity_id));
|
||||
joinable!(groups_entities -> groups (group_id));
|
||||
|
@ -278,6 +322,7 @@ joinable!(members -> entities (entity_id));
|
|||
joinable!(members -> users (users_id));
|
||||
joinable!(members_roles -> entities (member_id));
|
||||
joinable!(members_roles -> roles (role_id));
|
||||
joinable!(password_resets -> users (user_id));
|
||||
joinable!(qualifications -> qualification_categories (category));
|
||||
joinable!(qualifications_members -> members (member_id));
|
||||
joinable!(qualifications_members -> qualifications (qualification_id));
|
||||
|
@ -297,13 +342,17 @@ allow_tables_to_appear_in_same_query!(
|
|||
buildings,
|
||||
communication_targets,
|
||||
communication_types,
|
||||
cost_centres,
|
||||
cost_centres_members,
|
||||
entities,
|
||||
groups,
|
||||
groups_entities,
|
||||
license_categories,
|
||||
licenses_members,
|
||||
login_attempts,
|
||||
members,
|
||||
members_roles,
|
||||
password_resets,
|
||||
permissions,
|
||||
qualification_categories,
|
||||
qualifications,
|
||||
|
|
Loading…
Reference in New Issue