Browse Source

Many updates

Split codebase into modules
Added custom all in one error type
Added password to user model
Added password hashing using bcrypt
Added model validation for username using regex
devel
Rohan Sircar 2 years ago
parent
commit
cb26393fcd
  1. 350
      Cargo.lock
  2. 91
      Cargo.toml
  3. 1
      README.md
  4. 6
      migrations/2020-05-02-115427_create_users/up.sql
  5. 69
      src/actions.rs
  6. 83
      src/actions/users.rs
  7. 2
      src/errors.rs
  8. 88
      src/errors/domain_error.rs
  9. 188
      src/main.rs
  10. 3
      src/middlewares.rs
  11. 276
      src/middlewares/csrf.rs
  12. 28
      src/models.rs
  13. 13
      src/models/errors.rs
  14. 0
      src/models/roles.rs
  15. 38
      src/models/users.rs
  16. 2
      src/routes.rs
  17. 45
      src/routes/auth.rs
  18. 92
      src/routes/users.rs
  19. 2
      src/schema.rs
  20. 4
      src/utils.rs
  21. 14
      src/utils/auth.rs
  22. 5
      src/utils/regexs.rs
  23. BIN
      test.db

350
Cargo.lock

@ -45,22 +45,32 @@ dependencies = [
"actix-service",
"actix-web",
"actix-web-httpauth",
"bcrypt",
"bytes",
"chrono",
"comp",
"custom_error",
"diesel",
"dotenv",
"env_logger 0.7.1",
"futures",
"json",
"jsonwebtoken",
"lazy-regex",
"lazy_static",
"listenfd",
"log",
"nanoid",
"r2d2",
"rand 0.7.3",
"regex",
"rusqlite",
"serde",
"serde_json",
"timeago",
"uuid 0.8.1",
"validator",
"validator_derive",
"yarte",
"yarte_helpers",
]
@ -501,6 +511,25 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "base64"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42"
[[package]]
name = "bcrypt"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f02d7d008a57bcb2251ba115b803934e02315edbde9a861c88713493e381b63"
dependencies = [
"base64 0.12.1",
"blowfish",
"byteorder",
"lazy_static",
"rand 0.7.3",
]
[[package]]
name = "bincode"
version = "1.2.1"
@ -551,6 +580,26 @@ dependencies = [
"constant_time_eq",
]
[[package]]
name = "block-cipher-trait"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
dependencies = [
"generic-array",
]
[[package]]
name = "blowfish"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
dependencies = [
"block-cipher-trait",
"byteorder",
"opaque-debug",
]
[[package]]
name = "brotli-sys"
version = "0.3.2"
@ -627,6 +676,7 @@ checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
dependencies = [
"num-integer",
"num-traits",
"serde",
"time",
]
@ -677,6 +727,12 @@ dependencies = [
"bitflags",
]
[[package]]
name = "comp"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7b6cae29f71a26f0dae0e291da438d6fced0e22e78aa1484cbbc085b5170949"
[[package]]
name = "console"
version = "0.10.3"
@ -735,6 +791,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "custom_error"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a0fc65739ae998afc8d68e64bdac2efd1bc4ffa1a0703d171ef2defae3792f"
[[package]]
name = "darling"
version = "0.10.2"
@ -813,6 +875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d7ca63eb2efea87a7f56a283acc49e2ce4b2bd54adf7465dc1d81fef13d8fc"
dependencies = [
"byteorder",
"chrono",
"diesel_derives",
"libsqlite3-sys",
"r2d2",
@ -1029,6 +1092,18 @@ dependencies = [
"synstructure",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "flate2"
version = "1.0.14"
@ -1179,6 +1254,15 @@ dependencies = [
"byteorder",
]
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
dependencies = [
"typenum",
]
[[package]]
name = "getrandom"
version = "0.1.14"
@ -1198,9 +1282,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "h2"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42"
checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff"
dependencies = [
"bytes",
"fnv",
@ -1287,6 +1371,12 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "if_chain"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d"
[[package]]
name = "indexmap"
version = "1.3.2"
@ -1317,6 +1407,16 @@ dependencies = [
"winreg",
]
[[package]]
name = "isolang"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "265ef164908329e47e753c769b14cbb27434abf0c41984dca201484022f09ce5"
dependencies = [
"phf 0.7.24",
"phf_codegen 0.7.24",
]
[[package]]
name = "itoa"
version = "0.4.5"
@ -1368,6 +1468,15 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
[[package]]
name = "lazy-regex"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d91276c62198fd9dd1be0d8a4ed647d0a51d5d6a0679dc324dd0b499d024ff"
dependencies = [
"lazy_static",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -1402,6 +1511,7 @@ version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d90181c2904c287e5390186be820e5ef311a3c62edebb7d6ca3d6a48ce041d"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
@ -1417,9 +1527,9 @@ dependencies = [
[[package]]
name = "linked-hash-map"
version = "0.5.2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]]
name = "listenfd"
@ -1472,8 +1582,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab"
dependencies = [
"log",
"phf",
"phf_codegen",
"phf 0.8.0",
"phf_codegen 0.8.0",
"serde",
"serde_derive",
"serde_json",
@ -1672,6 +1782,12 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "parking_lot"
version = "0.10.2"
@ -1719,13 +1835,32 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "phf"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18"
dependencies = [
"phf_shared 0.7.24",
]
[[package]]
name = "phf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_shared",
"phf_shared 0.8.0",
]
[[package]]
name = "phf_codegen"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e"
dependencies = [
"phf_generator 0.7.24",
"phf_shared 0.7.24",
]
[[package]]
@ -1734,8 +1869,18 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
dependencies = [
"phf_generator",
"phf_shared",
"phf_generator 0.8.0",
"phf_shared 0.8.0",
]
[[package]]
name = "phf_generator"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
dependencies = [
"phf_shared 0.7.24",
"rand 0.6.5",
]
[[package]]
@ -1744,33 +1889,42 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
dependencies = [
"phf_shared",
"phf_shared 0.8.0",
"rand 0.7.3",
]
[[package]]
name = "phf_shared"
version = "0.7.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
dependencies = [
"siphasher 0.2.3",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [
"siphasher",
"siphasher 0.3.3",
]
[[package]]
name = "pin-project"
version = "0.4.10"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e3dcd42688c05a66f841d22c5d8390d9a5d4c9aaf57b9285eae4900a080063"
checksum = "82c3bfbfb5bb42f99498c7234bbd768c220eb0cea6818259d0d18a1aa3d2595d"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.10"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4d7346ac577ff1296e06a418e7618e22655bae834d4970cb6e39d6da8119969"
checksum = "ccbf6449dcfb18562c015526b085b8df1aa3cdab180af8ec2ebd300a3bd28f63"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
@ -1779,9 +1933,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f"
[[package]]
name = "pin-utils"
@ -1872,15 +2026,6 @@ dependencies = [
"unicode-xid 0.2.0",
]
[[package]]
name = "proc-macro2-impersonated"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32df793782c132f437089d84b487d617a0baac886fa8519751dca07db9266e0"
dependencies = [
"unicode-xid 0.2.0",
]
[[package]]
name = "quick-error"
version = "1.2.3"
@ -1905,15 +2050,6 @@ dependencies = [
"proc-macro2 1.0.12",
]
[[package]]
name = "quote-impersonated"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e3e450e6f50c99055b77f01eb206d8accbb7a1d14c2c7be2b0a829286296dc9"
dependencies = [
"proc-macro2-impersonated",
]
[[package]]
name = "r2d2"
version = "0.8.8"
@ -2160,6 +2296,21 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "rusqlite"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a656821bb6317a84b257737b7934f79c0dbb7eb694710475908280ebad3e64"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"libsqlite3-sys",
"lru-cache",
"memchr",
"time",
]
[[package]]
name = "rust-argon2"
version = "0.7.0"
@ -2311,6 +2462,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "siphasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
[[package]]
name = "siphasher"
version = "0.3.3"
@ -2355,7 +2512,7 @@ checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"phf_shared",
"phf_shared 0.8.0",
"precomputed-hash",
"serde",
]
@ -2366,8 +2523,8 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
dependencies = [
"phf_generator",
"phf_shared",
"phf_generator 0.8.0",
"phf_shared 0.8.0",
"proc-macro2 1.0.12",
"quote 1.0.4",
]
@ -2386,26 +2543,15 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "syn"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213"
checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.4",
"unicode-xid 0.2.0",
]
[[package]]
name = "syn-impersonated"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11664eb1aded8a1be30656bedb3c2ee3d6b594842c90ec635cf7b855a5d8478"
dependencies = [
"proc-macro2-impersonated",
"quote-impersonated",
"unicode-xid 0.2.0",
]
[[package]]
name = "synstructure"
version = "0.12.3"
@ -2529,6 +2675,16 @@ dependencies = [
"winapi 0.3.8",
]
[[package]]
name = "timeago"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aff2f3f1ac92d664adfdea85496dceb8c044f66d62e7d953a059023385967cfc"
dependencies = [
"chrono",
"isolang",
]
[[package]]
name = "tokio"
version = "0.2.20"
@ -2536,7 +2692,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1"
dependencies = [
"bytes",
"fnv",
"futures-core",
"iovec",
"lazy_static",
@ -2639,6 +2794,12 @@ dependencies = [
"trust-dns-proto",
]
[[package]]
name = "typenum"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "unicase"
version = "2.6.0"
@ -2755,12 +2916,12 @@ dependencies = [
[[package]]
name = "v_eval"
version = "0.5.3"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a419cdda656c514fbb974d5634994278be348867645cb65bf21a9b0676b9c2"
checksum = "0dd8b599d797eb038d0dde9a3860aacb6bbba3bffa4ac64f807c8673820cc9d9"
dependencies = [
"regex",
"syn-impersonated",
"syn",
]
[[package]]
@ -2788,6 +2949,21 @@ dependencies = [
"url",
]
[[package]]
name = "validator_derive"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e668e9cd05c5009b833833aa1147e5727b5396ea401f22dd1167618eed4a10c9"
dependencies = [
"if_chain",
"lazy_static",
"proc-macro2 1.0.12",
"quote 1.0.4",
"regex",
"syn",
"validator",
]
[[package]]
name = "vcpkg"
version = "0.2.8"
@ -2796,9 +2972,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
[[package]]
name = "vec_map"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
@ -2988,9 +3164,9 @@ dependencies = [
[[package]]
name = "yarte"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884cff3b02a11d026c493282e598a5c990b17fa22b91fb73aad2bfeee47f4f01"
checksum = "819b7b4da6e6b0192ef2f8da43ed7d79716737a57246aff58ba75611f8083b94"
dependencies = [
"yarte_derive",
"yarte_helpers",
@ -2998,13 +3174,13 @@ dependencies = [
[[package]]
name = "yarte_codegen"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64e8ba00f9d77cd05b665b3515251544c5b1f4885e7e3f7ffc54ea27f52eabc"
checksum = "e3c178dcde178349d41d3b6c85353614fa43c2ddceba3e86073a864454149165"
dependencies = [
"proc-macro2-impersonated",
"quote-impersonated",
"syn-impersonated",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn",
"yarte_dom",
"yarte_helpers",
"yarte_hir",
@ -3013,14 +3189,14 @@ dependencies = [
[[package]]
name = "yarte_derive"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f11ebd841875bd9b906017844640aaac1571a4a188b3f7577efd6c02616da1"
checksum = "3a549fd6831f6354c39bb83bf78d7587e9b5ff18f7d390aefc4ba89435ba114f"
dependencies = [
"prettyprint",
"proc-macro2-impersonated",
"quote-impersonated",
"syn-impersonated",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn",
"tempfile",
"toolchain_find",
"yarte_codegen",
@ -3031,13 +3207,13 @@ dependencies = [
[[package]]
name = "yarte_dom"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43cc56a6351af550001f7f7e07be3581cde4908a8ddcbba34630168afc4ebabc"
checksum = "c08abf1e4cbf721cce79c77a7da1e42b37e7321966724b094891dc0c3d1d5cda"
dependencies = [
"markup5ever",
"quote-impersonated",
"syn-impersonated",
"quote 1.0.4",
"syn",
"yarte_helpers",
"yarte_hir",
"yarte_html",
@ -3045,9 +3221,9 @@ dependencies = [
[[package]]
name = "yarte_helpers"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771250e834a31e39a6e34e48e3fd9032542d211cb3f194f9a71b603fb841a793"
checksum = "e981464e47692f747fbc3f29cf271f65ac363cde5518ae77a50617d5490b9fce"
dependencies = [
"serde",
"toml",
@ -3056,14 +3232,14 @@ dependencies = [
[[package]]
name = "yarte_hir"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c86bad7da0c2ae56374390919fa1d7abaa067f6325a3e27865ae8598bfc24057"
checksum = "6105dad390184a8e4afdce19cdd0956ae1af057da1571d7add988527cbd59953"
dependencies = [
"derive_more",
"proc-macro2-impersonated",
"quote-impersonated",
"syn-impersonated",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn",
"v_eval",
"v_htmlescape",
"yarte_helpers",
@ -3072,9 +3248,9 @@ dependencies = [
[[package]]
name = "yarte_html"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261043d486d9ecdcb33c68d0323444db6ab6915f90e973c5cbd4331560ec0281"
checksum = "a0e8c01747147af821238b33c76c3045420a245fcfb3e2987becf2860b54598a"
dependencies = [
"log",
"mac",
@ -3087,15 +3263,15 @@ dependencies = [
[[package]]
name = "yarte_parser"
version = "0.8.3"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f855c622d29c8a8af4f4b7a3f81e83ac0be6e6074977a017c3945db05fba311"
checksum = "061ed8ddf990b97d89827c42346f1273d434e79a3618786d3f595f2f1befe418"
dependencies = [
"annotate-snippets",
"derive_more",
"proc-macro2-impersonated",
"quote-impersonated",
"syn-impersonated",
"proc-macro2 1.0.12",
"quote 1.0.4",
"syn",
"unicode-xid 0.2.0",
"yarte_helpers",
]

91
Cargo.toml

@ -1,43 +1,70 @@
[package]
name = "actix-demo"
version = "0.1.0"
authors = ["Rohan Sircar <rohansircar@tuta.io>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
name = 'actix-demo'
version = '0.1.0'
authors = ['Rohan Sircar <rohansircar@tuta.io>']
edition = '2018'
[dependencies]
actix-web = "2"
actix-rt = "1"
actix-service = "1.0.5"
actix-files = "0.2.1"
actix-web = '2'
actix-rt = '1'
actix-service = '1.0.5'
actix-files = '0.2.1'
bytes = '0.5.4'
futures = '0.3.4'
log = '0.4.8'
env_logger = '0.7'
serde_json = '1.0.52'
json = '0.12.4'
listenfd = '0.3.3'
dotenv = '0.15'
r2d2 = '0.8.8'
validator = '0.10.0'
validator_derive = '0.10'
jsonwebtoken = '7'
actix-identity = '0.2.1'
actix-web-httpauth = '0.4.1'
actix-http = '1.0.1'
rand = '0.7.3'
nanoid = '0.3.0'
bcrypt = '0.7'
timeago = '0.2.1'
comp = '0.2.1'
regex = '1.3.7'
lazy_static = '1.4.0'
lazy-regex = '0.1.2'
custom_error = '1.7.1'
bytes = "0.5.4"
futures = "0.3.4"
log = "0.4.8"
env_logger = "0.7"
[dependencies.serde]
version = '1.0.106'
features = ['derive']
serde = { version = "1.0.106", features = ["derive"] }
serde_json = "1.0.52"
json = "0.12.4"
[dependencies.yarte]
version = '0.9.0'
features = ['html-min']
yarte = { version = "0.8.3", features = ["html-min"] }
[dependencies.diesel]
version = '1.4.4'
features = [
'sqlite',
'r2d2',
'chrono',
]
listenfd = "0.3.3"
[dependencies.uuid]
version = '0.8'
features = [
'serde',
'v4',
]
diesel = { version = "1.4.4", features = ["sqlite", "r2d2"] }
dotenv = "0.15"
r2d2 = "0.8.8"
uuid = { version = "0.8", features = ["serde", "v4"] }
validator = "0.10.0"
jsonwebtoken = "7"
actix-identity = "0.2.1"
actix-web-httpauth = "0.4.1"
actix-http = "1.0.1"
rand = "0.7.3"
nanoid = "0.3.0"
[dependencies.rusqlite]
version = '0.21.0'
features = ['bundled']
[dependencies.chrono]
version = '0.4.11'
features = ['serde']
[build-dependencies.yarte_helpers]
version = "0.8"
version = '0.9.0'
default-features = false
features = ["config"]
features = ['config']

1
README.md

@ -0,0 +1 @@
Testing out the Rust framework Actix-Web to create a JSON API CRUD Web App.

6
migrations/2020-05-02-115427_create_users/up.sql

@ -1,5 +1,7 @@
-- Your SQL goes here
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL
id INTEGER PRIMARY KEY NOT NULL ,
name VARCHAR NOT NULL,
password VARCHAR NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)

69
src/actions.rs

@ -1,67 +1,2 @@
use diesel::prelude::*;
use crate::models;
pub fn find_user_by_uid(
uid: i32,
conn: &SqliteConnection,
) -> Result<Option<models::User>, diesel::result::Error> {
use crate::schema::users::dsl::*;
let maybe_user = users.find(uid).first::<models::User>(conn).optional();
// Ok(user)
maybe_user
}
pub fn find_user_by_name(
user_name: String,
conn: &SqliteConnection,
) -> Result<Option<models::User>, diesel::result::Error> {
use crate::schema::users::dsl::*;
let maybe_user = users
.filter(name.eq(user_name))
.first::<models::User>(conn)
.optional();
maybe_user
}
pub fn get_all(
conn: &SqliteConnection,
) -> Result<Option<Vec<models::User>>, diesel::result::Error> {
use crate::schema::users::dsl::*;
users.load::<models::User>(conn).optional()
}
/// Run query using Diesel to insert a new database row and return the result.
pub fn insert_new_user(
nu: &models::NewUser,
conn: &SqliteConnection,
) -> Result<models::User, diesel::result::Error> {
// It is common when using Diesel with Actix web to import schema-related
// modules inside a function's scope (rather than the normal module's scope)
// to prevent import collisions and namespace pollution.
use crate::schema::users::dsl::*;
// let new_user = models::User {
// id: Uuid::new_v4().to_string(),
// name: nu.name.to_string(),
// };
// let x = users.load::<models::User>(conn).optional();
// let target = users.find("4");
// let test_user = models::User {
// id: "5".to_owned(),
// name: "who".to_owned(),
// };
// let update_result = diesel::update(target).set(&test_user).execute(conn);
diesel::insert_into(users).values(nu).execute(conn)?;
let user = users
.filter(name.eq(nu.name.clone()))
.first::<models::User>(conn);
user
// Ok(nu.clone())
}
pub mod users;
pub use self::users::*;

83
src/actions/users.rs

@ -0,0 +1,83 @@
use diesel::prelude::*;
use crate::errors;
use crate::models;
use bcrypt::{hash, DEFAULT_COST};
use std::rc::Rc;
pub fn find_user_by_uid(
uid: i32,
conn: &SqliteConnection,
) -> Result<Option<models::UserDTO>, errors::DomainError> {
use crate::schema::users::dsl::*;
let maybe_user = users
.select((name, created_at))
.find(uid)
.first::<models::UserDTO>(conn)
.optional();
Ok(maybe_user?)
}
pub fn _find_user_by_name(
user_name: String,
conn: &SqliteConnection,
) -> Result<Option<models::UserDTO>, errors::DomainError> {
use crate::schema::users::dsl::*;
let maybe_user = users
.select((name, created_at))
.filter(name.eq(user_name))
.first::<models::UserDTO>(conn)
.optional();
Ok(maybe_user?)
}
pub fn get_all(
conn: &SqliteConnection,
) -> Result<Option<Vec<models::UserDTO>>, errors::DomainError> {
use crate::schema::users::dsl::*;
Ok(users
.select((name, created_at))
.load::<models::UserDTO>(conn)
.optional()?)
}
/// Run query using Diesel to insert a new database row and return the result.
pub fn insert_new_user(
mut nu: Rc<models::NewUser>,
conn: &SqliteConnection,
) -> Result<models::UserDTO, errors::DomainError> {
// It is common when using Diesel with Actix web to import schema-related
// modules inside a function's scope (rather than the normal module's scope)
// to prevent import collisions and namespace pollution.
use crate::schema::users::dsl::*;
// let new_user = models::User {
// id: Uuid::new_v4().to_string(),
// name: nu.name.to_string(),
// };
// let x = users.load::<models::User>(conn).optional();
// let target = users.find("4");
// let test_user = models::User {
// id: "5".to_owned(),
// name: "who".to_owned(),
// };
// let update_result = diesel::update(target).set(&test_user).execute(conn);
// let mut nu2 = nu.clone();
let mut nu2 = Rc::make_mut(&mut nu);
nu2.password = hash(nu2.password.clone(), DEFAULT_COST)?;
diesel::insert_into(users)
.values(nu.as_ref())
.execute(conn)?;
let user = users
.select((name, created_at))
.filter(name.eq(nu.name.clone()))
.first::<models::UserDTO>(conn);
Ok(user?)
}

2
src/errors.rs

@ -0,0 +1,2 @@
pub mod domain_error;
pub use self::domain_error::*;

88
src/errors/domain_error.rs

@ -0,0 +1,88 @@
use actix_web::{HttpResponse, ResponseError};
use bcrypt::BcryptError;
use custom_error::custom_error;
// use derive_more::Display;
// use diesel::result::DatabaseErrorKind;
use crate::models::errors::*;
use r2d2;
use std::convert::From;
// use std::error::Error;
// pub enum DomainError {
// #[display(fmt = "PasswordHashError")]
// PwdHashError,
// #[display(fmt = "Bad Id")]
// IdError,
// #[display(fmt = "Generic Error")]
// GenericError,
// DuplicateValue,
// }
// impl Error for DomainError {
// fn source(&self) -> Option<&(dyn error::Error + 'static)> {
// // Generic error, underlying cause isn't tracked.
// None
// }
// }
// impl From<DBError> for DomainError {
// fn from(error: DBError) -> DomainError {
// // We only care about UniqueViolations
// match error {
// DBError::DatabaseError(kind, info) => {
// let message = info.details().unwrap_or_else(|| info.message()).to_string();
// match kind {
// DatabaseErrorKind::UniqueViolation => DomainError::DuplicateValue(message),
// _ => DomainError::GenericError(message),
// }
// }
// _ => DomainError::GenericError(String::from("Some database error occured")),
// }
// }
// }
custom_error! { pub DomainError
PwdHashError {source: BcryptError} = "Failed to has password",
DbError {source: diesel::result::Error} = "Database error",
DbPoolError {source: r2d2::Error} = "Failed to get connection from pool",
GenericError {cause: String} = "Generic Error - Reason: {cause}"
}
impl ResponseError for DomainError {
fn error_response(&self) -> HttpResponse {
match self {
DomainError::PwdHashError { source } => {
HttpResponse::InternalServerError().json(ErrorModel {
status_code: 500,
reason: format!(
"{} {}",
"Unexpected Error - Failed to hash password", source
),
})
}
DomainError::DbError { source } => {
HttpResponse::InternalServerError().json(ErrorModel {
status_code: 500,
reason: format!("{} {}", "Unexpected Database Error", source),
})
}
DomainError::DbPoolError { source } => {
HttpResponse::InternalServerError().json(ErrorModel {
status_code: 500,
reason: format!(
"{} {}",
"Unexpected Error - Failed to get connection from pool", source
),
})
}
DomainError::GenericError { cause } => HttpResponse::BadRequest().json(ErrorModel {
status_code: 400,
reason: format!(
"{} {}, ",
"Unexpected Database Error - ".to_owned(),
cause.clone()
),
}),
}
}
}

188
src/main.rs

@ -1,32 +1,43 @@
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate comp;
#[macro_use]
extern crate validator_derive;
extern crate bcrypt;
extern crate custom_error;
extern crate regex;
extern crate validator;
use actix_web::{
dev::ServiceRequest, error, get, middleware, post, web, App, Error, HttpRequest, HttpResponse,
HttpServer, Responder,
error, get, middleware, post, web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder,
};
use yarte::Template;
use actix_web_httpauth::{extractors::basic::BasicAuth, middleware::HttpAuthentication};
use actix_web_httpauth::middleware::HttpAuthentication;
use actix_http::cookie::SameSite;
use actix_identity::{CookieIdentityPolicy, Identity, IdentityService};
use actix_identity::{CookieIdentityPolicy, IdentityService};
use rand::Rng;
// use actix_http::*;
use actix_files as fs;
use diesel::prelude::*;
use diesel::r2d2::{self, ConnectionManager};
use routes::*;
// use middlewares::csrf;
// use routes;
// use routes::users;
// use utils;
mod actions;
mod errors;
mod middlewares;
mod models;
mod routes;
mod schema;
mod types;
mod utils;
#[macro_use]
extern crate log;
@ -68,161 +79,14 @@ impl UserServiceImpl {
}
}
pub trait UserService {
fn do_something(&self);
}
impl UserService for UserServiceImpl {
fn do_something(&self) {
println!("hello");
}
}
fn fun1(user_service: &dyn UserService) {
user_service.do_something();
}
fn fun2<T>(user_service: T)
where
T: UserService,
{
user_service.do_something();
}
/// In this example validator returns immediately,
/// but since it is required to return anything
/// that implements `IntoFuture` trait,
/// it can be extended to query database
/// or to do something else in a async manner.
async fn validator(req: ServiceRequest, credentials: BasicAuth) -> Result<ServiceRequest, Error> {
// All users are great and more than welcome!
// let pool = req.app_data::<DbPool>();
// let maybe_header = req.headers().get("Authorization");
// match maybe_header {
// Some(value) => {
// info!("{:?}", *value);
// let x: Result<Basic, _> = Scheme::parse(value);
// let y = x.expect("Error parsing header");
// println!("{}", y.user_id());
// println!("{:?}", y.password().clone());
// }
// None => debug!("Header not found"),
// }
// maybe_header
// .map(|value| {
// let x: Result<Basic, _> = Scheme::parse(value);
// x
// })
// .map(|maybe_basic| {
// maybe_basic
// .map(|x| {
// println!("{}", x.user_id());
// println!("{:?}", x.password().clone());
// })
// .map_err(|x| println!("error parsing reason - {}", x.to_string()))
// // maybe_basic
// });
// let auth = Authorization::<Basic>;
println!("{}", credentials.user_id());
println!("{:?}", credentials.password());
Ok(req)
}
// fn parse(header: &HeaderValue) -> Result<Basic, ParseError> {
// // "Basic *" length
// if header.len() < 7 {
// return Err(ParseError::Invalid);
// }
// let mut parts = header.to_str()?.splitn(2, ' ');
// match parts.next() {
// Some(scheme) if scheme == "Basic" => (),
// _ => return Err(ParseError::MissingScheme),
// }
// let decoded = base64::decode(parts.next().ok_or(ParseError::Invalid)?)?;
// let mut credentials = str::from_utf8(&decoded)?.splitn(2, ':');
// let user_id = credentials
// .next()
// .ok_or(ParseError::MissingField("user_id"))
// .map(|user_id| user_id.to_string().into())?;
// let password = credentials
// .next()
// .ok_or(ParseError::MissingField("password"))
// .map(|password| {
// if password.is_empty() {
// None
// } else {
// Some(password.to_string().into())
// }
// })?;
// Ok(Basic { user_id, password })
// }
#[get("/login")]
async fn login(id: Identity) -> HttpResponse {
let maybe_identity = id.identity();
// id.remember("user1".to_owned());
let response = if let Some(identity) = maybe_identity {
HttpResponse::Ok()
.header("location", "/")
.content_type("text/plain")
.body(format!("Already logged in {}", identity))
} else {
id.remember("user1".to_owned());
HttpResponse::Found().header("location", "/").finish()
};
// HttpResponse::Found().header("location", "/").finish()
response
}
#[get("/logout")]
async fn logout(id: Identity) -> HttpResponse {
let maybe_identity = id.identity();
// id.remember("user1".to_owned());
let response = if let Some(identity) = maybe_identity {
info!("Logging out {user}", user = identity);
id.forget();
HttpResponse::Found().header("location", "/").finish()
} else {
HttpResponse::Ok()
.header("location", "/")
.content_type("text/plain")
.body("Not logged in")
};
// id.forget();
// HttpResponse::Found().header("location", "/").finish()
response
}
#[get("/")]
async fn index2(id: Identity) -> String {
format!(
"Hello {}",
id.identity().unwrap_or_else(|| "Anonymous".to_owned())
)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
std::env::set_var("RUST_LOG", "debug");
env_logger::init();
dotenv::dotenv().ok();
let user_service: Box<dyn UserService> = Box::new(UserServiceImpl::new());
user_service.do_something();
fun1(user_service.as_ref());
let user_service_impl = UserServiceImpl::new();
fun2(user_service_impl);
let basic_auth_middleware = HttpAuthentication::basic(validator);
let basic_auth_middleware = HttpAuthentication::basic(utils::auth::validator);
// fun1(Rc::clone(&user_service).as_ref());
// set up database connection pool
let connspec = std::env::var("DATABASE_URL").expect("DATABASE_URL NOT FOUND");
let manager = ConnectionManager::<SqliteConnection>::new(connspec);
@ -240,18 +104,18 @@ async fn main() -> std::io::Result<()> {
CookieIdentityPolicy::new(&private_key)
.name("my-app-auth")
.secure(false)
.same_site(SameSite::Lax), // .same_site(),
.same_site(SameSite::Lax),
))
.wrap(middleware::Logger::default())
.service(web::scope("/chat").wrap(basic_auth_middleware.clone()))
// .service(extract_my_obj)
// .service(index)
.service(get_user)
.service(add_user)
.service(get_all_users)
.service(login)
.service(logout)
.service(index2)
.service(routes::users::get_user)
.service(routes::users::add_user)
.service(routes::users::get_all_users)
.service(routes::auth::login)
.service(routes::auth::logout)
.service(routes::auth::index)
.service(fs::Files::new("/", "./static"))
};
HttpServer::new(app).bind(addr)?.run().await

3
src/middlewares.rs

@ -0,0 +1,3 @@
pub mod csrf;
pub use self::csrf::*;

276
src/middlewares/csrf.rs

@ -0,0 +1,276 @@
// //! A filter for cross-site request forgery (CSRF).
// //!
// //! This middleware is stateless and [based on request
// //! headers](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers).
// //!
// //! By default requests are allowed only if one of these is true:
// //!
// //! * The request method is safe (`GET`, `HEAD`, `OPTIONS`). It is the
// //! applications responsibility to ensure these methods cannot be used to
// //! execute unwanted actions. Note that upgrade requests for websockets are
// //! also considered safe.
// //! * The `Origin` header (added automatically by the browser) matches one
// //! of the allowed origins.
// //! * There is no `Origin` header but the `Referer` header matches one of
// //! the allowed origins.
// //!
// //! Use [`CsrfFilter::allow_xhr()`](struct.CsrfFilter.html#method.allow_xhr)
// //! if you want to allow requests with unprotected methods via
// //! [CORS](../cors/struct.Cors.html).
// //!
// //! # Example
// //!
// //! ```
// //! # extern crate actix_web;
// //! use actix_web::middleware::csrf;
// //! use actix_web::{http, App, HttpRequest, HttpResponse};
// //!
// //! fn handle_post(_: &HttpRequest) -> &'static str {
// //! "This action should only be triggered with requests from the same site"
// //! }
// //!
// //! fn main() {
// //! let app = App::new()
// //! .middleware(
// //! csrf::CsrfFilter::new().allowed_origin("https://www.example.com"),
// //! )
// //! .resource("/", |r| {
// //! r.method(http::Method::GET).f(|_| HttpResponse::Ok());
// //! r.method(http::Method::POST).f(handle_post);
// //! })
// //! .finish();
// //! }
// //! ```
// //!
// //! In this example the entire application is protected from CSRF.
// use std::borrow::Cow;
// use std::collections::HashSet;
// use bytes::Bytes;
// use error::{ResponseError, Result};
// use http::{header, HeaderMap, HttpTryFrom, Uri};
// use httprequest::HttpRequest;
// use httpresponse::HttpResponse;
// use middleware::{Middleware, Started};
// use server::Request;
// /// Potential cross-site request forgery detected.
// #[derive(Debug, Fail)]
// pub enum CsrfError {
// /// The HTTP request header `Origin` was required but not provided.
// #[fail(display = "Origin header required")]
// MissingOrigin,
// /// The HTTP request header `Origin` could not be parsed correctly.
// #[fail(display = "Could not parse Origin header")]
// BadOrigin,
// /// The cross-site request was denied.
// #[fail(display = "Cross-site request denied")]
// CsrDenied,
// }
// impl ResponseError for CsrfError {
// fn error_response(&self) -> HttpResponse {
// HttpResponse::Forbidden().body(self.to_string())
// }
// }
// fn uri_origin(uri: &Uri) -> Option<String> {
// match (
// uri.scheme_part(),
// uri.host(),
// uri.port_part().map(|port| port.as_u16()),
// ) {
// (Some(scheme), Some(host), Some(port)) => Some(format!("{}://{}:{}", scheme, host, port)),
// (Some(scheme), Some(host), None) => Some(format!("{}://{}", scheme, host)),
// _ => None,
// }
// }
// fn origin(headers: &HeaderMap) -> Option<Result<Cow<str>, CsrfError>> {
// headers
// .get(header::ORIGIN)
// .map(|origin| {
// origin
// .to_str()
// .map_err(|_| CsrfError::BadOrigin)
// .map(|o| o.into())
// })
// .or_else(|| {
// headers.get(header::REFERER).map(|referer| {
// Uri::try_from(Bytes::from(referer.as_bytes()))
// .ok()
// .as_ref()
// .and_then(uri_origin)
// .ok_or(CsrfError::BadOrigin)
// .map(|o| o.into())
// })
// })
// }
// /// A middleware that filters cross-site requests.
// ///
// /// To construct a CSRF filter:
// ///
// /// 1. Call [`CsrfFilter::build`](struct.CsrfFilter.html#method.build) to
// /// start building.
// /// 2. [Add](struct.CsrfFilterBuilder.html#method.allowed_origin) allowed
// /// origins.
// /// 3. Call [finish](struct.CsrfFilterBuilder.html#method.finish) to retrieve
// /// the constructed filter.
// ///
// /// # Example
// ///
// /// ```
// /// use actix_web::middleware::csrf;
// /// use actix_web::App;
// ///
// /// # fn main() {
// /// let app = App::new()
// /// .middleware(csrf::CsrfFilter::new().allowed_origin("https://www.example.com"));
// /// # }
// /// ```
// #[derive(Default)]
// pub struct CsrfFilter {
// origins: HashSet<String>,
// allow_xhr: bool,
// allow_missing_origin: bool,
// allow_upgrade: bool,
// }
// impl CsrfFilter {
// /// Start building a `CsrfFilter`.
// pub fn new() -> CsrfFilter {
// CsrfFilter {
// origins: HashSet::new(),
// allow_xhr: false,
// allow_missing_origin: false,
// allow_upgrade: false,
// }
// }
// /// Add an origin that is allowed to make requests. Will be verified
// /// against the `Origin` request header.
// pub fn allowed_origin<T: Into<String>>(mut self, origin: T) -> CsrfFilter {
// self.origins.insert(origin.into());
// self
// }
// /// Allow all requests with an `X-Requested-With` header.
// ///
// /// A cross-site attacker should not be able to send requests with custom
// /// headers unless a CORS policy whitelists them. Therefore it should be
// /// safe to allow requests with an `X-Requested-With` header (added
// /// automatically by many JavaScript libraries).
// ///
// /// This is disabled by default, because in Safari it is possible to
// /// circumvent this using redirects and Flash.
// ///
// /// Use this method to enable more lax filtering.
// pub fn allow_xhr(mut self) -> CsrfFilter {
// self.allow_xhr = true;
// self
// }
// /// Allow requests if the expected `Origin` header is missing (and
// /// there is no `Referer` to fall back on).
// ///
// /// The filter is conservative by default, but it should be safe to allow
// /// missing `Origin` headers because a cross-site attacker cannot prevent
// /// the browser from sending `Origin` on unprotected requests.
// pub fn allow_missing_origin(mut self) -> CsrfFilter {
// self.allow_missing_origin = true;
// self
// }
// /// Allow cross-site upgrade requests (for example to open a WebSocket).
// pub fn allow_upgrade(mut self) -> CsrfFilter {
// self.allow_upgrade = true;
// self
// }
// fn validate(&self, req: &Request) -> Result<(), CsrfError> {
// let is_upgrade = req.headers().contains_key(header::UPGRADE);
// let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);
// if is_safe || (self.allow_xhr && req.headers().contains_key("x-requested-with")) {
// Ok(())
// } else if let Some(header) = origin(req.headers()) {
// match header {
// Ok(ref origin) if self.origins.contains(origin.as_ref()) => Ok(()),
// Ok(_) => Err(CsrfError::CsrDenied),
// Err(err) => Err(err),
// }
// } else if self.allow_missing_origin {
// Ok(())
// } else {
// Err(CsrfError::MissingOrigin)
// }
// }
// }
// impl<S> Middleware<S> for CsrfFilter {
// fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
// self.validate(req)?;
// Ok(Started::Done)
// }
// }
// #[cfg(test)]
// mod tests {
// use super::*;
// use http::Method;
// use test::TestRequest;
// #[test]
// fn test_safe() {
// let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
// let req = TestRequest::with_header("Origin", "https://www.w3.org")
// .method(Method::HEAD)
// .finish();
// assert!(csrf.start(&req).is_ok());
// }
// #[test]
// fn test_csrf() {
// let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
// let req = TestRequest::with_header("Origin", "https://www.w3.org")
// .method(Method::POST)
// .finish();
// assert!(csrf.start(&req).is_err());
// }
// #[test]
// fn test_referer() {
// let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
// let req =
// TestRequest::with_header("Referer", "https://www.example.com/some/path?query=param")
// .method(Method::POST)
// .finish();
// assert!(csrf.start(&req).is_ok());
// }
// #[test]
// fn test_upgrade() {
// let strict_csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
// let lax_csrf = CsrfFilter::new()
// .allowed_origin("https://www.example.com")
// .allow_upgrade();
// let req = TestRequest::with_header("Origin", "https://cswsh.com")
// .header("Connection", "Upgrade")
// .header("Upgrade", "websocket")
// .method(Method::GET)
// .finish();
// assert!(strict_csrf.start(&req).is_err());
// assert!(lax_csrf.start(&req).is_ok());
// }
// }

28
src/models.rs

@ -1,24 +1,4 @@
use serde::{Deserialize, Serialize};
use crate::schema::users;
use yarte::Template;
#[derive(Debug, Clone, Serialize, Queryable, Identifiable, Deserialize)]
pub struct User {
pub id: i32,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Insertable, Deserialize)]
#[table_name = "users"]
pub struct NewUser {
pub name: String,
}
#[derive(Template)]
#[template(path = "hello.hbs")]
pub struct CardTemplate<'a> {
pub title: &'a str,