From 7dde11789943add5d8d7b8aca2f9fd3f8657eff7 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 12:31:17 +0200 Subject: [PATCH 01/18] security for db records, initial steps, key is hardcoded for testing purposes --- .env | 3 +- Cargo.lock | 640 +++++++++++------- Cargo.toml | 3 + docker-compose.yml | 22 + docker/dev/.env | 5 +- docker/dev/docker-compose.yml | 23 + src/forms/cloud.rs | 124 +++- src/helpers/cloud/mod.rs | 2 + src/helpers/cloud/security.rs | 135 ++++ src/helpers/mod.rs | 3 + .../authentication/method/f_oauth.rs | 11 +- src/models/cloud.rs | 35 + src/routes/cloud/add.rs | 12 +- src/routes/cloud/get.rs | 25 +- src/routes/cloud/update.rs | 2 + src/routes/project/compose.rs | 2 +- src/routes/server/get.rs | 13 +- src/routes/server/update.rs | 2 - src/startup.rs | 5 +- tests/mock_data/cloud.json | 3 +- 20 files changed, 777 insertions(+), 293 deletions(-) create mode 100644 src/helpers/cloud/mod.rs create mode 100644 src/helpers/cloud/security.rs diff --git a/.env b/.env index 14bdc640..1dcbed0b 100644 --- a/.env +++ b/.env @@ -4,4 +4,5 @@ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/stacker POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker -POSTGRES_PORT=5432 \ No newline at end of file +POSTGRES_PORT=5432 +SECURITY_KEY=SECURITY_KEY_SHOULD_BE_OF_LEN_32 diff --git a/Cargo.lock b/Cargo.lock index f04999e9..b5d6160f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,7 +5,7 @@ version = 3 [[package]] name = "actix-casbin-auth" version = "0.4.4" -source = "git+https://github.com/smart--petea/actix-casbin-auth.git?branch=dirty-master#5b76cc4e0df68ccee02c563b947a788ab1cb5193" +source = "git+https://github.com/smart--petea/actix-casbin-auth.git?branch=dirty-master#278bd574038617171a6493aef3d4d4fdef324602" dependencies = [ "actix-service", "actix-web", @@ -20,7 +20,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "bytes", "futures-core", "futures-sink", @@ -56,9 +56,9 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "ahash 0.8.10", + "ahash 0.8.11", "base64 0.21.7", - "bitflags 2.4.2", + "bitflags 2.5.0", "brotli", "bytes", "bytestring", @@ -92,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -171,7 +171,7 @@ dependencies = [ "actix-service", "actix-utils", "actix-web-codegen", - "ahash 0.8.10", + "ahash 0.8.11", "bytes", "bytestring", "cfg-if", @@ -205,7 +205,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -223,6 +223,41 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.8" @@ -236,9 +271,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "const-random", @@ -250,9 +285,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -391,9 +426,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "assert-json-diff" @@ -423,7 +458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 5.1.0", + "event-listener 5.2.0", "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", @@ -438,8 +473,8 @@ dependencies = [ "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.2.0", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] @@ -451,10 +486,10 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.2.0", "async-executor", - "async-io 2.3.1", + "async-io 2.3.2", "async-lock 3.3.0", "blocking", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "once_cell", ] @@ -491,18 +526,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "parking", - "polling 3.5.0", - "rustix 0.38.31", + "polling 3.6.0", + "rustix 0.38.32", "slab", "tracing", "windows-sys 0.52.0", @@ -548,13 +583,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -581,27 +616,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomic-write-file" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" -dependencies = [ - "nix", - "rand 0.8.5", -] - [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -624,6 +649,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "bitflags" version = "1.3.2" @@ -632,9 +663,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -663,18 +694,18 @@ dependencies = [ "async-channel 2.2.0", "async-lock 3.3.0", "async-task", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "piper", "tracing", ] [[package]] name = "brotli" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -693,9 +724,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytecount" @@ -711,9 +742,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bytestring" @@ -735,9 +766,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -787,10 +818,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ + "jobserver", "libc", ] @@ -828,9 +860,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -838,9 +870,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -850,14 +882,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.0" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -872,6 +904,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -902,9 +948,9 @@ dependencies = [ [[package]] name = "const-random" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" dependencies = [ "const-random-macro", ] @@ -939,9 +985,12 @@ dependencies = [ [[package]] name = "cookie-factory" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] [[package]] name = "core-foundation" @@ -994,9 +1043,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -1029,9 +1078,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "darling" version = "0.14.4" @@ -1263,12 +1322,12 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "docker-compose-types" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e366950e7785fe0e404901a2b4e5c09ebd6656767f0c2167e34c5068ce0cc2d" +checksum = "6d6fdd6fa1c9e8e716f5f73406b868929f468702449621e7397066478b9bf89c" dependencies = [ "derive_builder 0.13.1", - "indexmap 2.2.3", + "indexmap 2.2.6", "serde", "serde_yaml", ] @@ -1358,9 +1417,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" dependencies = [ "concurrent-queue", "parking", @@ -1383,7 +1442,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" dependencies = [ - "event-listener 5.1.0", + "event-listener 5.2.0", "pin-project-lite", ] @@ -1407,9 +1466,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "finl_unicode" @@ -1571,11 +1630,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-core", "futures-io", "parking", @@ -1590,7 +1649,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -1673,6 +1732,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.28.1" @@ -1687,9 +1756,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -1697,7 +1766,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.3", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1728,7 +1797,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.10", + "ahash 0.8.11", "allocator-api2", ] @@ -1750,11 +1819,17 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1791,9 +1866,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1932,9 +2007,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1994,15 +2069,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2065,7 +2149,7 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", "redox_syscall 0.4.1", ] @@ -2117,9 +2201,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -2184,9 +2268,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -2218,17 +2302,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.2", - "cfg-if", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -2289,13 +2362,19 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -2312,7 +2391,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -2440,9 +2519,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" dependencies = [ "memchr", "thiserror", @@ -2451,9 +2530,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" dependencies = [ "pest", "pest_generator", @@ -2461,22 +2540,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] name = "pest_meta" -version = "2.7.7" +version = "2.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" dependencies = [ "once_cell", "pest", @@ -2490,27 +2569,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.3", + "indexmap 2.2.6", ] [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -2544,7 +2623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", ] @@ -2572,18 +2651,31 @@ dependencies = [ [[package]] name = "polling" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi", "pin-project-lite", - "rustix 0.38.31", + "rustix 0.38.32", "tracing", "windows-sys 0.52.0", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2622,9 +2714,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -2635,7 +2727,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "memchr", "unicase", ] @@ -2740,6 +2832,27 @@ dependencies = [ "futures-io", ] +[[package]] +name = "redis" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d64e978fd98a0e6b105d066ba4889a7301fca65aeac850a877d8797343feeb" +dependencies = [ + "async-trait", + "bytes", + "combine", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "socket2 0.5.6", + "tokio", + "tokio-util", + "url", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2771,13 +2884,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -2792,9 +2905,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -2815,9 +2928,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.24" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", @@ -2865,8 +2978,8 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6273372244d04a8a4b0bec080ea1e710403e88c5d9d83f9808b2bfa64f0982a" dependencies = [ - "ahash 0.8.10", - "bitflags 2.4.2", + "ahash 0.8.11", + "bitflags 2.5.0", "instant", "num-traits", "once_cell", @@ -2885,7 +2998,7 @@ checksum = "9db7f8dc4c9d48183a17ce550574c42995252b82d267eaca3fcd1b979159856c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -2980,11 +3093,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys 0.4.13", @@ -3147,14 +3260,14 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -3163,9 +3276,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -3200,7 +3313,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70c0e00fab6460447391a1981c21341746bc2d0178a7c46a3bbf667f450ac6e4" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.6", "itertools", "num-traits", "once_cell", @@ -3225,7 +3338,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.0", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -3240,11 +3353,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.32" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -3262,6 +3375,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.10.8" @@ -3317,9 +3436,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -3394,12 +3513,12 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" dependencies = [ - "sqlx-core 0.7.3", - "sqlx-macros 0.7.3", + "sqlx-core 0.7.4", + "sqlx-macros 0.7.4", "sqlx-postgres", ] @@ -3412,7 +3531,7 @@ dependencies = [ "async-trait", "casbin", "dotenv", - "sqlx 0.7.3", + "sqlx 0.7.4", ] [[package]] @@ -3472,17 +3591,16 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" dependencies = [ - "ahash 0.8.10", + "ahash 0.8.11", "atoi 2.0.0", "byteorder", "bytes", "crc", "crossbeam-queue", - "dotenvy", "either", "event-listener 2.5.3", "futures-channel", @@ -3492,7 +3610,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.2.3", + "indexmap 2.2.6", "log", "memchr", "native-tls", @@ -3519,7 +3637,7 @@ checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" dependencies = [ "dotenvy", "either", - "heck", + "heck 0.4.1", "hex", "once_cell", "proc-macro2", @@ -3535,27 +3653,26 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" dependencies = [ "proc-macro2", "quote", - "sqlx-core 0.7.3", + "sqlx-core 0.7.4", "sqlx-macros-core", "syn 1.0.109", ] [[package]] name = "sqlx-macros-core" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" dependencies = [ - "atomic-write-file", "dotenvy", "either", - "heck", + "heck 0.4.1", "hex", "once_cell", "proc-macro2", @@ -3563,7 +3680,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "sqlx-core 0.7.3", + "sqlx-core 0.7.4", "sqlx-postgres", "syn 1.0.109", "tempfile", @@ -3573,13 +3690,13 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi 2.0.0", "base64 0.21.7", - "bitflags 2.4.2", + "bitflags 2.5.0", "byteorder", "crc", "dotenvy", @@ -3600,10 +3717,9 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "sha1", "sha2", "smallvec", - "sqlx-core 0.7.3", + "sqlx-core 0.7.4", "stringprep", "thiserror", "tracing", @@ -3629,6 +3745,8 @@ dependencies = [ "actix-cors", "actix-http", "actix-web", + "aes-gcm", + "base64 0.22.0", "brotli", "chrono", "clap", @@ -3637,13 +3755,14 @@ dependencies = [ "derive_builder 0.12.0", "docker-compose-types", "futures", - "futures-lite 2.2.0", + "futures-lite 2.3.0", "futures-util", "glob", "hmac", - "indexmap 2.2.3", + "indexmap 2.2.6", "lapin", "rand 0.8.5", + "redis", "regex", "reqwest", "serde", @@ -3715,9 +3834,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.51" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -3776,8 +3895,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "rustix 0.38.31", + "fastrand 2.0.2", + "rustix 0.38.32", "windows-sys 0.52.0", ] @@ -3792,22 +3911,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -3924,7 +4043,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -3950,9 +4069,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -4002,9 +4121,9 @@ dependencies = [ [[package]] name = "tracing-actix-web" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe0d5feac3f4ca21ba33496bcb1ccab58cca6412b1405ae80f0581541e0ca78" +checksum = "fa069bd1503dd526ee793bb3fce408895136c95fc86d2edb2acf1c646d7f0684" dependencies = [ "actix-web", "mutually_exclusive_features", @@ -4021,7 +4140,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] @@ -4030,7 +4149,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5c266b9ac83dedf0e0385ad78514949e6d89491269e7065bee51d2bb8ec7373" dependencies = [ - "ahash 0.8.10", + "ahash 0.8.11", "gethostname", "log", "serde", @@ -4158,11 +4277,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -4196,9 +4325,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom 0.2.12", "serde", @@ -4230,9 +4359,9 @@ checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -4265,11 +4394,17 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4277,24 +4412,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -4304,9 +4439,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4314,28 +4449,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4362,11 +4497,12 @@ dependencies = [ [[package]] name = "whoami" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "wasm-bindgen", + "redox_syscall 0.4.1", + "wasite", "web-sys", ] @@ -4407,7 +4543,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -4425,7 +4561,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.4", ] [[package]] @@ -4445,17 +4581,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -4466,9 +4602,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -4478,9 +4614,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -4490,9 +4626,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -4502,9 +4638,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -4514,9 +4650,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -4526,9 +4662,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -4538,9 +4674,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winreg" @@ -4606,7 +4742,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.51", + "syn 2.0.55", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 982000f3..030e403f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,9 @@ deadpool-lapin = "0.11.0" docker-compose-types = "0.7.0" #actix-casbin-auth = { git = "https://github.com/casbin-rs/actix-casbin-auth.git"} actix-casbin-auth = { git = "https://github.com/smart--petea/actix-casbin-auth.git", branch = "dirty-master"} +aes-gcm = "0.10.3" +base64 = "0.22.0" +redis = { version = "0.25.2", features = ["tokio-comp"] } [dependencies.sqlx] version = "0.6.3" diff --git a/docker-compose.yml b/docker-compose.yml index eeb10eb2..4ba581c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,9 @@ volumes: stackerdb: driver: local + redis-data: + driver: local + services: @@ -29,6 +32,25 @@ services: # stackerdb: # condition: service_healthy + redis: + container_name: redis + image: redis + restart: always + ports: + - 6379:6379 + volumes: + - redis-data:/data +# - ./redis/rc.local:/etc/rc.local +# - ./redis/redis.conf:/usr/local/etc/redis/redis.conf + sysctls: + net.core.somaxconn: 1024 + logging: + driver: "json-file" + options: + max-size: "10m" + tag: "container_{{.Name}}" + + # stacker_queue: # image: trydirect/stacker:0.0.7 # container_name: stacker_queue diff --git a/docker/dev/.env b/docker/dev/.env index 6371a972..d60f2662 100644 --- a/docker/dev/.env +++ b/docker/dev/.env @@ -1,5 +1,8 @@ +SECURITY_KEY=SECURITY_KEY_SHOULD_BE_OF_LEN_32 + DATABASE_URL=postgres://postgres:postgres@stackerdb:5432/stacker POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker -POSTGRES_PORT=5432 \ No newline at end of file +POSTGRES_PORT=5432 + diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index 3577b145..eeac0386 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -4,6 +4,9 @@ volumes: stackerdb: driver: local + redis-data: + driver: local + networks: backend: driver: bridge @@ -75,3 +78,23 @@ services: - ./postgresql.conf:/etc/postgresql/postgresql.conf networks: - backend + + redis: + container_name: stackerredis + image: redis:latest + restart: always + ports: + - 6379 + volumes: + - redis-data:/data + # - ./redis/rc.local:/etc/rc.local + # - ./redis/redis.conf:/usr/local/etc/redis/redis.conf + sysctls: + net.core.somaxconn: 1024 + logging: + driver: "json-file" + options: + max-size: "10m" + tag: "container_{{.Name}}" + + diff --git a/src/forms/cloud.rs b/src/forms/cloud.rs index ab040c37..8cc1567e 100644 --- a/src/forms/cloud.rs +++ b/src/forms/cloud.rs @@ -1,32 +1,146 @@ use crate::models; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Serialize, Serializer}; use serde_valid::Validate; +use crate::helpers::cloud::security::Secret; +use tracing::Instrument; -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] + +// fn hide_part(value: &Option, s: S) -> Result +// where +// S: Serializer, +// { +// eprintln!("value in serde {:?}", value); +// let result: &str = match value { +// Some(value) => { +// let value = value.as_str(); +// value +// // value.into_iter().take(6).collect::() +// } +// None => "", +// }; +// s.serialize_str(result) +// } +fn hide_parts(value: String) -> String { + value.chars().into_iter().take(6).collect::() + "****" +} + + +#[derive(Default, Clone, PartialEq, Serialize, Deserialize, Validate)] pub struct Cloud { + pub user_id: Option, pub project_id: Option, #[validate(min_length = 2)] #[validate(max_length = 50)] pub provider: String, + // #[serde(serialize_with = "hide_part")] pub cloud_token: Option, + // #[serde(serialize_with = "hide_part")] pub cloud_key: Option, + // #[serde(serialize_with = "hide_part")] pub cloud_secret: Option, pub save_token: Option, } +impl Cloud { + pub(crate) fn decode(secret: &mut Secret, encrypted_value: String) -> String { + // tracing::error!("encrypted_value {:?}", &encrypted_value); + let b64_decoded = Secret::b64_decode(&encrypted_value).unwrap(); + match secret.decrypt(b64_decoded) { + Ok(decoded) => decoded, + Err(_err) => { + tracing::error!("🟥 Could not decode {:?},{:?}",secret.field,_err); + // panic!("Could not decode "); + "".to_owned() + } + } + } + + // @todo should be refactored, may be moved to cloud.into() or Secret::from() + pub(crate) fn decode_model(mut cloud: models::Cloud) -> models::Cloud { + + let encrypted_value = cloud.cloud_token.clone().unwrap(); + let mut secret = Secret::new(); + secret.user_id = cloud.user_id.clone(); + secret.project_id = cloud.project_id.clone().unwrap(); + secret.field = "cloud_token".to_string(); + let cloud_token = Cloud::decode(&mut secret, encrypted_value); + cloud.cloud_token = Some(hide_parts(cloud_token)); + cloud + } +} + +impl std::fmt::Debug for Cloud { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let cloud_key: String = match self.cloud_key.as_ref() { + Some(val) => + { + val.chars().take(4).collect::() + "****" + }, + None => "".to_string(), + }; + let cloud_token: String = match self.cloud_token.as_ref() { + Some(val) => { + eprintln!("cloud token {val:?}"); + val.chars().take(4).collect::() + "****" + }, + None => "".to_string(), + }; + + let cloud_secret: String = match self.cloud_secret.as_ref() { + Some(val) => { + val.chars().take(4).collect::() + "****" + } + None => "".to_string(), + }; + + write!(f, "{} cloud creds: cloud_key : {} cloud_token: {} cloud_secret: {} project_id: {:?}", + self.provider, + cloud_key, + cloud_token, + cloud_secret, + self.project_id + ) + } +} + impl Into for &Cloud { fn into(self) -> models::Cloud { let mut cloud = models::Cloud::default(); cloud.provider = self.provider.clone(); - cloud.cloud_token = self.cloud_token.clone(); - cloud.cloud_key = self.cloud_key.clone(); - cloud.cloud_secret = self.cloud_secret.clone(); + cloud.user_id = self.user_id.clone().unwrap(); + cloud.project_id = self.project_id; + + let mut secret = Secret::new(); + secret.user_id = self.user_id.clone().unwrap(); + secret.project_id = self.project_id.unwrap(); + + if let Some(val) = self.cloud_token.clone() { + secret.field = "cloud_token".to_owned(); + if let Ok(encrypted) = secret.encrypt(val) { + cloud.cloud_token = Some(Secret::b64_encode(&encrypted)); + }; + } + if let Some(val) = self.cloud_key.clone() { + secret.field = "cloud_key".to_owned(); + if let Ok(encrypted) = secret.encrypt(val) { + // cloud.cloud_token = Some(encrypted.data.iter().map(|&c| c as char).collect::()) + cloud.cloud_key = Some(Secret::b64_encode(&encrypted)); + }; + } + if let Some(val) = self.cloud_secret.clone() { + secret.field = "cloud_secret".to_owned(); + if let Ok(encrypted) = secret.encrypt(val) { + cloud.cloud_secret = Some(Secret::b64_encode(&encrypted)) + }; + } cloud.save_token = self.save_token.clone(); cloud } + } + impl Into for models::Cloud { fn into(self) -> Cloud { let mut form = Cloud::default(); diff --git a/src/helpers/cloud/mod.rs b/src/helpers/cloud/mod.rs new file mode 100644 index 00000000..9d2c9991 --- /dev/null +++ b/src/helpers/cloud/mod.rs @@ -0,0 +1,2 @@ +pub(crate) mod security; +pub use security::*; \ No newline at end of file diff --git a/src/helpers/cloud/security.rs b/src/helpers/cloud/security.rs new file mode 100644 index 00000000..29b997cf --- /dev/null +++ b/src/helpers/cloud/security.rs @@ -0,0 +1,135 @@ +use aes_gcm::{ + aead::{Aead, AeadCore, KeyInit, OsRng}, + Aes256Gcm, Nonce, Key // Or `Aes128Gcm` +}; +use base64::{engine::general_purpose, Engine as _}; +use redis::{Commands, Connection}; +use tracing::Instrument; + +#[derive(Debug, Default, PartialEq, Clone)] +pub(crate) struct Secret { + pub(crate) user_id: String, + pub(crate) project_id: i32, + pub(crate) field: String, // cloud_token/cloud_key/cloud_secret + pub(crate) nonce: Vec, +} + + +impl Secret { + pub fn new() -> Self { + + Secret { + user_id: "".to_string(), + project_id: 0, + field: "".to_string(), + nonce: vec![], + } + } + #[tracing::instrument(name = "Secret::connect_storage")] + fn connect_storage() -> Connection { + match redis::Client::open("redis://127.0.0.1/"){ + Ok(client) => { + match client.get_connection() { + Ok(connection) => connection, + Err(_err) => panic!("Error connecting Redis") + } + } + Err(err) => panic!("Could not connect to Redis, {:?}", err) + } + } + + #[tracing::instrument(name = "Secret::save")] + fn save(&self, value: &[u8]) -> &Self { + let mut conn = Secret::connect_storage(); + let key = format!("{}_{}_{}", self.user_id, self.project_id, self.field); + tracing::debug!("Saving into storage.."); + let _: () = match conn.set(key, value) { + Ok(s) => s, + Err(e) => panic!("Could not save to storage {}", e) + }; + self + } + + pub fn b64_encode(value: &Vec) -> String { + general_purpose::STANDARD.encode(value) + } + + pub fn b64_decode(value: &String) -> Result, String> { + general_purpose::STANDARD.decode(value) + .map_err(|e| format!("b64_decode error {}", e)) + } + + #[tracing::instrument(name = "Secret::get")] + fn get(&mut self, key: String) -> &mut Self { + let mut conn = Secret::connect_storage(); + let nonce: Vec = match conn.get(&key) { + Ok(value) => { + tracing::debug!("Got value from storage {:?}", &value); + value + }, + Err(_e) => { + tracing::error!("Could not get value from storage by key {:?} {:?}", &key, _e); + vec![] + } + }; + + self.nonce = nonce; + self + } + + #[tracing::instrument(name = "encrypt.")] + pub fn encrypt(&self, token: String) -> Result, String> { + + // let sec_key = std::env::var("SECURITY_KEY") + // .expect("SECURITY_KEY environment variable is not set") + // .as_bytes(); + let sec_key = "SECURITY_KEY_SHOULD_BE_OF_LEN_32".as_bytes(); + // let key = Aes256Gcm::generate_key(OsRng); + let key: &Key:: = Key::::from_slice(&sec_key); + // eprintln!("encrypt key {key:?}"); + // eprintln!("encrypt: from slice key {key:?}"); + let cipher = Aes256Gcm::new(&key); + // eprintln!("encrypt: Cipher str {cipher:?}"); + let nonce = Aes256Gcm::generate_nonce(&mut OsRng); // 96-bits; unique per message + eprintln!("Nonce bytes {nonce:?}"); + // let nonce_b64: String = general_purpose::STANDARD.encode(nonce); + // eprintln!("Nonce b64 {nonce_b64:?}"); + eprintln!("token {token:?}"); + + let cipher_vec = cipher.encrypt(&nonce, token.as_ref()) + .map_err(|e| format!("{:?}", e))?; + + // store nonce for a limited amount of time + // self.save(cipher_vec.clone()); + self.save(nonce.as_slice()); + + eprintln!("Cipher {cipher_vec:?}"); + Ok(cipher_vec) + } + + #[tracing::instrument(name = "decrypt.")] + pub fn decrypt(&mut self, encrypted_data: Vec) -> Result { + let sec_key = "SECURITY_KEY_SHOULD_BE_OF_LEN_32".as_bytes(); + // let sec_key = std::env::var("SECURITY_KEY") + // .expect("SECURITY_KEY environment variable is not set") + // .as_bytes(); + let key: &Key:: = Key::::from_slice(&sec_key); + // eprintln!("decrypt: Key str {key:?}"); + let rkey = format!("{}_{}_{}", self.user_id, self.project_id, self.field); + eprintln!("decrypt: Key str {rkey:?}"); + self.get(rkey); + // eprintln!("decrypt: nonce b64:decoded {nonce:?}"); + + let nonce = Nonce::from_slice(self.nonce.as_slice()); + eprintln!("decrypt: nonce {nonce:?}"); + + let cipher = Aes256Gcm::new(&key); + // eprintln!("decrypt: Cipher str {cipher:?}"); + eprintln!("decrypt: str {encrypted_data:?}"); + + let plaintext = cipher.decrypt(&nonce, encrypted_data.as_ref()) + .map_err(|e| format!("{:?}", e))?; + + Ok(String::from_utf8(plaintext).map_err(|e| format!("{:?}", e))?) + } +} \ No newline at end of file diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 0640df84..368eafd6 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -7,5 +7,8 @@ pub use json::*; pub use mq_manager::*; pub mod dockerhub; pub(crate) mod compressor; +pub(crate) mod cloud; pub use dockerhub::*; + +pub use cloud::*; \ No newline at end of file diff --git a/src/middleware/authentication/method/f_oauth.rs b/src/middleware/authentication/method/f_oauth.rs index efe215c6..8400e81d 100644 --- a/src/middleware/authentication/method/f_oauth.rs +++ b/src/middleware/authentication/method/f_oauth.rs @@ -20,7 +20,7 @@ fn try_extract_token(authentication: String) -> Result { Ok(token.unwrap().into()) } -#[tracing::instrument(name = "try authenticate via bearer")] +#[tracing::instrument(name = "Authenticate with bearer token")] pub async fn try_oauth(req: &mut ServiceRequest) -> Result { let authentication = get_header::(&req, "authorization")?; if authentication.is_none() { @@ -33,7 +33,8 @@ pub async fn try_oauth(req: &mut ServiceRequest) -> Result { .await .map_err(|err| format!("{err}"))?; - let accesscontrol_vals = actix_casbin_auth::CasbinVals { + // println!("user ================== {}", user.id.clone()); + let acl_vals = actix_casbin_auth::CasbinVals { subject: user.id.clone(), domain: None, }; @@ -42,8 +43,8 @@ pub async fn try_oauth(req: &mut ServiceRequest) -> Result { return Err("user already logged".to_string()); } - if req.extensions_mut().insert(accesscontrol_vals).is_some() { - return Err("sth wrong with access control".to_string()); + if req.extensions_mut().insert(acl_vals).is_some() { + return Err("Something wrong with access control".to_string()); } Ok(true) @@ -58,7 +59,7 @@ async fn fetch_user(auth_url: &str, token: &str) -> Result .header(ACCEPT, "application/json") .send() .await - .map_err(|_err| "no resp from auth server".to_string())?; + .map_err(|_err| "No response from OAuth server".to_string())?; if !resp.status().is_success() { return Err("401 Unauthorized".to_string()); diff --git a/src/models/cloud.rs b/src/models/cloud.rs index 91b494c1..d0fd31bd 100644 --- a/src/models/cloud.rs +++ b/src/models/cloud.rs @@ -14,3 +14,38 @@ pub struct Cloud { pub created_at: DateTime, pub updated_at: DateTime, } + + + +impl std::fmt::Display for Cloud { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let cloud_key: String = match self.cloud_key.as_ref() { + Some(val) => + { + val.chars().take(4).collect::() + "****" + }, + None => "".to_string(), + }; + let cloud_token: String = match self.cloud_token.as_ref() { + Some(val) => { + val.chars().take(4).collect::() + "****" + }, + None => "".to_string(), + }; + + let cloud_secret: String = match self.cloud_secret.as_ref() { + Some(val) => { + val.chars().take(4).collect::() + "****" + } + None => "".to_string(), + }; + + write!(f, "{} cloud creds: cloud_key : {} cloud_token: {} cloud_secret: {} project_id: {:?}", + self.provider, + cloud_key, + cloud_token, + cloud_secret, + self.project_id + ) + } +} diff --git a/src/routes/cloud/add.rs b/src/routes/cloud/add.rs index ca8e7b80..ce4126fa 100644 --- a/src/routes/cloud/add.rs +++ b/src/routes/cloud/add.rs @@ -6,18 +6,16 @@ use crate::db; use actix_web::{post, web, Responder, Result}; use sqlx::PgPool; use std::sync::Arc; +use chrono::Utc; use serde_valid::Validate; +use tracing::Instrument; -// workflow -// add, update, list, get(user_id), ACL, -// ACL - access to func for a user -// ACL - access to objects for a user #[tracing::instrument(name = "Add cloud.")] #[post("")] pub async fn add( user: web::ReqData>, - form: web::Json, + mut form: web::Json, pg_pool: web::Data, ) -> Result { @@ -29,8 +27,10 @@ pub async fn add( return Err(JsonResponse::::build().form_error(errors)); } + form.user_id = Some(user.id.clone()); let mut cloud: models::Cloud = form.deref().into(); - cloud.user_id = user.id.clone(); + cloud.created_at = Utc::now(); + cloud.updated_at = Utc::now(); db::cloud::insert(pg_pool.get_ref(), cloud) .await diff --git a/src/routes/cloud/get.rs b/src/routes/cloud/get.rs index 23af867c..82983985 100644 --- a/src/routes/cloud/get.rs +++ b/src/routes/cloud/get.rs @@ -4,31 +4,34 @@ use crate::helpers::JsonResponse; use crate::models; use actix_web::{get, web, Responder, Result}; use sqlx::PgPool; +use crate::forms::Cloud; +use tracing::Instrument; -// workflow -// add, update, list, get(user_id), ACL, -// ACL - access to func for a user -// ACL - access to objects for a user - -#[tracing::instrument(name = "Get cloud.")] +#[tracing::instrument(name = "Get cloud credentials.")] #[get("/{id}")] pub async fn item( path: web::Path<(i32,)>, + user: web::ReqData>, pg_pool: web::Data, ) -> Result { let id = path.0; - let cloud = db::cloud::fetch(pg_pool.get_ref(), id) + db::cloud::fetch(pg_pool.get_ref(), id) .await .map_err(|_err| JsonResponse::::build() .internal_server_error("")) .and_then(|cloud| { match cloud { - Some(cloud) => { Ok(cloud) }, - None => Err(JsonResponse::::build().not_found("object not found")) + Some(cloud) if cloud.user_id != user.id => { + Err(JsonResponse::not_found("record not found")) + }, + Some(cloud) => { + let cloud = Cloud::decode_model(cloud); + Ok(JsonResponse::build().set_item(Some(cloud)).ok("OK")) + }, + None => Err(JsonResponse::not_found("record not found")), } - })?; + }) - Ok(JsonResponse::build().set_item(cloud).ok("OK")) } #[tracing::instrument(name = "Get all clouds.")] diff --git a/src/routes/cloud/update.rs b/src/routes/cloud/update.rs index aca859c3..3c603603 100644 --- a/src/routes/cloud/update.rs +++ b/src/routes/cloud/update.rs @@ -8,6 +8,7 @@ use sqlx::PgPool; use std::sync::Arc; use tracing::Instrument; use std::ops::Deref; +use chrono::Utc; #[tracing::instrument(name = "Update cloud.")] #[put("/{id}")] @@ -37,6 +38,7 @@ pub async fn item( let mut cloud:models::Cloud = form.deref().into(); cloud.id = cloud_row.id; cloud.user_id = user.id.clone(); + // cloud.updated_at = Utc::now(); tracing::debug!("Updating cloud {:?}", cloud); diff --git a/src/routes/project/compose.rs b/src/routes/project/compose.rs index de52ca03..ca2e414d 100644 --- a/src/routes/project/compose.rs +++ b/src/routes/project/compose.rs @@ -7,7 +7,7 @@ use sqlx::PgPool; use std::sync::Arc; #[tracing::instrument(name = "User's generate docker-compose.")] -#[get("/{id}")] +#[get("/{id}/compose")] pub async fn add( user: web::ReqData>, path: web::Path<(i32,)>, diff --git a/src/routes/server/get.rs b/src/routes/server/get.rs index de33e8f8..3bd5a6f8 100644 --- a/src/routes/server/get.rs +++ b/src/routes/server/get.rs @@ -15,21 +15,24 @@ use sqlx::PgPool; #[get("/{id}")] pub async fn item( path: web::Path<(i32,)>, + user: web::ReqData>, pg_pool: web::Data, ) -> Result { let id = path.0; - let server = db::server::fetch(pg_pool.get_ref(), id) + db::server::fetch(pg_pool.get_ref(), id) .await .map_err(|_err| JsonResponse::::build() .internal_server_error("")) .and_then(|server| { match server { - Some(server) => { Ok(server) }, - None => Err(JsonResponse::::build().not_found("object not found")) + Some(project) if project.user_id != user.id => { + Err(JsonResponse::not_found("not found")) + }, + Some(server) => Ok(JsonResponse::build().set_item(Some(server)).ok("OK")), + None => Err(JsonResponse::not_found("not found")), } - })?; + }) - Ok(JsonResponse::build().set_item(server).ok("OK")) } #[tracing::instrument(name = "Get all servers.")] diff --git a/src/routes/server/update.rs b/src/routes/server/update.rs index e72c2965..b0741259 100644 --- a/src/routes/server/update.rs +++ b/src/routes/server/update.rs @@ -38,8 +38,6 @@ pub async fn item( server.id = server_row.id; server.project_id = server_row.project_id; server.user_id = user.id.clone(); - // exclude - // server.created_at tracing::debug!("Updating server {:?}", server); diff --git a/src/startup.rs b/src/startup.rs index e199afab..6fa0f6cb 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -17,6 +17,7 @@ use crate::middleware; use sqlx::{Pool, Postgres}; use std::net::TcpListener; use tracing_actix_web::TracingLogger; +use redis::Commands; pub async fn run( listener: TcpListener, @@ -41,7 +42,7 @@ pub async fn run( let server = HttpServer::new(move || { App::new() .wrap(TracingLogger::default()) - .wrap(authorization.clone()) + // .wrap(authorization.clone()) .wrap(middleware::authentication::Manager::new()) .wrap(Cors::permissive()) .service( @@ -69,7 +70,6 @@ pub async fn run( .service(crate::routes::project::deploy::item) .service(crate::routes::project::deploy::saved_item) .service(crate::routes::project::compose::add) - .service(crate::routes::project::compose::admin) .service(crate::routes::project::get::item) .service(crate::routes::project::add::item) .service(crate::routes::project::update::item) @@ -80,6 +80,7 @@ pub async fn run( .service( web::scope("/project") .service(crate::routes::project::get::admin_list) + .service(crate::routes::project::compose::admin) ) .service( web::scope("/client") diff --git a/tests/mock_data/cloud.json b/tests/mock_data/cloud.json index 04f74125..3865cd7e 100644 --- a/tests/mock_data/cloud.json +++ b/tests/mock_data/cloud.json @@ -1,9 +1,8 @@ { - "user_id": "hy181TZa4DaabUZWklsrxw", "project_id": 1, "provider": "htz", "cloud_token": "cloud_token_here", - "cloud_key": "cloud_token_here", + "cloud_key": "cloud_key_here", "cloud_secret": "cloud_secret_here", "save_token": true } From ac8ebd5ac04912c00e079c0f9af1e8e81beedac0 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 13:02:48 +0200 Subject: [PATCH 02/18] secure all other cloud cred fields --- src/forms/cloud.rs | 49 ++++++++++++++++++++++++++++++++++++----- src/routes/cloud/get.rs | 2 +- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/forms/cloud.rs b/src/forms/cloud.rs index 8cc1567e..79515c50 100644 --- a/src/forms/cloud.rs +++ b/src/forms/cloud.rs @@ -56,15 +56,39 @@ impl Cloud { } // @todo should be refactored, may be moved to cloud.into() or Secret::from() - pub(crate) fn decode_model(mut cloud: models::Cloud) -> models::Cloud { + pub(crate) fn decode_model(mut cloud: models::Cloud, reveal:bool) -> models::Cloud { let encrypted_value = cloud.cloud_token.clone().unwrap(); let mut secret = Secret::new(); secret.user_id = cloud.user_id.clone(); secret.project_id = cloud.project_id.clone().unwrap(); + secret.field = "cloud_token".to_string(); let cloud_token = Cloud::decode(&mut secret, encrypted_value); - cloud.cloud_token = Some(hide_parts(cloud_token)); + if reveal { + cloud.cloud_token = Some(cloud_token); + } else { + cloud.cloud_token = Some(hide_parts(cloud_token)); + } + + secret.field = "cloud_secret".to_string(); + let encrypted_value = cloud.cloud_secret.clone().unwrap(); + let cloud_secret = Cloud::decode(&mut secret, encrypted_value); + if reveal { + cloud.cloud_secret = Some(cloud_secret); + } else { + cloud.cloud_secret = Some(hide_parts(cloud_secret)); + } + + secret.field = "cloud_key".to_string(); + let encrypted_value = cloud.cloud_key.clone().unwrap(); + let cloud_key = Cloud::decode(&mut secret, encrypted_value); + if reveal { + cloud.cloud_key = Some(cloud_key); + } else { + cloud.cloud_key = Some(hide_parts(cloud_key)); + } + cloud } } @@ -141,14 +165,29 @@ impl Into for &Cloud { } +// on deploy impl Into for models::Cloud { fn into(self) -> Cloud { let mut form = Cloud::default(); form.project_id = self.project_id; form.provider = self.provider; - form.cloud_token = self.cloud_token; - form.cloud_key = self.cloud_key; - form.cloud_secret = self.cloud_secret; + + let mut secret = Secret::new(); + secret.user_id = self.user_id.clone(); + secret.project_id = self.project_id.clone().unwrap(); + + secret.field = "cloud_token".to_string(); + let cloud_token = Cloud::decode(&mut secret, self.cloud_token.unwrap()); + form.cloud_token = Some(cloud_token); + + secret.field = "cloud_key".to_string(); + let cloud_key = Cloud::decode(&mut secret, self.cloud_key.unwrap()); + form.cloud_token = Some(cloud_key); + + secret.field = "cloud_secret".to_string(); + let cloud_secret = Cloud::decode(&mut secret, self.cloud_secret.unwrap()); + form.cloud_secret = Some(cloud_secret); + form.save_token = self.save_token; form diff --git a/src/routes/cloud/get.rs b/src/routes/cloud/get.rs index 82983985..5f0e7f9b 100644 --- a/src/routes/cloud/get.rs +++ b/src/routes/cloud/get.rs @@ -25,7 +25,7 @@ pub async fn item( Err(JsonResponse::not_found("record not found")) }, Some(cloud) => { - let cloud = Cloud::decode_model(cloud); + let cloud = Cloud::decode_model(cloud, false); Ok(JsonResponse::build().set_item(Some(cloud)).ok("OK")) }, None => Err(JsonResponse::not_found("record not found")), From 298bd0aa89c7af196471ce0a3a5c4e18abe632f7 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 13:06:52 +0200 Subject: [PATCH 03/18] little of optimization, get rid of duplcates --- src/models/cloud.rs | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/models/cloud.rs b/src/models/cloud.rs index d0fd31bd..3c2a7521 100644 --- a/src/models/cloud.rs +++ b/src/models/cloud.rs @@ -15,30 +15,18 @@ pub struct Cloud { pub updated_at: DateTime, } - +fn mask_string(s: Option<&String>) -> String { + match s { + Some(val) => val.chars().take(4).collect::() + "****", + None => "".to_string(), + } +} impl std::fmt::Display for Cloud { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let cloud_key: String = match self.cloud_key.as_ref() { - Some(val) => - { - val.chars().take(4).collect::() + "****" - }, - None => "".to_string(), - }; - let cloud_token: String = match self.cloud_token.as_ref() { - Some(val) => { - val.chars().take(4).collect::() + "****" - }, - None => "".to_string(), - }; - - let cloud_secret: String = match self.cloud_secret.as_ref() { - Some(val) => { - val.chars().take(4).collect::() + "****" - } - None => "".to_string(), - }; + let cloud_key = mask_string(self.cloud_key.as_ref()); + let cloud_token = mask_string(self.cloud_token.as_ref()); + let cloud_secret = mask_string(self.cloud_secret.as_ref()); write!(f, "{} cloud creds: cloud_key : {} cloud_token: {} cloud_secret: {} project_id: {:?}", self.provider, From 9246620419795615b0d9013ff2ce3d301db6b653 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 13:13:14 +0200 Subject: [PATCH 04/18] reduce repetition and improving readability --- src/forms/cloud.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/forms/cloud.rs b/src/forms/cloud.rs index 79515c50..debf9758 100644 --- a/src/forms/cloud.rs +++ b/src/forms/cloud.rs @@ -127,6 +127,20 @@ impl std::fmt::Debug for Cloud { } } +fn encrypt_field( + secret: &mut Secret, + field_name: &str, + value: Option, +) -> Option { + if let Some(val) = value { + secret.field = field_name.to_owned(); + if let Ok(encrypted) = secret.encrypt(val) { + return Some(Secret::b64_encode(&encrypted)); + } + } + None +} + impl Into for &Cloud { fn into(self) -> models::Cloud { let mut cloud = models::Cloud::default(); @@ -138,25 +152,9 @@ impl Into for &Cloud { secret.user_id = self.user_id.clone().unwrap(); secret.project_id = self.project_id.unwrap(); - if let Some(val) = self.cloud_token.clone() { - secret.field = "cloud_token".to_owned(); - if let Ok(encrypted) = secret.encrypt(val) { - cloud.cloud_token = Some(Secret::b64_encode(&encrypted)); - }; - } - if let Some(val) = self.cloud_key.clone() { - secret.field = "cloud_key".to_owned(); - if let Ok(encrypted) = secret.encrypt(val) { - // cloud.cloud_token = Some(encrypted.data.iter().map(|&c| c as char).collect::()) - cloud.cloud_key = Some(Secret::b64_encode(&encrypted)); - }; - } - if let Some(val) = self.cloud_secret.clone() { - secret.field = "cloud_secret".to_owned(); - if let Ok(encrypted) = secret.encrypt(val) { - cloud.cloud_secret = Some(Secret::b64_encode(&encrypted)) - }; - } + cloud.cloud_token = encrypt_field(&mut secret, "cloud_token", self.cloud_token.clone()); + cloud.cloud_key = encrypt_field(&mut secret, "cloud_key", self.cloud_key.clone()); + cloud.cloud_secret = encrypt_field(&mut secret, "cloud_secret", self.cloud_secret.clone()); cloud.save_token = self.save_token.clone(); cloud From 6e46901dada60869a67624d02919ad62425b8e91 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 14:08:29 +0200 Subject: [PATCH 05/18] reduce repetition and improving readability --- src/forms/cloud.rs | 48 +++++++++++++++++---------------------- tests/mock_data/README.md | 10 ++++++++ 2 files changed, 31 insertions(+), 27 deletions(-) create mode 100644 tests/mock_data/README.md diff --git a/src/forms/cloud.rs b/src/forms/cloud.rs index debf9758..2cdf6336 100644 --- a/src/forms/cloud.rs +++ b/src/forms/cloud.rs @@ -55,39 +55,33 @@ impl Cloud { } } + pub(crate) fn decrypt_field( + secret: &mut Secret, + field_name: &str, + encrypted_value: Option, + reveal: bool, + ) -> Option { + if let Some(val) = encrypted_value { + secret.field = field_name.to_owned(); + let decoded_value = Cloud::decode(secret, val); + if reveal { + return Some(decoded_value); + } else { + return Some(hide_parts(decoded_value)); + } + } + None + } + // @todo should be refactored, may be moved to cloud.into() or Secret::from() pub(crate) fn decode_model(mut cloud: models::Cloud, reveal:bool) -> models::Cloud { - let encrypted_value = cloud.cloud_token.clone().unwrap(); let mut secret = Secret::new(); secret.user_id = cloud.user_id.clone(); secret.project_id = cloud.project_id.clone().unwrap(); - - secret.field = "cloud_token".to_string(); - let cloud_token = Cloud::decode(&mut secret, encrypted_value); - if reveal { - cloud.cloud_token = Some(cloud_token); - } else { - cloud.cloud_token = Some(hide_parts(cloud_token)); - } - - secret.field = "cloud_secret".to_string(); - let encrypted_value = cloud.cloud_secret.clone().unwrap(); - let cloud_secret = Cloud::decode(&mut secret, encrypted_value); - if reveal { - cloud.cloud_secret = Some(cloud_secret); - } else { - cloud.cloud_secret = Some(hide_parts(cloud_secret)); - } - - secret.field = "cloud_key".to_string(); - let encrypted_value = cloud.cloud_key.clone().unwrap(); - let cloud_key = Cloud::decode(&mut secret, encrypted_value); - if reveal { - cloud.cloud_key = Some(cloud_key); - } else { - cloud.cloud_key = Some(hide_parts(cloud_key)); - } + cloud.cloud_token = Cloud::decrypt_field(&mut secret, "cloud_token", cloud.cloud_token.clone(), reveal); + cloud.cloud_secret = Cloud::decrypt_field(&mut secret, "cloud_secret", cloud.cloud_secret.clone(), reveal); + cloud.cloud_key = Cloud::decrypt_field(&mut secret, "cloud_key", cloud.cloud_key.clone(), reveal); cloud } diff --git a/tests/mock_data/README.md b/tests/mock_data/README.md new file mode 100644 index 00000000..4321e92a --- /dev/null +++ b/tests/mock_data/README.md @@ -0,0 +1,10 @@ +This directory contains mock data for different endpoints + +Here is some examples of making requests using CURL + +Add new cloud credentials +curl -X POST -v http://localhost:8000/cloud -d @cloud.json --header 'Content-Type: application/json' -H "Authorization: Bearer $TD_BEARER" + +Get cloud credentials +curl -X GET http://localhost:8000/cloud/31 --header 'Content-Type: application/json' -H "Authorization: Bearer $TD_BEARER" + From 62ce2d8095a5d48dcb989ed4f51837090763b70c Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 16:25:54 +0200 Subject: [PATCH 06/18] user project list --- src/routes/project/get.rs | 15 +++++++++++++++ src/startup.rs | 1 + 2 files changed, 16 insertions(+) diff --git a/src/routes/project/get.rs b/src/routes/project/get.rs index aa03159c..cc9da9cf 100644 --- a/src/routes/project/get.rs +++ b/src/routes/project/get.rs @@ -27,6 +27,21 @@ pub async fn item( }) } + +#[tracing::instrument(name = "Get project list.")] +#[get("")] +pub async fn list( + user: web::ReqData>, + pg_pool: web::Data, +) -> Result { + db::project::fetch_by_user(pg_pool.get_ref(), &user.id) + .await + .map_err(|err| JsonResponse::internal_server_error(err)) + .map(|projects| JsonResponse::build().set_list(projects).ok("OK")) +} + + +//admin's endpoint #[tracing::instrument(name = "Get user's project list.")] #[get("/user/{id}")] pub async fn admin_list( diff --git a/src/startup.rs b/src/startup.rs index 6fa0f6cb..a66b0e7f 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -70,6 +70,7 @@ pub async fn run( .service(crate::routes::project::deploy::item) .service(crate::routes::project::deploy::saved_item) .service(crate::routes::project::compose::add) + .service(crate::routes::project::get::list) .service(crate::routes::project::get::item) .service(crate::routes::project::add::item) .service(crate::routes::project::update::item) From d1f40b75fea624b96bbf475b2d10f2f2a4a307b5 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 19:37:14 +0200 Subject: [PATCH 07/18] redis creds to .env --- .env | 2 ++ src/forms/project/deploy.rs | 4 +++- src/helpers/cloud/security.rs | 6 +++++- src/routes/project/deploy.rs | 14 +++++++------- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.env b/.env index 1dcbed0b..0f19229d 100644 --- a/.env +++ b/.env @@ -6,3 +6,5 @@ POSTGRES_PASSWORD=postgres POSTGRES_DB=stacker POSTGRES_PORT=5432 SECURITY_KEY=SECURITY_KEY_SHOULD_BE_OF_LEN_32 + +REDIS_URL=redis://127.0.0.1/ \ No newline at end of file diff --git a/src/forms/project/deploy.rs b/src/forms/project/deploy.rs index ae4c5e8e..d0eee093 100644 --- a/src/forms/project/deploy.rs +++ b/src/forms/project/deploy.rs @@ -11,7 +11,9 @@ pub struct Deploy { #[validate] pub(crate) server: Server, #[validate] - pub(crate) cloud: Cloud + pub(crate) cloud: Cloud, + // pub user_id: Option, + // pub project_id: Option } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] diff --git a/src/helpers/cloud/security.rs b/src/helpers/cloud/security.rs index 29b997cf..af1e2569 100644 --- a/src/helpers/cloud/security.rs +++ b/src/helpers/cloud/security.rs @@ -27,7 +27,11 @@ impl Secret { } #[tracing::instrument(name = "Secret::connect_storage")] fn connect_storage() -> Connection { - match redis::Client::open("redis://127.0.0.1/"){ + + let storage_url = std::env::var("REDIS_URL") + .or_default("redis://127.0.0.1/".to_string()); + + match redis::Client::open(storage_url){ Ok(client) => { match client.get_connection() { Ok(connection) => connection, diff --git a/src/routes/project/deploy.rs b/src/routes/project/deploy.rs index 7cdbb437..b1914651 100644 --- a/src/routes/project/deploy.rs +++ b/src/routes/project/deploy.rs @@ -17,7 +17,7 @@ use crate::helpers::compressor::compress; pub async fn item( user: web::ReqData>, path: web::Path<(i32,)>, - form: web::Json, + mut form: web::Json, pg_pool: Data, mq_manager: Data, sets: Data, @@ -49,10 +49,10 @@ pub async fn item( JsonResponse::::build().internal_server_error(err) })?; + form.cloud.user_id = Some(user.id.clone()); + form.cloud.project_id = Some(id); // Save cloud credentials if requested let mut cloud_creds: models::Cloud = (&form.cloud).into(); - cloud_creds.project_id = Some(id); - cloud_creds.user_id = user.id.clone(); if Some(true) == cloud_creds.save_token { db::cloud::insert(pg_pool.get_ref(), cloud_creds.clone()) @@ -105,7 +105,7 @@ pub async fn item( }); tracing::debug!("Save deployment result: {:?}", result); - tracing::debug!("Send project data <<<<<<<<<<<>>>>>>>>>>>>>>>>{:?}", payload); + tracing::debug!("Send project data <<<>>>{:?}", payload); // Send Payload mq_manager @@ -134,7 +134,7 @@ pub async fn saved_item( ) -> Result { let id = path.0; let cloud_id = path.1; - //let cloud_id = Some(1); + tracing::debug!("User {:?} is deploying project: {} to cloud: {} ", user, id, cloud_id); // Validate project @@ -173,7 +173,7 @@ pub async fn saved_item( // if let Some(server) = server.into_iter().nth(0) { // server // } - server.into_iter().nth(0).unwrap() // @todo refactoring is required + server.into_iter().nth(0).unwrap() //@todo refactoring is required for multiple types } Err(_e) => { return Err(JsonResponse::::build().not_found("No servers configured")); @@ -210,7 +210,7 @@ pub async fn saved_item( }); tracing::debug!("Save deployment result: {:?}", result); - tracing::debug!("Send project data <<<<<<<<<<<>>>>>>>>>>>>>>>>{:?}", payload); + tracing::debug!("Send project data <<<>>>{:?}", payload); // Send Payload mq_manager From 330732decc3873f3d73c809b8803493abcecc051 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 27 Mar 2024 19:44:41 +0200 Subject: [PATCH 08/18] fix unwrap_or redis creds to .env --- src/helpers/cloud/security.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/cloud/security.rs b/src/helpers/cloud/security.rs index af1e2569..3c9d6b53 100644 --- a/src/helpers/cloud/security.rs +++ b/src/helpers/cloud/security.rs @@ -29,7 +29,7 @@ impl Secret { fn connect_storage() -> Connection { let storage_url = std::env::var("REDIS_URL") - .or_default("redis://127.0.0.1/".to_string()); + .unwrap_or("redis://127.0.0.1/".to_string()); match redis::Client::open(storage_url){ Ok(client) => { From 82a9e1f9582ab9c251de9562bdaede30db61c9e9 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sat, 30 Mar 2024 20:00:46 +0200 Subject: [PATCH 09/18] server updates --- src/db/cloud.rs | 9 ++- src/db/server.rs | 6 +- src/forms/cloud.rs | 134 +++++++++++++++++++++-------------- src/forms/project/deploy.rs | 6 +- src/forms/project/payload.rs | 2 +- src/forms/server.rs | 12 ++++ src/models/cloud.rs | 43 ++++++++++- src/models/server.rs | 2 +- src/models/user.rs | 2 - src/routes/cloud/add.rs | 6 +- src/routes/cloud/get.rs | 16 ++++- src/routes/cloud/update.rs | 2 +- src/routes/project/deploy.rs | 59 +++++++++++++-- src/routes/project/update.rs | 3 +- 14 files changed, 213 insertions(+), 89 deletions(-) diff --git a/src/db/cloud.rs b/src/db/cloud.rs index a5e71c2c..77c840e5 100644 --- a/src/db/cloud.rs +++ b/src/db/cloud.rs @@ -55,8 +55,9 @@ pub async fn insert(pool: &PgPool, mut cloud: models::Cloud) -> Result Result Result Result(value: &Option, s: S) -> Result -// where -// S: Serializer, -// { -// eprintln!("value in serde {:?}", value); -// let result: &str = match value { -// Some(value) => { -// let value = value.as_str(); -// value -// // value.into_iter().take(6).collect::() -// } -// None => "", -// }; -// s.serialize_str(result) -// } fn hide_parts(value: String) -> String { value.chars().into_iter().take(6).collect::() + "****" } - #[derive(Default, Clone, PartialEq, Serialize, Deserialize, Validate)] -pub struct Cloud { +pub struct CloudForm { pub user_id: Option, pub project_id: Option, #[validate(min_length = 2)] #[validate(max_length = 50)] pub provider: String, - // #[serde(serialize_with = "hide_part")] pub cloud_token: Option, - // #[serde(serialize_with = "hide_part")] pub cloud_key: Option, - // #[serde(serialize_with = "hide_part")] pub cloud_secret: Option, pub save_token: Option, } -impl Cloud { +impl CloudForm { + #[tracing::instrument(name = "impl CloudForm::decode()")] pub(crate) fn decode(secret: &mut Secret, encrypted_value: String) -> String { // tracing::error!("encrypted_value {:?}", &encrypted_value); let b64_decoded = Secret::b64_decode(&encrypted_value).unwrap(); + // tracing::error!("decoded {:?}", &b64_decoded); match secret.decrypt(b64_decoded) { Ok(decoded) => decoded, Err(_err) => { @@ -63,7 +47,7 @@ impl Cloud { ) -> Option { if let Some(val) = encrypted_value { secret.field = field_name.to_owned(); - let decoded_value = Cloud::decode(secret, val); + let decoded_value = CloudForm::decode(secret, val); if reveal { return Some(decoded_value); } else { @@ -74,20 +58,21 @@ impl Cloud { } // @todo should be refactored, may be moved to cloud.into() or Secret::from() - pub(crate) fn decode_model(mut cloud: models::Cloud, reveal:bool) -> models::Cloud { + #[tracing::instrument(name = "decode_model")] + pub fn decode_model(mut cloud: models::Cloud, reveal:bool) -> models::Cloud { let mut secret = Secret::new(); secret.user_id = cloud.user_id.clone(); secret.project_id = cloud.project_id.clone().unwrap(); - cloud.cloud_token = Cloud::decrypt_field(&mut secret, "cloud_token", cloud.cloud_token.clone(), reveal); - cloud.cloud_secret = Cloud::decrypt_field(&mut secret, "cloud_secret", cloud.cloud_secret.clone(), reveal); - cloud.cloud_key = Cloud::decrypt_field(&mut secret, "cloud_key", cloud.cloud_key.clone(), reveal); + cloud.cloud_token = CloudForm::decrypt_field(&mut secret, "cloud_token", cloud.cloud_token.clone(), reveal); + cloud.cloud_secret = CloudForm::decrypt_field(&mut secret, "cloud_secret", cloud.cloud_secret.clone(), reveal); + cloud.cloud_key = CloudForm::decrypt_field(&mut secret, "cloud_key", cloud.cloud_key.clone(), reveal); cloud } } -impl std::fmt::Debug for Cloud { +impl std::fmt::Debug for CloudForm { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let cloud_key: String = match self.cloud_key.as_ref() { Some(val) => @@ -135,22 +120,30 @@ fn encrypt_field( None } -impl Into for &Cloud { +impl Into for &CloudForm { + #[tracing::instrument(name = "impl Into for &CloudForm")] fn into(self) -> models::Cloud { let mut cloud = models::Cloud::default(); cloud.provider = self.provider.clone(); cloud.user_id = self.user_id.clone().unwrap(); cloud.project_id = self.project_id; - let mut secret = Secret::new(); - secret.user_id = self.user_id.clone().unwrap(); - secret.project_id = self.project_id.unwrap(); - - cloud.cloud_token = encrypt_field(&mut secret, "cloud_token", self.cloud_token.clone()); - cloud.cloud_key = encrypt_field(&mut secret, "cloud_key", self.cloud_key.clone()); - cloud.cloud_secret = encrypt_field(&mut secret, "cloud_secret", self.cloud_secret.clone()); + if Some(true) == self.save_token { + let mut secret = Secret::new(); + secret.user_id = self.user_id.clone().unwrap(); + secret.project_id = self.project_id.unwrap(); + + cloud.cloud_token = encrypt_field(&mut secret, "cloud_token", self.cloud_token.clone()); + cloud.cloud_key = encrypt_field(&mut secret, "cloud_key", self.cloud_key.clone()); + cloud.cloud_secret = encrypt_field(&mut secret, "cloud_secret", self.cloud_secret.clone()); + } else { + cloud.cloud_token = self.cloud_token.clone(); + cloud.cloud_key = self.cloud_key.clone(); + cloud.cloud_secret = self.cloud_secret.clone(); + } cloud.save_token = self.save_token.clone(); - + cloud.created_at = Utc::now(); + cloud.updated_at = Utc::now(); cloud } @@ -158,30 +151,61 @@ impl Into for &Cloud { // on deploy -impl Into for models::Cloud { - fn into(self) -> Cloud { - let mut form = Cloud::default(); +impl Into for models::Cloud { + #[tracing::instrument(name = "Into for models::Cloud .")] + fn into(self) -> CloudForm { + let mut form = CloudForm::default(); form.project_id = self.project_id; form.provider = self.provider; - let mut secret = Secret::new(); - secret.user_id = self.user_id.clone(); - secret.project_id = self.project_id.clone().unwrap(); - - secret.field = "cloud_token".to_string(); - let cloud_token = Cloud::decode(&mut secret, self.cloud_token.unwrap()); - form.cloud_token = Some(cloud_token); - - secret.field = "cloud_key".to_string(); - let cloud_key = Cloud::decode(&mut secret, self.cloud_key.unwrap()); - form.cloud_token = Some(cloud_key); - - secret.field = "cloud_secret".to_string(); - let cloud_secret = Cloud::decode(&mut secret, self.cloud_secret.unwrap()); - form.cloud_secret = Some(cloud_secret); + if Some(true) == self.save_token { + let mut secret = Secret::new(); + secret.user_id = self.user_id.clone(); + secret.project_id = self.project_id.unwrap(); + secret.field = "cloud_token".to_string(); + + let value = match self.cloud_token { + Some(value) => { + CloudForm::decode(&mut secret, value) + } + None => { + tracing::debug!("Skip {}", secret.field); + "".to_string() + } + }; + form.cloud_token = Some(value); + + secret.field = "cloud_key".to_string(); + let value = match self.cloud_key { + Some(value) => { + CloudForm::decode(&mut secret, value) + } + None => { + tracing::debug!("Skipp {}", secret.field); + "".to_string() + } + }; + form.cloud_key = Some(value); + + secret.field = "cloud_secret".to_string(); + let value = match self.cloud_secret { + Some(value) => { + CloudForm::decode(&mut secret, value) + } + None => { + tracing::debug!("Skipp {}", secret.field); + "".to_string() + } + }; + form.cloud_secret = Some(value); + + } else { + form.cloud_token = self.cloud_token; + form.cloud_key = self.cloud_key; + form.cloud_secret = self.cloud_secret; + } form.save_token = self.save_token; - form } } diff --git a/src/forms/project/deploy.rs b/src/forms/project/deploy.rs index d0eee093..86c1453f 100644 --- a/src/forms/project/deploy.rs +++ b/src/forms/project/deploy.rs @@ -2,7 +2,7 @@ use serde_derive::{Deserialize, Serialize}; use serde_json::Value; use serde_valid::Validate; use crate::forms; -use crate::forms::{Cloud, Server}; +use crate::forms::{CloudForm, Server}; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] pub struct Deploy { @@ -11,9 +11,7 @@ pub struct Deploy { #[validate] pub(crate) server: Server, #[validate] - pub(crate) cloud: Cloud, - // pub user_id: Option, - // pub project_id: Option + pub(crate) cloud: CloudForm, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] diff --git a/src/forms/project/payload.rs b/src/forms/project/payload.rs index d7e84d4b..5eb115a5 100644 --- a/src/forms/project/payload.rs +++ b/src/forms/project/payload.rs @@ -12,7 +12,7 @@ pub struct Payload { pub(crate) user_token: Option, pub(crate) user_email: Option, #[serde(flatten)] - pub cloud: Option, + pub cloud: Option, #[serde(flatten)] pub server: Option, #[serde(flatten)] diff --git a/src/forms/server.rs b/src/forms/server.rs index cba997f9..8b78c14e 100644 --- a/src/forms/server.rs +++ b/src/forms/server.rs @@ -29,6 +29,18 @@ impl Into for &Server { } } +// impl From for models::Server { +// fn from(self, value: Server) -> &mut Self { +// self.disk_type = value.disk_type.clone(); +// self.region = value.region.clone(); +// self.server = value.server.clone(); +// self.zone = value.zone.clone(); +// self.os = value.os.clone(); +// self.updated_at = Utc::now(); +// self +// } +// } + impl Into for models::Server { fn into(self) -> Server { diff --git a/src/models/cloud.rs b/src/models/cloud.rs index 3c2a7521..77c3224a 100644 --- a/src/models/cloud.rs +++ b/src/models/cloud.rs @@ -1,7 +1,7 @@ use chrono::{DateTime, Utc}; use serde_derive::{Deserialize, Serialize}; -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Cloud { pub id: i32, pub user_id: String, @@ -37,3 +37,44 @@ impl std::fmt::Display for Cloud { ) } } + +impl Cloud { + pub fn new(user_id: String, + project_id: Option, + provider: String, + cloud_token: Option, + cloud_key: Option, + cloud_secret: Option, + save_token: Option + ) -> Self { + Self { + id: 0, + user_id, + project_id, + provider, + cloud_token, + cloud_key, + cloud_secret, + save_token, + created_at: Utc::now(), + updated_at: Utc::now(), + } + } +} + +impl Default for Cloud { + fn default() -> Self { + Cloud { + id: 0, + provider: "".to_string(), + user_id: "".to_string(), + project_id: Default::default(), + cloud_key: Default::default(), + cloud_token: Default::default(), + cloud_secret: Default::default(), + save_token: Some(false), + created_at: Default::default(), + updated_at: Default::default(), + } + } +} diff --git a/src/models/server.rs b/src/models/server.rs index 3827fcf3..3e575a1a 100644 --- a/src/models/server.rs +++ b/src/models/server.rs @@ -24,4 +24,4 @@ pub struct Server { pub disk_type: Option, pub created_at: DateTime, pub updated_at: DateTime, -} +} \ No newline at end of file diff --git a/src/models/user.rs b/src/models/user.rs index 8217ac80..21111431 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -7,6 +7,4 @@ pub struct User { pub last_name: String, pub email: String, pub email_confirmed: bool, - // pub phone: Option, - // pub website: Option, } diff --git a/src/routes/cloud/add.rs b/src/routes/cloud/add.rs index ce4126fa..ebd261e5 100644 --- a/src/routes/cloud/add.rs +++ b/src/routes/cloud/add.rs @@ -15,7 +15,7 @@ use tracing::Instrument; #[post("")] pub async fn add( user: web::ReqData>, - mut form: web::Json, + mut form: web::Json, pg_pool: web::Data, ) -> Result { @@ -28,9 +28,7 @@ pub async fn add( } form.user_id = Some(user.id.clone()); - let mut cloud: models::Cloud = form.deref().into(); - cloud.created_at = Utc::now(); - cloud.updated_at = Utc::now(); + let cloud: models::Cloud = form.deref().into(); db::cloud::insert(pg_pool.get_ref(), cloud) .await diff --git a/src/routes/cloud/get.rs b/src/routes/cloud/get.rs index 5f0e7f9b..43ac8012 100644 --- a/src/routes/cloud/get.rs +++ b/src/routes/cloud/get.rs @@ -4,7 +4,7 @@ use crate::helpers::JsonResponse; use crate::models; use actix_web::{get, web, Responder, Result}; use sqlx::PgPool; -use crate::forms::Cloud; +use crate::forms::CloudForm; use tracing::Instrument; #[tracing::instrument(name = "Get cloud credentials.")] @@ -25,7 +25,7 @@ pub async fn item( Err(JsonResponse::not_found("record not found")) }, Some(cloud) => { - let cloud = Cloud::decode_model(cloud, false); + let cloud = CloudForm::decode_model(cloud, false); Ok(JsonResponse::build().set_item(Some(cloud)).ok("OK")) }, None => Err(JsonResponse::not_found("record not found")), @@ -43,6 +43,16 @@ pub async fn list( ) -> Result { db::cloud::fetch_by_user(pg_pool.get_ref(), user.id.as_ref()) .await - .map(|clouds| JsonResponse::build().set_list(clouds).ok("OK")) + .map(|clouds| { + + let clouds = clouds + .into_iter() + .map(|cloud| CloudForm::decode_model(cloud, false) ) + // .map_err(|e| tracing::error!("Failed to decode cloud, {:?}", e)) + .collect(); + + JsonResponse::build().set_list(clouds).ok("OK") + + }) .map_err(|_err| JsonResponse::::build().internal_server_error("")) } diff --git a/src/routes/cloud/update.rs b/src/routes/cloud/update.rs index 3c603603..7672f551 100644 --- a/src/routes/cloud/update.rs +++ b/src/routes/cloud/update.rs @@ -14,7 +14,7 @@ use chrono::Utc; #[put("/{id}")] pub async fn item( path: web::Path<(i32,)>, - form: web::Json, + form: web::Json, user: web::ReqData>, pg_pool: Data, ) -> Result { diff --git a/src/routes/project/deploy.rs b/src/routes/project/deploy.rs index b1914651..3a9bea3d 100644 --- a/src/routes/project/deploy.rs +++ b/src/routes/project/deploy.rs @@ -9,6 +9,7 @@ use sqlx::PgPool; use std::sync::Arc; use serde_valid::Validate; use crate::helpers::compressor::compress; +use chrono::{Utc}; @@ -52,7 +53,9 @@ pub async fn item( form.cloud.user_id = Some(user.id.clone()); form.cloud.project_id = Some(id); // Save cloud credentials if requested - let mut cloud_creds: models::Cloud = (&form.cloud).into(); + let cloud_creds: models::Cloud = (&form.cloud).into(); + + // let cloud_creds = forms::Cloud::decode_model(cloud_creds, false); if Some(true) == cloud_creds.save_token { db::cloud::insert(pg_pool.get_ref(), cloud_creds.clone()) @@ -127,6 +130,7 @@ pub async fn item( #[post("/{id}/deploy/{cloud_id}")] pub async fn saved_item( user: web::ReqData>, + mut form: web::Json, path: web::Path<(i32, i32)>, pg_pool: Data, mq_manager: Data, @@ -137,6 +141,14 @@ pub async fn saved_item( tracing::debug!("User {:?} is deploying project: {} to cloud: {} ", user, id, cloud_id); + if !form.validate().is_ok() { + let errors = form.validate().unwrap_err().to_string(); + let err_msg = format!("Invalid form data received {:?}", &errors); + tracing::debug!(err_msg); + + return Err(JsonResponse::::build().form_error(errors)); + } + // Validate project let project = db::project::fetch(pg_pool.get_ref(), id) .await @@ -169,26 +181,56 @@ pub async fn saved_item( let server = match db::server::fetch_by_project(pg_pool.get_ref(), dc.project.id.clone()).await { Ok(server) => { - // for now we support only one type of servers - // if let Some(server) = server.into_iter().nth(0) { - // server - // } - server.into_iter().nth(0).unwrap() //@todo refactoring is required for multiple types + // currently we support only one type of servers + //@todo multiple server types support + match server.into_iter().nth(0) { + Some(mut server) => { + // new updates + server.disk_type = form.server.disk_type.clone(); + server.region = form.server.region.clone(); + server.server = form.server.server.clone(); + server.zone = form.server.zone.clone(); + server.os = form.server.os.clone(); + server.user_id = user.id.clone(); + server.project_id = id; + server + }, + None => { + // Create new server + let mut server: models::Server = (&form.server).into(); + server.user_id = user.id.clone(); + server.project_id = id; + db::server::insert(pg_pool.get_ref(), server) + .await + .map(|server| server) + .map_err(|_| { + JsonResponse::::build().internal_server_error("Internal Server Error") + })? + } + } } Err(_e) => { return Err(JsonResponse::::build().not_found("No servers configured")); } }; + let server = db::server::update(pg_pool.get_ref(), server) + .await + .map(|server| server) + .map_err(|_| { + JsonResponse::::build().internal_server_error("Internal Server Error") + })?; + + // Building Payload for the 3-d party service through RabbitMQ // let mut payload = forms::project::Payload::default(); let mut payload = forms::project::Payload::try_from(&dc.project) .map_err(|err| JsonResponse::::build().bad_request(err))?; payload.server = Some(server.into()); payload.cloud = Some(cloud.into()); + payload.stack = form.stack.clone().into(); payload.user_token = Some(user.id.clone()); payload.user_email = Some(user.email.clone()); - // let compressed = fc.unwrap_or("".to_string()); payload.docker_compose = Some(compress(fc.as_str())); // Store deployment attempts into deployment table in db @@ -228,3 +270,6 @@ pub async fn saved_item( }) } + + + diff --git a/src/routes/project/update.rs b/src/routes/project/update.rs index 3e1bb19e..cdd532e6 100644 --- a/src/routes/project/update.rs +++ b/src/routes/project/update.rs @@ -42,7 +42,8 @@ pub async fn item( let project_name = form.custom.custom_stack_code.clone(); if Ok(false) == form.is_readable_docker_image().await { - return Err(JsonResponse::::build().bad_request("Can not access docker image")); + return Err(JsonResponse::::build() + .bad_request("Can not access docker image")); } let body: Value = serde_json::to_value::(form) From 865882d744c13d129bd87d5df3bca4b15c0e2d3a Mon Sep 17 00:00:00 2001 From: vsilent Date: Sat, 30 Mar 2024 20:41:27 +0200 Subject: [PATCH 10/18] enable casbin, use security key from env --- src/helpers/cloud/security.rs | 19 +++++++++---------- src/startup.rs | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/helpers/cloud/security.rs b/src/helpers/cloud/security.rs index 3c9d6b53..2e389940 100644 --- a/src/helpers/cloud/security.rs +++ b/src/helpers/cloud/security.rs @@ -84,12 +84,12 @@ impl Secret { #[tracing::instrument(name = "encrypt.")] pub fn encrypt(&self, token: String) -> Result, String> { - // let sec_key = std::env::var("SECURITY_KEY") - // .expect("SECURITY_KEY environment variable is not set") - // .as_bytes(); - let sec_key = "SECURITY_KEY_SHOULD_BE_OF_LEN_32".as_bytes(); + let sec_key = std::env::var("SECURITY_KEY") + .expect("SECURITY_KEY environment variable is not set") + .clone(); + // let key = Aes256Gcm::generate_key(OsRng); - let key: &Key:: = Key::::from_slice(&sec_key); + let key: &Key:: = Key::::from_slice(&sec_key.as_bytes()); // eprintln!("encrypt key {key:?}"); // eprintln!("encrypt: from slice key {key:?}"); let cipher = Aes256Gcm::new(&key); @@ -113,11 +113,10 @@ impl Secret { #[tracing::instrument(name = "decrypt.")] pub fn decrypt(&mut self, encrypted_data: Vec) -> Result { - let sec_key = "SECURITY_KEY_SHOULD_BE_OF_LEN_32".as_bytes(); - // let sec_key = std::env::var("SECURITY_KEY") - // .expect("SECURITY_KEY environment variable is not set") - // .as_bytes(); - let key: &Key:: = Key::::from_slice(&sec_key); + let sec_key = std::env::var("SECURITY_KEY") + .expect("SECURITY_KEY environment variable is not set") + .clone(); + let key: &Key:: = Key::::from_slice(&sec_key.as_bytes()); // eprintln!("decrypt: Key str {key:?}"); let rkey = format!("{}_{}_{}", self.user_id, self.project_id, self.field); eprintln!("decrypt: Key str {rkey:?}"); diff --git a/src/startup.rs b/src/startup.rs index a66b0e7f..302070d0 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -42,7 +42,7 @@ pub async fn run( let server = HttpServer::new(move || { App::new() .wrap(TracingLogger::default()) - // .wrap(authorization.clone()) + .wrap(authorization.clone()) .wrap(middleware::authentication::Manager::new()) .wrap(Cors::permissive()) .service( From 45839648c4356f4433c2708091bc97ab35e9cf79 Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 1 Apr 2024 13:34:53 +0300 Subject: [PATCH 11/18] initial casbin rules --- docker-compose.yml | 2 +- docker/dev/docker-compose.yml | 2 +- ...240401103123_casbin_initial_rules.down.sql | 1 + ...20240401103123_casbin_initial_rules.up.sql | 30 +++++++++++++++++++ src/console/commands/appclient/new.rs | 1 + src/forms/user.rs | 2 ++ .../authentication/method/f_oauth.rs | 7 +++-- src/models/user.rs | 1 + src/routes/project/deploy.rs | 1 + 9 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 migrations/20240401103123_casbin_initial_rules.down.sql create mode 100644 migrations/20240401103123_casbin_initial_rules.up.sql diff --git a/docker-compose.yml b/docker-compose.yml index 4ba581c1..808216e8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ volumes: services: stacker: - image: trydirect/stacker:0.0.7 + image: trydirect/stacker:0.0.8 build: . container_name: stacker restart: always diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index eeac0386..af707ba0 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -16,7 +16,7 @@ networks: services: stacker: - image: trydirect/stacker:0.0.7 + image: trydirect/stacker:0.0.8 build: . container_name: stacker restart: always diff --git a/migrations/20240401103123_casbin_initial_rules.down.sql b/migrations/20240401103123_casbin_initial_rules.down.sql new file mode 100644 index 00000000..d2f607c5 --- /dev/null +++ b/migrations/20240401103123_casbin_initial_rules.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/migrations/20240401103123_casbin_initial_rules.up.sql b/migrations/20240401103123_casbin_initial_rules.up.sql new file mode 100644 index 00000000..cbd06691 --- /dev/null +++ b/migrations/20240401103123_casbin_initial_rules.up.sql @@ -0,0 +1,30 @@ +-- Add up migration script here +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (1, 'p', 'group_admin', '/client', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (2, 'g', 'admin_petru', 'group_admin', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (3, 'g', 'user_petru', 'group_user', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (4, 'p', 'group_user', '/client/:id/disable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (5, 'p', 'group_admin', '/admin/client/:id/disable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (6, 'p', 'group_admin', '/admin/client/:id/enable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (7, 'p', 'group_user', '/client/:id/enable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (8, 'p', 'group_user', '/client/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (9, 'p', 'group_admin', '/admin/client/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (10, 'g', 'anonym', 'group_anonymous', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (11, 'p', 'group_anonymous', '/health_check', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (12, 'p', 'group_anonymous', '/rating/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (13, 'p', 'group_user', '/rating/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (14, 'p', 'group_admin', '/rating/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (15, 'p', 'group_user', '/rating', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (16, 'p', 'group_admin', '/rating', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (17, 'p', 'group_anonymous', '/rating', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (18, 'p', 'group_user', '/rating', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (19, 'p', 'group_admin', '/admin/project/user/:userid', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (21, 'p', 'group_user', '/project', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (33, 'g', 'group_admin', 'group_anonymous', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (34, 'g', 'group_user', 'group_anonymous', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (37, 'p', 'group_user', '/project/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (36, 'p', 'group_user', '/project/:id/compose', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (38, 'p', 'group_user', '/project/:id/deploy', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (39, 'p', 'group_user', '/project', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (40, 'g', 'user', 'group_user', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (41, 'p', 'group_user', '/project/:id/deploy/:cloud_id', 'POST', '', '', ''); + diff --git a/src/console/commands/appclient/new.rs b/src/console/commands/appclient/new.rs index ada01acb..accbd765 100644 --- a/src/console/commands/appclient/new.rs +++ b/src/console/commands/appclient/new.rs @@ -31,6 +31,7 @@ impl crate::console::commands::CallableTrait for NewCommand { last_name: "last_name".to_string(), email: "email".to_string(), email_confirmed: true, + role: "role".to_string() }; crate::routes::client::add_handler_inner(&user.id, settings, db_pool).await?; diff --git a/src/forms/user.rs b/src/forms/user.rs index 5a7fa48f..0c58d75f 100644 --- a/src/forms/user.rs +++ b/src/forms/user.rs @@ -56,6 +56,7 @@ pub struct User { pub deployments_left: Value, #[serde(rename = "suspension_hints")] pub suspension_hints: Option, + pub role: String } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -137,6 +138,7 @@ impl TryInto for UserForm { last_name: self.user.last_name.unwrap_or("Noname".to_string()), email: self.user.email, email_confirmed: self.user.email_confirmed, + role: self.user.role }) } diff --git a/src/middleware/authentication/method/f_oauth.rs b/src/middleware/authentication/method/f_oauth.rs index 8400e81d..cec47b4c 100644 --- a/src/middleware/authentication/method/f_oauth.rs +++ b/src/middleware/authentication/method/f_oauth.rs @@ -14,7 +14,8 @@ fn try_extract_token(authentication: String) -> Result { } let token = authentication_parts.next(); if token.is_none() { - return Err("Empty bearer token".to_string()); + tracing::error!("Bearer token is missing"); + return Err("Authentication required".to_string()); } Ok(token.unwrap().into()) @@ -33,9 +34,9 @@ pub async fn try_oauth(req: &mut ServiceRequest) -> Result { .await .map_err(|err| format!("{err}"))?; - // println!("user ================== {}", user.id.clone()); + // control access using user role let acl_vals = actix_casbin_auth::CasbinVals { - subject: user.id.clone(), + subject: user.role.clone(), domain: None, }; diff --git a/src/models/user.rs b/src/models/user.rs index 21111431..0f6b1efd 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -6,5 +6,6 @@ pub struct User { pub first_name: String, pub last_name: String, pub email: String, + pub role: String, pub email_confirmed: bool, } diff --git a/src/routes/project/deploy.rs b/src/routes/project/deploy.rs index 3a9bea3d..661115e0 100644 --- a/src/routes/project/deploy.rs +++ b/src/routes/project/deploy.rs @@ -197,6 +197,7 @@ pub async fn saved_item( }, None => { // Create new server + // form.update_with(server.into()); let mut server: models::Server = (&form.server).into(); server.user_id = user.id.clone(); server.project_id = id; From 3e50dcbe3a3c10f8072432cd766697e9d6980143 Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 1 Apr 2024 13:38:22 +0300 Subject: [PATCH 12/18] initial casbin rules --- docker/dev/docker-compose.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml index af707ba0..6f8c0aba 100644 --- a/docker/dev/docker-compose.yml +++ b/docker/dev/docker-compose.yml @@ -4,7 +4,7 @@ volumes: stackerdb: driver: local - redis-data: + stacker-redis-data: driver: local networks: @@ -13,6 +13,7 @@ networks: name: backend external: true + services: stacker: @@ -32,7 +33,7 @@ services: - ./.env environment: - RUST_LOG=debug - - RUST_BACKTRACE=1 + - RUST_BACKTRACE=full depends_on: stackerdb: condition: service_healthy @@ -79,14 +80,14 @@ services: networks: - backend - redis: + stackerredis: container_name: stackerredis image: redis:latest restart: always ports: - - 6379 + - 127.0.0.1:6379:6379 volumes: - - redis-data:/data + - stacker-redis-data:/data # - ./redis/rc.local:/etc/rc.local # - ./redis/redis.conf:/usr/local/etc/redis/redis.conf sysctls: From 820f52232703b3e7464c0e1fd618db437f660b92 Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 1 Apr 2024 22:13:45 +0300 Subject: [PATCH 13/18] remove project_id from cloud, as cloud keys can be use across projects --- Dockerfile | 5 -- ...20240401103123_casbin_initial_rules.up.sql | 70 +++++++++++-------- ...4313_remove_project_id_from_cloud.down.sql | 2 + ...184313_remove_project_id_from_cloud.up.sql | 3 + src/console/commands/appclient/new.rs | 2 +- src/db/cloud.rs | 16 ++--- src/forms/cloud.rs | 11 ++- src/forms/project/deploy.rs | 4 +- src/forms/project/payload.rs | 2 +- src/forms/server.rs | 22 ++---- src/helpers/cloud/security.rs | 8 +-- .../authentication/method/f_oauth.rs | 1 + src/models/cloud.rs | 7 +- src/routes/project/deploy.rs | 4 +- src/routes/server/update.rs | 2 +- 15 files changed, 76 insertions(+), 83 deletions(-) create mode 100644 migrations/20240401184313_remove_project_id_from_cloud.down.sql create mode 100644 migrations/20240401184313_remove_project_id_from_cloud.up.sql diff --git a/Dockerfile b/Dockerfile index 66222d1e..8d969e75 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,11 +31,6 @@ COPY ./src ./src #RUN sqlx migrate run #RUN cargo sqlx prepare -- --bin stacker -# rebuild app with project source -#RUN rm -rf ./target/release/deps/server*; \ -# apt-get install --no-install-recommends -y libssl-dev; \ -# cargo build --release - RUN apt-get update && apt-get install --no-install-recommends -y libssl-dev; \ cargo build --bin=console && cargo build --release diff --git a/migrations/20240401103123_casbin_initial_rules.up.sql b/migrations/20240401103123_casbin_initial_rules.up.sql index cbd06691..effa7035 100644 --- a/migrations/20240401103123_casbin_initial_rules.up.sql +++ b/migrations/20240401103123_casbin_initial_rules.up.sql @@ -1,30 +1,42 @@ --- Add up migration script here -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (1, 'p', 'group_admin', '/client', 'POST', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (2, 'g', 'admin_petru', 'group_admin', '', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (3, 'g', 'user_petru', 'group_user', '', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (4, 'p', 'group_user', '/client/:id/disable', 'PUT', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (5, 'p', 'group_admin', '/admin/client/:id/disable', 'PUT', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (6, 'p', 'group_admin', '/admin/client/:id/enable', 'PUT', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (7, 'p', 'group_user', '/client/:id/enable', 'PUT', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (8, 'p', 'group_user', '/client/:id', 'PUT', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (9, 'p', 'group_admin', '/admin/client/:id', 'PUT', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (10, 'g', 'anonym', 'group_anonymous', '', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (11, 'p', 'group_anonymous', '/health_check', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (12, 'p', 'group_anonymous', '/rating/:id', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (13, 'p', 'group_user', '/rating/:id', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (14, 'p', 'group_admin', '/rating/:id', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (15, 'p', 'group_user', '/rating', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (16, 'p', 'group_admin', '/rating', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (17, 'p', 'group_anonymous', '/rating', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (18, 'p', 'group_user', '/rating', 'POST', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (19, 'p', 'group_admin', '/admin/project/user/:userid', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (21, 'p', 'group_user', '/project', 'POST', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (33, 'g', 'group_admin', 'group_anonymous', '', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (34, 'g', 'group_user', 'group_anonymous', '', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (37, 'p', 'group_user', '/project/:id', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (36, 'p', 'group_user', '/project/:id/compose', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (38, 'p', 'group_user', '/project/:id/deploy', 'POST', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (39, 'p', 'group_user', '/project', 'GET', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (40, 'g', 'user', 'group_user', '', '', '', ''); -INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (41, 'p', 'group_user', '/project/:id/deploy/:cloud_id', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (1, 'g', 'anonym', 'group_anonymous', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (2, 'g', 'group_admin', 'group_anonymous', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (3, 'g', 'group_user', 'group_anonymous', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (4, 'g', 'user', 'group_user', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (5, 'g', 'admin_petru', 'group_admin', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (6, 'g', 'user_petru', 'group_user', '', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (7, 'p', 'group_anonymous', '/health_check', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (8, 'p', 'group_anonymous', '/rating/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (9, 'p', 'group_anonymous', '/rating', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (10, 'p', 'group_admin', '/client', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (11, 'p', 'group_admin', '/rating', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (12, 'p', 'group_admin', '/admin/client/:id/disable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (13, 'p', 'group_admin', '/admin/client/:id/enable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (14, 'p', 'group_admin', '/admin/client/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (15, 'p', 'group_admin', '/admin/project/user/:userid', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (16, 'p', 'group_admin', '/rating/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (17, 'p', 'group_user', '/client/:id/enable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (18, 'p', 'group_user', '/client/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (19, 'p', 'group_user', '/client/:id/disable', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (20, 'p', 'group_user', '/rating/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (21, 'p', 'group_user', '/rating', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (22, 'p', 'group_user', '/rating', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (23, 'p', 'group_user', '/project', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (24, 'p', 'group_user', '/project', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (25, 'p', 'group_user', '/project/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (26, 'p', 'group_user', '/project/:id', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (27, 'p', 'group_user', '/project/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (28, 'p', 'group_user', '/project/:id', 'DELETE', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (29, 'p', 'group_user', '/project/:id/compose', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (30, 'p', 'group_user', '/project/:id/compose', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (31, 'p', 'group_user', '/project/:id/deploy', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (32, 'p', 'group_user', '/project/:id/deploy/:cloud_id', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (33, 'p', 'group_user', '/server', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (34, 'p', 'group_user', '/server', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (35, 'p', 'group_user', '/server/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (36, 'p', 'group_user', '/server/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (37, 'p', 'group_user', '/cloud', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (38, 'p', 'group_user', '/cloud', 'POST', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (39, 'p', 'group_user', '/cloud/:id', 'GET', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (40, 'p', 'group_user', '/cloud/:id', 'PUT', '', '', ''); +INSERT INTO public.casbin_rule (id, ptype, v0, v1, v2, v3, v4, v5) VALUES (41, 'p', 'group_user', '/cloud/:id', 'DELETE', '', '', ''); diff --git a/migrations/20240401184313_remove_project_id_from_cloud.down.sql b/migrations/20240401184313_remove_project_id_from_cloud.down.sql new file mode 100644 index 00000000..3b99d4c9 --- /dev/null +++ b/migrations/20240401184313_remove_project_id_from_cloud.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER table cloud ADD COLUMN project_id INT CONSTRAINT cloud_project_id REFERENCES project(id) ON UPDATE CASCADE ON DELETE CASCADE; diff --git a/migrations/20240401184313_remove_project_id_from_cloud.up.sql b/migrations/20240401184313_remove_project_id_from_cloud.up.sql new file mode 100644 index 00000000..4974d954 --- /dev/null +++ b/migrations/20240401184313_remove_project_id_from_cloud.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here + +alter table cloud DROP column project_id; diff --git a/src/console/commands/appclient/new.rs b/src/console/commands/appclient/new.rs index accbd765..dfafa9f0 100644 --- a/src/console/commands/appclient/new.rs +++ b/src/console/commands/appclient/new.rs @@ -24,7 +24,7 @@ impl crate::console::commands::CallableTrait for NewCommand { let settings = web::Data::new(settings); let db_pool = web::Data::new(db_pool); - //todo get user from trydirect + //todo get user from TryDirect let user = crate::models::user::User { id: format!("{}", self.user_id), first_name: "first_name".to_string(), diff --git a/src/db/cloud.rs b/src/db/cloud.rs index 77c840e5..92f79d1e 100644 --- a/src/db/cloud.rs +++ b/src/db/cloud.rs @@ -48,7 +48,6 @@ pub async fn insert(pool: &PgPool, mut cloud: models::Cloud) -> Result Result Result String { #[derive(Default, Clone, PartialEq, Serialize, Deserialize, Validate)] pub struct CloudForm { pub user_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub project_id: Option, #[validate(min_length = 2)] #[validate(max_length = 50)] @@ -63,7 +64,7 @@ impl CloudForm { let mut secret = Secret::new(); secret.user_id = cloud.user_id.clone(); - secret.project_id = cloud.project_id.clone().unwrap(); + secret.provider = cloud.provider.clone(); cloud.cloud_token = CloudForm::decrypt_field(&mut secret, "cloud_token", cloud.cloud_token.clone(), reveal); cloud.cloud_secret = CloudForm::decrypt_field(&mut secret, "cloud_secret", cloud.cloud_secret.clone(), reveal); cloud.cloud_key = CloudForm::decrypt_field(&mut secret, "cloud_key", cloud.cloud_key.clone(), reveal); @@ -126,12 +127,11 @@ impl Into for &CloudForm { let mut cloud = models::Cloud::default(); cloud.provider = self.provider.clone(); cloud.user_id = self.user_id.clone().unwrap(); - cloud.project_id = self.project_id; if Some(true) == self.save_token { let mut secret = Secret::new(); secret.user_id = self.user_id.clone().unwrap(); - secret.project_id = self.project_id.unwrap(); + secret.provider = self.provider.clone(); cloud.cloud_token = encrypt_field(&mut secret, "cloud_token", self.cloud_token.clone()); cloud.cloud_key = encrypt_field(&mut secret, "cloud_key", self.cloud_key.clone()); @@ -155,13 +155,12 @@ impl Into for models::Cloud { #[tracing::instrument(name = "Into for models::Cloud .")] fn into(self) -> CloudForm { let mut form = CloudForm::default(); - form.project_id = self.project_id; - form.provider = self.provider; + form.provider = self.provider.clone(); if Some(true) == self.save_token { let mut secret = Secret::new(); secret.user_id = self.user_id.clone(); - secret.project_id = self.project_id.unwrap(); + secret.provider = self.provider; secret.field = "cloud_token".to_string(); let value = match self.cloud_token { diff --git a/src/forms/project/deploy.rs b/src/forms/project/deploy.rs index 86c1453f..e300a18c 100644 --- a/src/forms/project/deploy.rs +++ b/src/forms/project/deploy.rs @@ -2,14 +2,14 @@ use serde_derive::{Deserialize, Serialize}; use serde_json::Value; use serde_valid::Validate; use crate::forms; -use crate::forms::{CloudForm, Server}; +use crate::forms::{CloudForm, ServerForm}; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] pub struct Deploy { #[validate] pub(crate) stack: Stack, #[validate] - pub(crate) server: Server, + pub(crate) server: ServerForm, #[validate] pub(crate) cloud: CloudForm, } diff --git a/src/forms/project/payload.rs b/src/forms/project/payload.rs index 5eb115a5..6a2c8686 100644 --- a/src/forms/project/payload.rs +++ b/src/forms/project/payload.rs @@ -14,7 +14,7 @@ pub struct Payload { #[serde(flatten)] pub cloud: Option, #[serde(flatten)] - pub server: Option, + pub server: Option, #[serde(flatten)] pub stack: forms::project::Stack, pub custom: forms::project::Custom, diff --git a/src/forms/server.rs b/src/forms/server.rs index 8b78c14e..134973ac 100644 --- a/src/forms/server.rs +++ b/src/forms/server.rs @@ -4,7 +4,7 @@ use serde_valid::Validate; use chrono::{Utc}; #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] -pub struct Server { +pub struct ServerForm { // pub cloud_id: i32, // pub project_id: i32, pub region: String, @@ -14,7 +14,7 @@ pub struct Server { pub disk_type: Option, } -impl Into for &Server { +impl Into for &ServerForm { fn into(self) -> models::Server { let mut server = models::Server::default(); server.disk_type = self.disk_type.clone(); @@ -29,22 +29,10 @@ impl Into for &Server { } } -// impl From for models::Server { -// fn from(self, value: Server) -> &mut Self { -// self.disk_type = value.disk_type.clone(); -// self.region = value.region.clone(); -// self.server = value.server.clone(); -// self.zone = value.zone.clone(); -// self.os = value.os.clone(); -// self.updated_at = Utc::now(); -// self -// } -// } +impl Into for models::Server { -impl Into for models::Server { - - fn into(self) -> Server { - let mut form = Server::default(); + fn into(self) -> ServerForm { + let mut form = ServerForm::default(); // form.cloud_id = self.cloud_id; // form.project_id = self.project_id; form.disk_type = self.disk_type; diff --git a/src/helpers/cloud/security.rs b/src/helpers/cloud/security.rs index 2e389940..23e82cce 100644 --- a/src/helpers/cloud/security.rs +++ b/src/helpers/cloud/security.rs @@ -9,7 +9,7 @@ use tracing::Instrument; #[derive(Debug, Default, PartialEq, Clone)] pub(crate) struct Secret { pub(crate) user_id: String, - pub(crate) project_id: i32, + pub(crate) provider: String, pub(crate) field: String, // cloud_token/cloud_key/cloud_secret pub(crate) nonce: Vec, } @@ -20,7 +20,7 @@ impl Secret { Secret { user_id: "".to_string(), - project_id: 0, + provider: "".to_string(), field: "".to_string(), nonce: vec![], } @@ -45,7 +45,7 @@ impl Secret { #[tracing::instrument(name = "Secret::save")] fn save(&self, value: &[u8]) -> &Self { let mut conn = Secret::connect_storage(); - let key = format!("{}_{}_{}", self.user_id, self.project_id, self.field); + let key = format!("{}_{}_{}", self.user_id, self.provider, self.field); tracing::debug!("Saving into storage.."); let _: () = match conn.set(key, value) { Ok(s) => s, @@ -118,7 +118,7 @@ impl Secret { .clone(); let key: &Key:: = Key::::from_slice(&sec_key.as_bytes()); // eprintln!("decrypt: Key str {key:?}"); - let rkey = format!("{}_{}_{}", self.user_id, self.project_id, self.field); + let rkey = format!("{}_{}_{}", self.user_id, self.provider, self.field); eprintln!("decrypt: Key str {rkey:?}"); self.get(rkey); // eprintln!("decrypt: nonce b64:decoded {nonce:?}"); diff --git a/src/middleware/authentication/method/f_oauth.rs b/src/middleware/authentication/method/f_oauth.rs index cec47b4c..1b861a75 100644 --- a/src/middleware/authentication/method/f_oauth.rs +++ b/src/middleware/authentication/method/f_oauth.rs @@ -35,6 +35,7 @@ pub async fn try_oauth(req: &mut ServiceRequest) -> Result { .map_err(|err| format!("{err}"))?; // control access using user role + tracing::debug!("ACL check for role: {}", user.role.clone()); let acl_vals = actix_casbin_auth::CasbinVals { subject: user.role.clone(), domain: None, diff --git a/src/models/cloud.rs b/src/models/cloud.rs index 77c3224a..b7c8f636 100644 --- a/src/models/cloud.rs +++ b/src/models/cloud.rs @@ -5,7 +5,6 @@ use serde_derive::{Deserialize, Serialize}; pub struct Cloud { pub id: i32, pub user_id: String, - pub project_id: Option, pub provider: String, pub cloud_token: Option, pub cloud_key: Option, @@ -28,19 +27,17 @@ impl std::fmt::Display for Cloud { let cloud_token = mask_string(self.cloud_token.as_ref()); let cloud_secret = mask_string(self.cloud_secret.as_ref()); - write!(f, "{} cloud creds: cloud_key : {} cloud_token: {} cloud_secret: {} project_id: {:?}", + write!(f, "{} cloud creds: cloud_key : {} cloud_token: {} cloud_secret: {}", self.provider, cloud_key, cloud_token, cloud_secret, - self.project_id ) } } impl Cloud { pub fn new(user_id: String, - project_id: Option, provider: String, cloud_token: Option, cloud_key: Option, @@ -50,7 +47,6 @@ impl Cloud { Self { id: 0, user_id, - project_id, provider, cloud_token, cloud_key, @@ -68,7 +64,6 @@ impl Default for Cloud { id: 0, provider: "".to_string(), user_id: "".to_string(), - project_id: Default::default(), cloud_key: Default::default(), cloud_token: Default::default(), cloud_secret: Default::default(), diff --git a/src/routes/project/deploy.rs b/src/routes/project/deploy.rs index 661115e0..95105173 100644 --- a/src/routes/project/deploy.rs +++ b/src/routes/project/deploy.rs @@ -168,7 +168,9 @@ pub async fn saved_item( let cloud = match db::cloud::fetch(pg_pool.get_ref(), cloud_id).await { Ok(cloud) => { match cloud { - Some(cloud) => cloud, + Some(mut cloud) => { + cloud + }, None => { return Err(JsonResponse::::build().not_found("No cloud configured")); } diff --git a/src/routes/server/update.rs b/src/routes/server/update.rs index b0741259..52f03273 100644 --- a/src/routes/server/update.rs +++ b/src/routes/server/update.rs @@ -13,7 +13,7 @@ use std::ops::Deref; #[put("/{id}")] pub async fn item( path: web::Path<(i32,)>, - form: web::Json, + form: web::Json, user: web::ReqData>, pg_pool: Data, ) -> Result { From 0576773da4b209563289637434df5d97a908f956 Mon Sep 17 00:00:00 2001 From: vsilent Date: Tue, 2 Apr 2024 15:59:58 +0300 Subject: [PATCH 14/18] fix warnings, return app id from docker image validator --- src/console/commands/debug/json.rs | 3 +-- src/forms/cloud.rs | 2 +- src/forms/project/form.rs | 29 ++++++++++++++++++++++++----- src/helpers/cloud/mod.rs | 2 +- src/helpers/cloud/security.rs | 2 +- src/helpers/compressor.rs | 4 ++-- src/helpers/json.rs | 22 +++++++++++----------- src/helpers/mq_manager.rs | 2 +- src/routes/client/disable.rs | 4 ++-- src/routes/client/enable.rs | 4 ++-- src/routes/client/update.rs | 4 ++-- src/routes/cloud/update.rs | 2 -- src/routes/project/deploy.rs | 4 ++-- src/routes/project/update.rs | 12 ++++++++---- src/startup.rs | 6 +----- 15 files changed, 59 insertions(+), 43 deletions(-) diff --git a/src/console/commands/debug/json.rs b/src/console/commands/debug/json.rs index 047faf0f..e05e3b07 100644 --- a/src/console/commands/debug/json.rs +++ b/src/console/commands/debug/json.rs @@ -1,5 +1,4 @@ -use crate::configuration::get_configuration; -use actix_web::{rt, post, web, HttpResponse, Result, http::header::ContentType}; +use actix_web::{Result}; pub struct JsonCommand { line: usize, diff --git a/src/forms/cloud.rs b/src/forms/cloud.rs index cc54f816..fe4cdf6a 100644 --- a/src/forms/cloud.rs +++ b/src/forms/cloud.rs @@ -1,5 +1,5 @@ use crate::models; -use serde::{Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use serde_valid::Validate; use crate::helpers::cloud::security::Secret; use tracing::Instrument; diff --git a/src/forms/project/form.rs b/src/forms/project/form.rs index a0514a4f..b849abb5 100644 --- a/src/forms/project/form.rs +++ b/src/forms/project/form.rs @@ -18,18 +18,31 @@ impl TryFrom<&models::Project> for ProjectForm { } } + +#[derive(Serialize, Default)] +pub struct DockerImageReadResult { + pub(crate) id: String, + pub(crate) readable: bool +} + impl ProjectForm { - pub async fn is_readable_docker_image(&self) -> Result { + pub async fn is_readable_docker_image(&self) -> Result { for app in &self.custom.web { if !app.app.docker_image.is_active().await? { - return Ok(false); + return Ok(DockerImageReadResult{ + id: app.app.id.clone(), + readable: false + }); } } if let Some(service) = &self.custom.service { for app in service { if !app.app.docker_image.is_active().await? { - return Ok(false); + return Ok(DockerImageReadResult{ + id: app.app.id.clone(), + readable: false + }); } } } @@ -37,10 +50,16 @@ impl ProjectForm { if let Some(features) = &self.custom.feature { for app in features { if !app.app.docker_image.is_active().await? { - return Ok(false); + return Ok(DockerImageReadResult{ + id: app.app.id.clone(), + readable: false + }); } } } - Ok(true) + Ok(DockerImageReadResult{ + id: "".to_owned(), + readable: true + }) } } \ No newline at end of file diff --git a/src/helpers/cloud/mod.rs b/src/helpers/cloud/mod.rs index 9d2c9991..96224c8b 100644 --- a/src/helpers/cloud/mod.rs +++ b/src/helpers/cloud/mod.rs @@ -1,2 +1,2 @@ pub(crate) mod security; -pub use security::*; \ No newline at end of file +pub use security::Secret; \ No newline at end of file diff --git a/src/helpers/cloud/security.rs b/src/helpers/cloud/security.rs index 23e82cce..ddb6d1bb 100644 --- a/src/helpers/cloud/security.rs +++ b/src/helpers/cloud/security.rs @@ -7,7 +7,7 @@ use redis::{Commands, Connection}; use tracing::Instrument; #[derive(Debug, Default, PartialEq, Clone)] -pub(crate) struct Secret { +pub struct Secret { pub(crate) user_id: String, pub(crate) provider: String, pub(crate) field: String, // cloud_token/cloud_key/cloud_secret diff --git a/src/helpers/compressor.rs b/src/helpers/compressor.rs index c6e12589..ec126fca 100644 --- a/src/helpers/compressor.rs +++ b/src/helpers/compressor.rs @@ -1,5 +1,5 @@ -use brotli::{CompressorWriter, Decompressor}; -use std::io::{Read, Write}; +use brotli::{CompressorWriter}; +use std::io::{Write}; pub fn compress(input: &str) -> Vec { let mut compressed = Vec::new(); diff --git a/src/helpers/json.rs b/src/helpers/json.rs index 36f8d39f..ebb9df1e 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -2,7 +2,6 @@ use actix_web::error::{ErrorBadRequest, ErrorConflict, ErrorInternalServerError, use actix_web::web::Json; use actix_web::Error; use serde_derive::Serialize; -use std::convert::From; #[derive(Serialize)] pub(crate) struct JsonResponse { @@ -90,16 +89,17 @@ where ErrorInternalServerError(self.set_msg(msg).to_string()) } - pub(crate) fn unauthorized>( - self, - msg: I, - ) -> Error { - ErrorUnauthorized(self.set_msg(msg).to_string()) - } - - pub(crate) fn conflict>(self, msg: I) -> Error { - ErrorConflict(self.set_msg(msg).to_string()) - } + // not used + // pub(crate) fn unauthorized>( + // self, + // msg: I, + // ) -> Error { + // ErrorUnauthorized(self.set_msg(msg).to_string()) + // } + // + // pub(crate) fn conflict>(self, msg: I) -> Error { + // ErrorConflict(self.set_msg(msg).to_string()) + // } } impl JsonResponse diff --git a/src/helpers/mq_manager.rs b/src/helpers/mq_manager.rs index dfcda23d..f38604a0 100644 --- a/src/helpers/mq_manager.rs +++ b/src/helpers/mq_manager.rs @@ -127,7 +127,7 @@ impl MqManager { let mut args = FieldTable::default(); args.insert("x-expires".into(), AMQPValue::LongUInt(3600000)); - let queue = channel.queue_declare( + let _queue = channel.queue_declare( queue_name, QueueDeclareOptions { passive: false, diff --git a/src/routes/client/disable.rs b/src/routes/client/disable.rs index 0d1fe3be..1c8b9d17 100644 --- a/src/routes/client/disable.rs +++ b/src/routes/client/disable.rs @@ -15,7 +15,7 @@ pub async fn disable_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - let mut client = db::client::fetch(pg_pool.get_ref(), client_id) + let client = db::client::fetch(pg_pool.get_ref(), client_id) .await .map_err(|msg| JsonResponse::::build().internal_server_error(msg)) .and_then( |client| { @@ -38,7 +38,7 @@ pub async fn admin_disable_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - let mut client = db::client::fetch(pg_pool.get_ref(), client_id) + let client = db::client::fetch(pg_pool.get_ref(), client_id) .await .map_err(|msg| JsonResponse::::build().internal_server_error(msg))? .ok_or_else(|| JsonResponse::::build().not_found("not found"))?; diff --git a/src/routes/client/enable.rs b/src/routes/client/enable.rs index e3d6c43e..e3955a6d 100644 --- a/src/routes/client/enable.rs +++ b/src/routes/client/enable.rs @@ -16,7 +16,7 @@ pub async fn enable_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - let mut client = db::client::fetch(pg_pool.get_ref(), client_id) + let client = db::client::fetch(pg_pool.get_ref(), client_id) .await .map_err(|msg| JsonResponse::::build().internal_server_error(msg))? .ok_or_else(|| JsonResponse::::build().not_found("not found"))?; @@ -37,7 +37,7 @@ pub async fn admin_enable_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - let mut client = db::client::fetch(pg_pool.get_ref(), client_id) + let client = db::client::fetch(pg_pool.get_ref(), client_id) .await .map_err(|msg| JsonResponse::::build().internal_server_error(msg))? .ok_or_else(|| JsonResponse::::build().not_found("not found"))?; diff --git a/src/routes/client/update.rs b/src/routes/client/update.rs index 3ed728bf..de095814 100644 --- a/src/routes/client/update.rs +++ b/src/routes/client/update.rs @@ -15,7 +15,7 @@ pub async fn update_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - let mut client = db::client::fetch(pg_pool.get_ref(), client_id) + let client = db::client::fetch(pg_pool.get_ref(), client_id) .await .map_err(|msg| JsonResponse::::build().internal_server_error(msg))? .ok_or_else(|| JsonResponse::::build().not_found("not found"))?; @@ -36,7 +36,7 @@ pub async fn admin_update_handler( path: web::Path<(i32,)>, ) -> Result { let client_id = path.0; - let mut client = db::client::fetch(pg_pool.get_ref(), client_id) + let client = db::client::fetch(pg_pool.get_ref(), client_id) .await .map_err(|msg| JsonResponse::::build().internal_server_error(msg))? .ok_or_else(|| JsonResponse::::build().not_found("not found"))?; diff --git a/src/routes/cloud/update.rs b/src/routes/cloud/update.rs index 7672f551..5b4f4a1b 100644 --- a/src/routes/cloud/update.rs +++ b/src/routes/cloud/update.rs @@ -8,7 +8,6 @@ use sqlx::PgPool; use std::sync::Arc; use tracing::Instrument; use std::ops::Deref; -use chrono::Utc; #[tracing::instrument(name = "Update cloud.")] #[put("/{id}")] @@ -38,7 +37,6 @@ pub async fn item( let mut cloud:models::Cloud = form.deref().into(); cloud.id = cloud_row.id; cloud.user_id = user.id.clone(); - // cloud.updated_at = Utc::now(); tracing::debug!("Updating cloud {:?}", cloud); diff --git a/src/routes/project/deploy.rs b/src/routes/project/deploy.rs index 95105173..b0d767e5 100644 --- a/src/routes/project/deploy.rs +++ b/src/routes/project/deploy.rs @@ -130,7 +130,7 @@ pub async fn item( #[post("/{id}/deploy/{cloud_id}")] pub async fn saved_item( user: web::ReqData>, - mut form: web::Json, + form: web::Json, path: web::Path<(i32, i32)>, pg_pool: Data, mq_manager: Data, @@ -168,7 +168,7 @@ pub async fn saved_item( let cloud = match db::cloud::fetch(pg_pool.get_ref(), cloud_id).await { Ok(cloud) => { match cloud { - Some(mut cloud) => { + Some(cloud) => { cloud }, None => { diff --git a/src/routes/project/update.rs b/src/routes/project/update.rs index cdd532e6..a6a8a8c9 100644 --- a/src/routes/project/update.rs +++ b/src/routes/project/update.rs @@ -1,5 +1,5 @@ use std::str::FromStr; -use crate::forms::project::ProjectForm; +use crate::forms::project::{ProjectForm, DockerImageReadResult}; use crate::helpers::JsonResponse; use crate::models; use crate::db; @@ -41,11 +41,15 @@ pub async fn item( let project_name = form.custom.custom_stack_code.clone(); - if Ok(false) == form.is_readable_docker_image().await { - return Err(JsonResponse::::build() - .bad_request("Can not access docker image")); + if let Ok(result) = form.is_readable_docker_image().await { + if false == result.readable { + return Err(JsonResponse::::build() + .set_item(result) + .bad_request("Can not access docker image")); + } } + let body: Value = serde_json::to_value::(form) .or(serde_json::to_value::(ProjectForm::default())) .unwrap(); diff --git a/src/startup.rs b/src/startup.rs index 302070d0..741393f1 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -9,15 +9,11 @@ use actix_web::{ web, App, HttpServer, - HttpResponse, - FromRequest, - rt, }; use crate::middleware; use sqlx::{Pool, Postgres}; use std::net::TcpListener; use tracing_actix_web::TracingLogger; -use redis::Commands; pub async fn run( listener: TcpListener, @@ -32,7 +28,7 @@ pub async fn run( let authorization = middleware::authorization::try_new(settings.database.connection_string()).await?; let json_config = web::JsonConfig::default() - .error_handler(|err, req| { //todo + .error_handler(|err, _req| { //todo let msg: String = match err { error::JsonPayloadError::Deserialize(err) => format!("{{\"kind\":\"deserialize\",\"line\":{}, \"column\":{}, \"msg\":\"{}\"}}", err.line(), err.column(), err), _ => format!("{{\"kind\":\"other\",\"msg\":\"{}\"}}", err) From 41b1ca6e182d19a0eec0b460d45db38632bdbee4 Mon Sep 17 00:00:00 2001 From: vsilent Date: Sun, 7 Apr 2024 09:04:45 +0300 Subject: [PATCH 15/18] unfinished tag fix --- src/helpers/dockerhub.rs | 195 ++++++++++++++++++---- tests/mock_data/custom-stack-payload.json | 2 +- 2 files changed, 161 insertions(+), 36 deletions(-) diff --git a/src/helpers/dockerhub.rs b/src/helpers/dockerhub.rs index 81f4feb2..60ace373 100644 --- a/src/helpers/dockerhub.rs +++ b/src/helpers/dockerhub.rs @@ -65,29 +65,58 @@ pub struct RepoResults { pub results: Vec, } +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] +pub struct OfficialRepoResults { + pub count: Option, + pub next: Option, + pub previous: Option, + pub results: Vec, +} + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RepoResult { pub name: String, - pub namespace: String, - pub repository_type: String, - pub status: i64, - pub status_description: String, - pub description: String, - pub is_private: bool, - pub star_count: i64, - pub pull_count: i64, + pub namespace: Option, + pub repository_type: Option, + pub status: Option, + pub status_description: Option, + pub description: Option, + pub is_private: Option, + pub star_count: Option, + pub pull_count: Option, pub last_updated: String, - pub date_registered: String, - pub affiliation: String, - pub media_types: Vec, - pub content_types: Vec, + pub date_registered: Option, + pub affiliation: Option, + pub media_types: Option>, + pub content_types: Option>, } + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OfficialRepoResult { + pub images: Vec, + pub last_updated: String, + pub last_updater: i64, + pub content_type: String, + pub creator: i64, + pub digest: Option, + pub full_size: i64, + pub id: i64, + pub last_updater_username: String, + pub media_type: String, + pub name: String, + pub repository: i64, + pub tag_last_pulled: Option, + pub tag_last_pushed: Option, + pub tag_status: String, + pub v2: bool, +} #[derive(Default, Debug, Clone, PartialEq, Serialize, Validate)] pub struct DockerHub<'a> { pub(crate) creds: DockerHubCreds<'a>, pub(crate) repos: String, pub(crate) image: String, + pub(crate) tag: Option, } impl<'a> DockerHub<'a> { @@ -105,33 +134,86 @@ impl<'a> DockerHub<'a> { .map_err(|err| format!("{:?}", err)) } - pub async fn lookup_public_repo(&self) -> Result { - let url = format!("https://hub.docker.com/v2/repositories/{}", &self.repos); - tracing::debug!("Validate public repositories {:?}", url); + pub async fn lookup_public_repos(&'a self) -> Result { + + let url = format!("https://hub.docker.com/v2/repositories/{}", self.repos); + tracing::debug!("Validate public repository {:?}", &url); let client = reqwest::Client::new() - .get(url) + .get(&url) .header("Accept", "application/json"); let client = self.set_token(client).await?; client .send() .await .map_err(|err| { - tracing::debug!("🟥Error response {:?}", err); - format!("{}", err) + let msg = format!("🟥Error response {:?}", err); + tracing::debug!(msg); + msg })? .json::() .await - .map_err(|err| format!("🟥Error on getting results:: {}", err)) + .map_err(|err| { + let msg = format!("🟥Error on getting results:: {} url: {}", &err, &url); + tracing::error!(msg); + msg + }) .map(|repositories| { - tracing::debug!("Get public image repositories response {:?}", repositories); + tracing::debug!("Get public image repo {:?} response {:?}", &url, repositories); if repositories.count.unwrap_or(0) > 0 { // let's find at least one active tag let active = repositories .results .into_iter() - .any(|repo| repo.status == 1); - tracing::debug!("✅ Image is active"); + .any(|repo| repo.status == Some(1)); + tracing::debug!("✅ Image is active. url: {:?}", &url); active + } else { + tracing::debug!("🟥 Image tag is not active, url: {:?}", &url); + false + } + }) + } + + pub async fn lookup_official_repos(&'a self) -> Result { + // search in official library repositories + let url = format!("https://hub.docker.com/v2/repositories/library/{}/tags", self.repos); + return self.lookup(&url).await; + } + + pub async fn lookup(&'a self, url: &String) -> Result { + tracing::debug!("Search official repos {:?}", url); + let client = reqwest::Client::new() + .get(url) + .header("Accept", "application/json"); + let client = self.set_token(client).await?; + client + .send() + .await + .map_err(|err| format!("🟥{}", err))? + .json::() + .await + .map_err(|err| { + tracing::debug!("🟥Error response {:?}", err); + format!("{}", err) + }) + .map(|tags| { + tracing::debug!("Validate official image response {:?}", tags); + if tags.count.unwrap_or(0) > 0 { + // let's find at least one active tag + let result = tags + .results + .into_iter() + .any(|tag| { + tracing::debug!("official: {:?}", tag); + if "active".to_string() == tag.tag_status { + tracing::debug!("✅ Image is active"); + true + } else { + false + } + }); + tracing::debug!("✅ result is {:?}", result); + result } else { tracing::debug!("🟥 Image tag is not active"); false @@ -139,7 +221,7 @@ impl<'a> DockerHub<'a> { }) } - pub async fn lookup_private_repo(&self) -> Result { + pub async fn lookup_private_repo(&'a self) -> Result { let url = format!( "https://hub.docker.com/v2/namespaces/{}/repositories/{}/tags", &self.creds.username, &self.repos @@ -168,29 +250,60 @@ impl<'a> DockerHub<'a> { .into_iter() .any(|tag| tag.tag_status.contains("active")); tracing::debug!("✅ Image is active"); - active + return active; } else { tracing::debug!("🟥 Image tag is not active"); false } }) } - pub async fn is_active(&'a self) -> Result { + pub async fn is_active(&'a mut self) -> Result { // if namespace/user is not set change endpoint and return a different response + + // let repo = self.repos.clone(); + // if self.repos.contains(':') { + // let _repo = repo.split(':') + // .collect::>(); + // self.repos = _repo.first().expect("split error").to_string(); + // self.tag = Some(_repo.last().expect("split error").to_string()); + // } + + // let n = self.repos.split(':').collect::>(); + // if let Some(name: &str) == n.first() { + // self.repos = name.to_string(); + // } + // if let Some(name: &str) == n.last() { + // self.tag = name.to_string(); + // } + if self.creds.username.is_empty() { - match self.lookup_public_repo().await { - Ok(result) => Ok(result), - Err(_e) => Ok(false), - } + + if Ok(true) == self.lookup_official_repos().await { + tracing::debug!("official: true"); + return Ok(true); + } else { + tracing::debug!("official: false"); + }; + + if Ok(true) == self.lookup_public_repos().await { + tracing::debug!("public: true"); + return Ok(true); + }; + + Ok(false) + } else { - match self.lookup_private_repo().await { - Ok(result) => Ok(result), - Err(_e) => Ok(false), - } + + if Ok(true) == self.lookup_private_repo().await { + tracing::debug!("private: true"); + return Ok(true); + }; + + Ok(false) } } - pub async fn set_token(&self, client: RequestBuilder) -> Result { + pub async fn set_token(&'a self, client: RequestBuilder) -> Result { if self.creds.password.is_empty() { tracing::debug!("Password is empty. Image should be public"); return Ok(client); @@ -217,13 +330,25 @@ impl<'a> From<&'a DockerImage> for DockerHub<'a> { None => "", }; + let name = image.dockerhub_name.clone().unwrap_or("".to_string()); + let mut tag = "".to_string(); + + if name.contains(':') { + let parts = name + .split(':') + .collect::>(); + let name = parts.first().expect("Could not split").to_string(); + tag = parts.last().expect("Could not split").to_string(); + } + DockerHub { creds: DockerHubCreds { username: username, password: password, }, - repos: image.dockerhub_name.clone().unwrap_or("".to_string()), + repos: name, image: format!("{}", image), + tag: Some(tag), } } } diff --git a/tests/mock_data/custom-stack-payload.json b/tests/mock_data/custom-stack-payload.json index ab48715a..88fe2c25 100644 --- a/tests/mock_data/custom-stack-payload.json +++ b/tests/mock_data/custom-stack-payload.json @@ -1 +1 @@ -{"commonDomain":"","domainList":{},"region":"","zone":null,"server":"cx11","os":"ubuntu-20.04","ssl":"letsencrypt","vars":[],"integrated_features":[],"extended_features":[],"subscriptions":[],"form_app":[],"save_token":true,"disk_type":"pd-standart","cloud_token":"*******************","cloud_key":"*********","cloud_secret":"****************","disk_size":40,"provider":"htz","stack_code":"custom-stack","selected_plan":"plan-individual-annually","custom":{"networks":[{"name":"default_network","id":"lsnd2ttg3ivhw3db5"}],"web":[{"_etag":null,"_id":"lsq2tdff3c0acf2lj","_created":"2023-04-28T09:46:19.470502","_updated":"2024-02-09T13:44:36.854036","name":"PostgREST","code":"postgrest","role":["postgrest"],"type":"web","default":false,"popularity":null,"descr":null,"ports":{"public":["3000","8080"]},"commercial":null,"subscription":null,"autodeploy":null,"suggested":null,"dependency":null,"avoid_render":null,"price":null,"icon":null,"category_id":null,"parent_app_id":null,"full_description":null,"description":null,"plan_type":null,"ansible_var":null,"repo_dir":null,"cpu":"0.0","ram_size":null,"disk_size":null,"dockerhub_image":"postgrest","form":null,"requirements":null,"docker_image_is_internal":true,"custom_preset":{"volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"restart":"always"},"category":[null],"group":[],"versions":[{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"}],"links":[{"url":"https://postgrest.org","title":"Vendor","type":"vendor","follow":false},{"repo_owner":"PostgREST","repo_name":"postgrest","type":"github","follow":false}],"version":{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"},"network":["lsnd2ttg3ivhw3db5"],"restart":"always","timestamp":"2024-02-17T12:50:01.659Z","volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"domain":"","dockerhub_name":"postgrest"}],"feature":[],"service":[],"servers_count":3,"project_name":"Databases","custom_stack_code":"databases"}} \ No newline at end of file +{"commonDomain":"","domainList":{},"region":"","zone":null,"server":"cx11","os":"ubuntu-20.04","ssl":"letsencrypt","vars":[],"integrated_features":[],"extended_features":[],"subscriptions":[],"form_app":[],"save_token":true,"disk_type":"pd-standart","cloud_token":"*******************","cloud_key":"*********","cloud_secret":"****************","disk_size":40,"provider":"htz","stack_code":"custom-stack","selected_plan":"plan-individual-annually","custom":{"networks":[{"name":"default_network","id":"lsnd2ttg3ivhw3db5"}],"web":[{"_etag":null,"_id":"lsq2tdff3c0acf2lj","_created":"2023-04-28T09:46:19.470502","_updated":"2024-02-09T13:44:36.854036","name":"PostgREST","code":"postgrest","role":["postgrest"],"type":"web","default":false,"popularity":null,"descr":null,"ports":{"public":["3000","8080"]},"commercial":null,"subscription":null,"autodeploy":null,"suggested":null,"dependency":null,"avoid_render":null,"price":null,"icon":null,"category_id":null,"parent_app_id":null,"full_description":null,"description":null,"plan_type":null,"ansible_var":null,"repo_dir":null,"cpu":"0.0","ram_size":null,"disk_size":null,"dockerhub_image":"","form":null,"requirements":null,"docker_image_is_internal":true,"custom_preset":{"volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"restart":"always"},"category":[null],"group":[],"versions":[{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"}],"links":[{"url":"https://postgrest.org","title":"Vendor","type":"vendor","follow":false},{"repo_owner":"PostgREST","repo_name":"postgrest","type":"github","follow":false}],"version":{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"},"network":["lsnd2ttg3ivhw3db5"],"restart":"always","timestamp":"2024-02-17T12:50:01.659Z","volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"domain":"","dockerhub_name":"mariadb:11.3.2"}],"feature":[],"service":[],"servers_count":3,"project_name":"Databases","custom_stack_code":"databases"}} \ No newline at end of file From b943f4cf005414b29bc1d531ba5aae9ff4839f74 Mon Sep 17 00:00:00 2001 From: vsilent Date: Mon, 8 Apr 2024 10:40:02 +0300 Subject: [PATCH 16/18] from to try_from, repo name format error fix --- src/forms/project/docker_image.rs | 2 +- src/helpers/dockerhub.rs | 84 ++++++++++++++++++++----------- src/routes/project/update.rs | 23 +++++++-- 3 files changed, 76 insertions(+), 33 deletions(-) diff --git a/src/forms/project/docker_image.rs b/src/forms/project/docker_image.rs index e9737d0b..a018174e 100644 --- a/src/forms/project/docker_image.rs +++ b/src/forms/project/docker_image.rs @@ -41,7 +41,7 @@ impl fmt::Display for DockerImage { impl DockerImage { #[tracing::instrument(name = "is_active")] pub async fn is_active(&self) -> Result { - DockerHub::from(self).is_active().await + DockerHub::try_from(self)?.is_active().await } } diff --git a/src/helpers/dockerhub.rs b/src/helpers/dockerhub.rs index 60ace373..7309cda2 100644 --- a/src/helpers/dockerhub.rs +++ b/src/helpers/dockerhub.rs @@ -114,6 +114,7 @@ pub struct OfficialRepoResult { #[derive(Default, Debug, Clone, PartialEq, Serialize, Validate)] pub struct DockerHub<'a> { pub(crate) creds: DockerHubCreds<'a>, + #[validate(pattern = r"^[^:]+(:[^:]*)?$")] pub(crate) repos: String, pub(crate) image: String, pub(crate) tag: Option, @@ -260,20 +261,22 @@ impl<'a> DockerHub<'a> { pub async fn is_active(&'a mut self) -> Result { // if namespace/user is not set change endpoint and return a different response - // let repo = self.repos.clone(); - // if self.repos.contains(':') { - // let _repo = repo.split(':') - // .collect::>(); - // self.repos = _repo.first().expect("split error").to_string(); - // self.tag = Some(_repo.last().expect("split error").to_string()); - // } - - // let n = self.repos.split(':').collect::>(); - // if let Some(name: &str) == n.first() { - // self.repos = name.to_string(); - // } - // if let Some(name: &str) == n.last() { - // self.tag = name.to_string(); + // let n = self.repos + // .split(':') + // .map(|x| x.to_string()) + // .collect::>(); + // + // match n.len() { + // 1 => { + // self.repos = n.first().unwrap().into(); + // } + // 2 => { + // self.repos = n.first().unwrap().to_string(); + // self.tag = n.last().map(|s| s.to_string()); + // } + // _ => { + // return Err(format!("Wrong format of repository name")); + // } // } if self.creds.username.is_empty() { @@ -319,8 +322,12 @@ impl<'a> DockerHub<'a> { } } -impl<'a> From<&'a DockerImage> for DockerHub<'a> { - fn from(image: &'a DockerImage) -> Self { + +impl<'a> TryFrom<&'a DockerImage> for DockerHub<'a> { + type Error = String; + + fn try_from(image: &'a DockerImage) -> Result { + let username = match image.dockerhub_user { Some(ref username) => username, None => "", @@ -331,24 +338,45 @@ impl<'a> From<&'a DockerImage> for DockerHub<'a> { }; let name = image.dockerhub_name.clone().unwrap_or("".to_string()); - let mut tag = "".to_string(); - - if name.contains(':') { - let parts = name - .split(':') - .collect::>(); - let name = parts.first().expect("Could not split").to_string(); - tag = parts.last().expect("Could not split").to_string(); - } + let n = name + .split(':') + .map(|x| x.to_string()) + .collect::>(); - DockerHub { + let (name, tag) = match n.len() { + 1 => { + ( + n.first().unwrap().into(), + Some("".to_string()) + ) + } + 2 => { + ( + n.first().unwrap().to_string(), + n.last().map(|s| s.to_string()) + ) + } + _ => { + return Err(format!("Wrong format of repository name")); + } + }; + + let hub = DockerHub { creds: DockerHubCreds { username: username, password: password, }, repos: name, image: format!("{}", image), - tag: Some(tag), + tag: tag, + }; + + if hub.validate().is_ok() { + Ok(hub) + } else { + let errors = hub.validate().unwrap_err(); + tracing::debug!("DockerHub image properties are not valid {:?}", errors); + return Err(format!("{:?}", errors)); } } -} +} \ No newline at end of file diff --git a/src/routes/project/update.rs b/src/routes/project/update.rs index a6a8a8c9..0303d5cd 100644 --- a/src/routes/project/update.rs +++ b/src/routes/project/update.rs @@ -34,6 +34,7 @@ pub async fn item( // @todo ACL let form: ProjectForm= serde_json::from_value(request_json.clone()) .map_err(|err| JsonResponse::bad_request(err.to_string()))?; + if !form.validate().is_ok() { let errors = form.validate().unwrap_err(); return Err(JsonResponse::bad_request(errors.to_string())); @@ -41,11 +42,25 @@ pub async fn item( let project_name = form.custom.custom_stack_code.clone(); - if let Ok(result) = form.is_readable_docker_image().await { - if false == result.readable { + // if let Ok(result) = form.is_readable_docker_image().await { + // if false == result.readable { + // return Err(JsonResponse::::build() + // .set_item(result) + // .bad_request("Can not access docker image")); + // } + // } + + match form.is_readable_docker_image().await { + Ok(result) => { + if false == result.readable { + return Err(JsonResponse::::build() + .set_item(result) + .bad_request("Can not access docker image")); + } + } + Err(e) => { return Err(JsonResponse::::build() - .set_item(result) - .bad_request("Can not access docker image")); + .bad_request(e)); } } From 869ce5422c52623146a1f72c2f65caaa3c28f325 Mon Sep 17 00:00:00 2001 From: vsilent Date: Wed, 10 Apr 2024 12:22:58 +0300 Subject: [PATCH 17/18] remove extra structure --- src/helpers/dockerhub.rs | 73 +++++++++-------------- src/routes/project/update.rs | 8 --- tests/mock_data/custom-stack-payload.json | 2 +- 3 files changed, 29 insertions(+), 54 deletions(-) diff --git a/src/helpers/dockerhub.rs b/src/helpers/dockerhub.rs index 7309cda2..fda20f0f 100644 --- a/src/helpers/dockerhub.rs +++ b/src/helpers/dockerhub.rs @@ -32,23 +32,24 @@ struct Image { #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] struct Tag { - content_type: String, - creator: i64, - digest: Option, - full_size: i64, - id: i64, - images: Vec, - last_updated: String, - last_updater: i64, - last_updater_username: String, - media_type: String, - name: String, - repository: i64, - tag_last_pulled: Option, - tag_last_pushed: Option, - tag_status: String, - v2: bool, + pub content_type: String, + pub creator: i64, + pub digest: Option, + pub full_size: i64, + pub id: i64, + pub images: Vec, + pub last_updated: String, + pub last_updater: i64, + pub last_updater_username: String, + pub media_type: String, + pub name: String, + pub repository: i64, + pub tag_last_pulled: Option, + pub tag_last_pushed: Option, + pub tag_status: String, + pub v2: bool, } + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Validate)] struct TagResult { pub count: Option, @@ -70,7 +71,7 @@ pub struct OfficialRepoResults { pub count: Option, pub next: Option, pub previous: Option, - pub results: Vec, + pub results: Vec, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -92,29 +93,11 @@ pub struct RepoResult { } -#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct OfficialRepoResult { - pub images: Vec, - pub last_updated: String, - pub last_updater: i64, - pub content_type: String, - pub creator: i64, - pub digest: Option, - pub full_size: i64, - pub id: i64, - pub last_updater_username: String, - pub media_type: String, - pub name: String, - pub repository: i64, - pub tag_last_pulled: Option, - pub tag_last_pushed: Option, - pub tag_status: String, - pub v2: bool, -} #[derive(Default, Debug, Clone, PartialEq, Serialize, Validate)] pub struct DockerHub<'a> { pub(crate) creds: DockerHubCreds<'a>, - #[validate(pattern = r"^[^:]+(:[^:]*)?$")] + //#[validate(pattern = r"^[^:]+(:[^:]*)?$")] + #[validate(pattern = r"^([a-z-_0-9]+)(:[a-z-_0-9\.]+)?$")] pub(crate) repos: String, pub(crate) image: String, pub(crate) tag: Option, @@ -213,7 +196,7 @@ impl<'a> DockerHub<'a> { false } }); - tracing::debug!("✅ result is {:?}", result); + tracing::debug!("✅ search official repos result is {:?}", result); result } else { tracing::debug!("🟥 Image tag is not active"); @@ -357,7 +340,7 @@ impl<'a> TryFrom<&'a DockerImage> for DockerHub<'a> { ) } _ => { - return Err(format!("Wrong format of repository name")); + return Err("Wrong format of repository name".to_owned()); } }; @@ -371,12 +354,12 @@ impl<'a> TryFrom<&'a DockerImage> for DockerHub<'a> { tag: tag, }; - if hub.validate().is_ok() { - Ok(hub) - } else { - let errors = hub.validate().unwrap_err(); - tracing::debug!("DockerHub image properties are not valid {:?}", errors); - return Err(format!("{:?}", errors)); + if let Err(errors) = hub.validate() { + let msg = "DockerHub image properties are not valid. Please verify repository name"; + tracing::debug!("{:?} {:?}", msg, errors); + return Err(format!("{:?}", msg)); } + + Ok(hub) } } \ No newline at end of file diff --git a/src/routes/project/update.rs b/src/routes/project/update.rs index 0303d5cd..38d08ea5 100644 --- a/src/routes/project/update.rs +++ b/src/routes/project/update.rs @@ -42,14 +42,6 @@ pub async fn item( let project_name = form.custom.custom_stack_code.clone(); - // if let Ok(result) = form.is_readable_docker_image().await { - // if false == result.readable { - // return Err(JsonResponse::::build() - // .set_item(result) - // .bad_request("Can not access docker image")); - // } - // } - match form.is_readable_docker_image().await { Ok(result) => { if false == result.readable { diff --git a/tests/mock_data/custom-stack-payload.json b/tests/mock_data/custom-stack-payload.json index 88fe2c25..f602dbbd 100644 --- a/tests/mock_data/custom-stack-payload.json +++ b/tests/mock_data/custom-stack-payload.json @@ -1 +1 @@ -{"commonDomain":"","domainList":{},"region":"","zone":null,"server":"cx11","os":"ubuntu-20.04","ssl":"letsencrypt","vars":[],"integrated_features":[],"extended_features":[],"subscriptions":[],"form_app":[],"save_token":true,"disk_type":"pd-standart","cloud_token":"*******************","cloud_key":"*********","cloud_secret":"****************","disk_size":40,"provider":"htz","stack_code":"custom-stack","selected_plan":"plan-individual-annually","custom":{"networks":[{"name":"default_network","id":"lsnd2ttg3ivhw3db5"}],"web":[{"_etag":null,"_id":"lsq2tdff3c0acf2lj","_created":"2023-04-28T09:46:19.470502","_updated":"2024-02-09T13:44:36.854036","name":"PostgREST","code":"postgrest","role":["postgrest"],"type":"web","default":false,"popularity":null,"descr":null,"ports":{"public":["3000","8080"]},"commercial":null,"subscription":null,"autodeploy":null,"suggested":null,"dependency":null,"avoid_render":null,"price":null,"icon":null,"category_id":null,"parent_app_id":null,"full_description":null,"description":null,"plan_type":null,"ansible_var":null,"repo_dir":null,"cpu":"0.0","ram_size":null,"disk_size":null,"dockerhub_image":"","form":null,"requirements":null,"docker_image_is_internal":true,"custom_preset":{"volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"restart":"always"},"category":[null],"group":[],"versions":[{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"}],"links":[{"url":"https://postgrest.org","title":"Vendor","type":"vendor","follow":false},{"repo_owner":"PostgREST","repo_name":"postgrest","type":"github","follow":false}],"version":{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"},"network":["lsnd2ttg3ivhw3db5"],"restart":"always","timestamp":"2024-02-17T12:50:01.659Z","volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"domain":"","dockerhub_name":"mariadb:11.3.2"}],"feature":[],"service":[],"servers_count":3,"project_name":"Databases","custom_stack_code":"databases"}} \ No newline at end of file +{"commonDomain":"","domainList":{},"region":"","zone":null,"server":"cx11","os":"ubuntu-20.04","ssl":"letsencrypt","vars":[],"integrated_features":[],"extended_features":[],"subscriptions":[],"form_app":[],"save_token":true,"disk_type":"pd-standart","cloud_token":"*******************","cloud_key":"*********","cloud_secret":"****************","disk_size":40,"provider":"htz","stack_code":"custom-stack","selected_plan":"plan-individual-annually","custom":{"networks":[{"name":"default_network","id":"lsnd2ttg3ivhw3db5"}],"web":[{"_etag":null,"_id":"lsq2tdff3c0acf2lj","_created":"2023-04-28T09:46:19.470502","_updated":"2024-02-09T13:44:36.854036","name":"PostgREST","code":"postgrest","role":["postgrest"],"type":"web","default":false,"popularity":null,"descr":null,"ports":{"public":["3000","8080"]},"commercial":null,"subscription":null,"autodeploy":null,"suggested":null,"dependency":null,"avoid_render":null,"price":null,"icon":null,"category_id":null,"parent_app_id":null,"full_description":null,"description":null,"plan_type":null,"ansible_var":null,"repo_dir":null,"cpu":"0.0","ram_size":null,"disk_size":null,"dockerhub_image":"","form":null,"requirements":null,"docker_image_is_internal":true,"custom_preset":{"volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"restart":"always"},"category":[null],"group":[],"versions":[{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"}],"links":[{"url":"https://postgrest.org","title":"Vendor","type":"vendor","follow":false},{"repo_owner":"PostgREST","repo_name":"postgrest","type":"github","follow":false}],"version":{"_id":556,"name":"Postgrest","version":"latest","update_status":"published","tag":"latest"},"network":["lsnd2ttg3ivhw3db5"],"restart":"always","timestamp":"2024-02-17T12:50:01.659Z","volumes":[],"environment":[{"key":"PGRST_OPENAPI_SERVER_PROXY_URI","value":"http://DOMAIN_NAME:3000"},{"key":"PGRST_DB_ANON_ROLE","value":"anon"},{"key":"PGRST_DB_SCHEMA","value":"public"},{"key":"PGHOST","value":"postgresql_host"},{"key":"PGPORT","value":"5432"},{"key":"PGUSER","value":"user"},{"key":"PGDATABASE","value":"database"},{"key":"PGPASSWORD ","value":"password"}],"shared_ports":[{"host_port":"3000","container_port":"3000"}],"domain":"","dockerhub_name":"mariadb:11.3.2-latest"}],"feature":[],"service":[],"servers_count":3,"project_name":"Databases","custom_stack_code":"databases"}} \ No newline at end of file From 15744042843c8fe443e8438b2ae378ff0091fae0 Mon Sep 17 00:00:00 2001 From: vsilent Date: Fri, 12 Apr 2024 11:54:35 +0300 Subject: [PATCH 18/18] social field should be optional --- src/forms/user.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/forms/user.rs b/src/forms/user.rs index 0c58d75f..9009c7ac 100644 --- a/src/forms/user.rs +++ b/src/forms/user.rs @@ -25,7 +25,7 @@ pub struct User { pub email: String, #[serde(rename = "email_confirmed")] pub email_confirmed: bool, - pub social: bool, + pub social: Option, pub website: Option, pub currency: Value, pub phone: Option,