diff --git a/.env.template b/.env.template index 51bb9e0f..421cb0c2 100644 --- a/.env.template +++ b/.env.template @@ -10,6 +10,10 @@ # ICON_CACHE_FOLDER=data/icon_cache # ATTACHMENTS_FOLDER=data/attachments +## Templates data folder, by default uses embedded templates +## Check source code to see the format +# TEMPLATES_FOLDER=/path/to/templates + ## Cache time-to-live for successfully obtained icons, in seconds (0 is "forever") # ICON_CACHE_TTL=2592000 ## Cache time-to-live for icons which weren't available, in seconds (0 is "forever") diff --git a/Cargo.lock b/Cargo.lock index b91c591b..22ae11d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,11 @@ name = "antidote" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "arrayref" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arrayvec" version = "0.4.10" @@ -119,6 +124,7 @@ dependencies = [ "diesel_migrations 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "fern 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonwebtoken 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre 0.9.0 (git+https://github.com/lettre/lettre?rev=c988b1760ad81)", @@ -145,6 +151,15 @@ dependencies = [ "yubico 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "block-buffer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "block-buffer" version = "0.7.0" @@ -200,6 +215,11 @@ name = "byte-tools" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byte-tools" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byte-tools" version = "0.3.0" @@ -445,6 +465,14 @@ dependencies = [ "generic-array 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "digest" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "digest" version = "0.8.0" @@ -689,6 +717,14 @@ dependencies = [ "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "generic-array" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "generic-array" version = "0.12.0" @@ -719,6 +755,22 @@ dependencies = [ "tokio-io 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "handlebars" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hmac" version = "0.1.1" @@ -1028,6 +1080,11 @@ dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "maplit" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "matches" version = "0.1.8" @@ -1422,6 +1479,45 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pest" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_generator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pest_meta" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.24" @@ -1965,6 +2061,17 @@ dependencies = [ "generic-array 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sha-1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "sha-1" version = "0.8.1" @@ -2329,6 +2436,11 @@ dependencies = [ "webpki 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ucd-trie" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ucd-util" version = "0.1.3" @@ -2576,6 +2688,7 @@ dependencies = [ "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" +"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum ascii 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "97be891acc47ca214468e09425d02cef3af2c94d0d82081cd02061f996802f14" "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" @@ -2586,6 +2699,7 @@ dependencies = [ "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" "checksum block-buffer 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49665c62e0e700857531fa5d3763e91b539ff1abeebd56808d378b495870d60d" "checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" "checksum block-modes 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d90b1e3da95151ddacfa78aa46d1a9173e28a8a11625102e088562d3407bf3b9" @@ -2593,6 +2707,7 @@ dependencies = [ "checksum buf_redux 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f25c67abbf523ff8457771622fb731ac4a2391439de33bc60febcdee1749c9" "checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" "checksum byte-tools 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0919189ba800c7ffe8778278116b7e0de3905ab81c72abb69c85cbfef7991279" +"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182" "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" @@ -2622,6 +2737,7 @@ dependencies = [ "checksum diesel_derives 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03bcaf77491f53e400d5ee3bdd57142ea4e1c47fe9217b3361ff9a76ca0e3d37" "checksum diesel_migrations 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b42c35d1ce9e8d57a3e7001b4127f2bc1b073a89708bb7019f5be27c991c28" "checksum digest 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a68d759d7a66a4f63d5bd2a2b14ad7e8cf93fe8c9be227031cd4e72ab0e9ee8" +"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" "checksum digest-buffer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4eb92364e9f6d3da159257250532d448b218406d2acb149f724e8f48e9f5cb9a" "checksum dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d0a1279c96732bc6800ce6337b6a614697b0e74ae058dc03c62ebeb78b4d86" @@ -2653,8 +2769,10 @@ dependencies = [ "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum generic-array 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" "checksum generic-array 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe043cf9b85297937897087de81f590361686e1ac2d4d471b45435de5dfb6a6" +"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum groupable 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32619942b8be646939eaf3db0602b39f5229b74575b67efc897811ded1db4e57" "checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0" +"checksum handlebars 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d82e5750d8027a97b9640e3fefa66bbaf852a35228e1c90790efd13c4b09c166" "checksum hmac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb5aa9647ba4711e9d6968dc1c810cd23989ed435443ca962e1bf6d8b8b83ff" "checksum hmac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" @@ -2685,6 +2803,7 @@ dependencies = [ "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" @@ -2725,6 +2844,10 @@ dependencies = [ "checksum pear 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c26d2b92e47063ffce70d3e3b1bd097af121a9e0db07ca38a6cc1cf0cc85ff25" "checksum pear_codegen 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "336db4a192cc7f54efeb0c4e11a9245394824cc3bcbd37ba3ff51240c35d7a6e" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54f0c72a98d8ab3c99560bfd16df8059cc10e1f9a8e83e6e3b97718dd766e9c3" +"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +"checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" +"checksum pest_meta 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5a3492a4ed208ffc247adcdcc7ba2a95be3104f58877d0d02f0df39bf3efb5e" "checksum phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" @@ -2784,6 +2907,7 @@ dependencies = [ "checksum serde_json 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb1277d4d0563e4593e0b8b5d23d744d277b55d2bc0bf1c38d0d8a6589d38aa" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum sha-1 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8347606816471548cd60f0abd5ef0d513a81f5202dbdab9c09f17a15b5248484" +"checksum sha-1 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9d1f3b5de8a167ab06834a7c883bd197f2191e1dda1a22d9ccfeedbf9aded" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" "checksum sha2 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "84920f9ac881e94e33ec89e1b3dcd36040523a308a92548e01217ce35d8cf6a8" @@ -2824,6 +2948,7 @@ dependencies = [ "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum u2f 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3c74b68bd3f67273d9d3161dbab8672a69df832a7fbcd1e2cd9b714063648ab5" +"checksum ucd-trie 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "71a9c5b1fe77426cf144cc30e49e955270f5086e31a6441dfa8b32efc09b9d77" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" diff --git a/Cargo.toml b/Cargo.toml index 71264870..18096d2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,9 @@ lettre = "0.9.0" lettre_email = "0.9.0" native-tls = "0.2.2" +# Template library +handlebars = "1.1.0" + # Number encoding library byteorder = "1.2.7" diff --git a/src/error.rs b/src/error.rs index 6e5c627e..98a7d0e4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -37,6 +37,7 @@ use jsonwebtoken::errors::Error as JwtError; use serde_json::{Error as SerError, Value}; use std::io::Error as IOError; use u2f::u2ferror::U2fError as U2fErr; +use handlebars::RenderError as HbError; // Error struct // Contains a String error message, meant for the user and an enum variant, with an error of different types. @@ -53,6 +54,7 @@ make_error! { SerdeError(SerError): _has_source, _api_error, JWTError(JwtError): _has_source, _api_error, IoErrror(IOError): _has_source, _api_error, + TemplErrror(HbError): _has_source, _api_error, //WsError(ws::Error): _has_source, _api_error, } diff --git a/src/mail.rs b/src/mail.rs index eddbec1c..e41ec858 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -4,11 +4,11 @@ use lettre::{ClientSecurity, ClientTlsParameters, SmtpClient, SmtpTransport, Tra use lettre_email::EmailBuilder; use native_tls::{Protocol, TlsConnector}; +use crate::api::EmptyResult; +use crate::auth::{encode_jwt, generate_invite_claims}; +use crate::error::Error; use crate::MailConfig; use crate::CONFIG; -use crate::auth::{generate_invite_claims, encode_jwt}; -use crate::api::EmptyResult; -use crate::error::Error; fn mailer(config: &MailConfig) -> SmtpTransport { let client_security = if config.smtp_ssl { @@ -35,24 +35,32 @@ fn mailer(config: &MailConfig) -> SmtpTransport { .transport() } -pub fn send_password_hint(address: &str, hint: Option, config: &MailConfig) -> EmptyResult { - let (subject, body) = if let Some(hint) = hint { - ( - "Your master password hint", - format!( - "You (or someone) recently requested your master password hint.\n\n\ - Your hint is: \"{}\"\n\n\ - If you did not request your master password hint you can safely ignore this email.\n", - hint - ), - ) - } else { - ( - "Sorry, you have no password hint...", - "Sorry, you have not specified any password hint...\n".into(), - ) +fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(String, String), Error> { + let text = CONFIG.templates.render(template_name, &data)?; + let mut text_split = text.split(""); + + let subject = match text_split.next() { + Some(s) => s.trim().to_string(), + None => err!("Template doesn't contain subject"), }; + let body = match text_split.next() { + Some(s) => s.trim().to_string(), + None => err!("Template doesn't contain body"), + }; + + Ok((subject, body)) +} + +pub fn send_password_hint(address: &str, hint: Option, config: &MailConfig) -> EmptyResult { + let template_name = if hint.is_some() { + "email_pw_hint_some" + } else { + "email_pw_hint_none" + }; + + let (subject, body) = get_text(template_name, json!({ "hint": hint }))?; + send_email(&address, &subject, &body, &config) } @@ -66,74 +74,66 @@ pub fn send_invite( config: &MailConfig, ) -> EmptyResult { let claims = generate_invite_claims( - uuid.to_string(), - String::from(address), - org_id.clone(), - org_user_id.clone(), - invited_by_email.clone(), - ); + uuid.to_string(), + String::from(address), + org_id.clone(), + org_user_id.clone(), + invited_by_email.clone(), + ); let invite_token = encode_jwt(&claims); - let (subject, body) = { - (format!("Join {}", &org_name), - format!( - " -

You have been invited to join the {} organization.

- - Click here to join

-

If you do not wish to join this organization, you can safely ignore this email.

- ", - org_name, CONFIG.domain, org_id.unwrap_or("_".to_string()), org_user_id.unwrap_or("_".to_string()), address, org_name, invite_token - )) - }; + + let (subject, body) = get_text( + "email_send_org_invite", + json!({ + "url": CONFIG.domain, + "org_id": org_id.unwrap_or("_".to_string()), + "org_user_id": org_user_id.unwrap_or("_".to_string()), + "email": address, + "org_name": org_name, + "token": invite_token, + }), + )?; send_email(&address, &subject, &body, &config) } -pub fn send_invite_accepted( - new_user_email: &str, - address: &str, - org_name: &str, - config: &MailConfig, -) -> EmptyResult { - let (subject, body) = { - ("Invitation accepted", - format!( - " -

Your invitation for {} to join {} was accepted. Please log in to the bitwarden_rs server and confirm them from the organization management page.

- ", new_user_email, org_name, CONFIG.domain)) - }; +pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str, config: &MailConfig) -> EmptyResult { + let (subject, body) = get_text( + "email_invite_accepted", + json!({ + "url": CONFIG.domain, + "email": new_user_email, + "org_name": org_name, + }), + )?; send_email(&address, &subject, &body, &config) } -pub fn send_invite_confirmed( - address: &str, - org_name: &str, - config: &MailConfig, -) -> EmptyResult { - let (subject, body) = { - (format!("Invitation to {} confirmed", org_name), - format!( - " -

Your invitation to join {} was confirmed. It will now appear under the Organizations the next time you log in to the web vault.

- ", org_name, CONFIG.domain)) - }; +pub fn send_invite_confirmed(address: &str, org_name: &str, config: &MailConfig) -> EmptyResult { + let (subject, body) = get_text( + "email_invite_confirmed", + json!({ + "url": CONFIG.domain, + "org_name": org_name, + }), + )?; send_email(&address, &subject, &body, &config) } fn send_email(address: &str, subject: &str, body: &str, config: &MailConfig) -> EmptyResult { let email = EmailBuilder::new() - .to(address) - .from((config.smtp_from.clone(), "Bitwarden-rs")) - .subject(subject) - .header(("Content-Type", "text/html")) - .body(body) - .build() - .map_err(|e| Error::new("Error building email", e.to_string()))?; + .to(address) + .from((config.smtp_from.clone(), "Bitwarden-rs")) + .subject(subject) + .header(("Content-Type", "text/html")) + .body(body) + .build() + .map_err(|e| Error::new("Error building email", e.to_string()))?; mailer(config) .send(email.into()) .map_err(|e| Error::new("Error sending email", e.to_string())) .and(Ok(())) -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 6373fa7f..8c312d0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,9 @@ extern crate derive_more; #[macro_use] extern crate num_derive; +use handlebars::Handlebars; use rocket::{fairing::AdHoc, Rocket}; + use std::{ path::Path, process::{exit, Command}, @@ -323,6 +325,26 @@ pub struct Config { yubico_server: Option, mail: Option, + templates: Handlebars, +} + +fn load_templates(path: Option) -> Handlebars { + let mut hb = Handlebars::new(); + + // First register default templates here (use include_str?) + hb.register_template_string("tpl_1", "Good afternoon, {{name}}").unwrap(); + + // TODO: Development-only. Remove this before release + hb.register_templates_directory(".hbs", "src/static/templates").unwrap(); + + // And then load user templates to overwrite the defaults + if let Some(path) = path { + // Use .hbs extension for the files + // Templates get registered with their relative name + hb.register_templates_directory(".hbs", path).unwrap(); + } + + hb } impl Config { @@ -381,6 +403,7 @@ impl Config { yubico_server: get_env("YUBICO_SERVER"), mail: MailConfig::load(), + templates: load_templates(get_env("TEMPLATES_FOLDER")), } } } diff --git a/src/static/templates/email_invite_accepted.hbs b/src/static/templates/email_invite_accepted.hbs new file mode 100644 index 00000000..c88372c6 --- /dev/null +++ b/src/static/templates/email_invite_accepted.hbs @@ -0,0 +1,8 @@ +Invitation accepted + + +

+ Your invitation for {{email}} to join {{org_name}} was accepted. + Please log in to the bitwarden_rs server and confirm them from the organization management page. +

+ diff --git a/src/static/templates/email_invite_confirmed.hbs b/src/static/templates/email_invite_confirmed.hbs new file mode 100644 index 00000000..7fc7db78 --- /dev/null +++ b/src/static/templates/email_invite_confirmed.hbs @@ -0,0 +1,8 @@ +Invitation to {{org_name}} confirmed + + +

+ Your invitation to join {{org_name}} was confirmed. + It will now appear under the Organizations the next time you log in to the web vault. +

+ \ No newline at end of file diff --git a/src/static/templates/email_pw_hint_none.hbs b/src/static/templates/email_pw_hint_none.hbs new file mode 100644 index 00000000..c73e3b3a --- /dev/null +++ b/src/static/templates/email_pw_hint_none.hbs @@ -0,0 +1,3 @@ +Sorry, you have no password hint... + +Sorry, you have not specified any password hint... diff --git a/src/static/templates/email_pw_hint_some.hbs b/src/static/templates/email_pw_hint_some.hbs new file mode 100644 index 00000000..a2917a3d --- /dev/null +++ b/src/static/templates/email_pw_hint_some.hbs @@ -0,0 +1,7 @@ +Your master password hint + +You (or someone) recently requested your master password hint. + +Your hint is: "{{hint}}" + +If you did not request your master password hint you can safely ignore this email. diff --git a/src/static/templates/email_send_org_invite.hbs b/src/static/templates/email_send_org_invite.hbs new file mode 100644 index 00000000..be4a443d --- /dev/null +++ b/src/static/templates/email_send_org_invite.hbs @@ -0,0 +1,12 @@ +Join {{org_name}} + + +

+You have been invited to join the {{org_name}} organization. +
+
+ +Click here to join +

+

If you do not wish to join this organization, you can safely ignore this email.

+