mirror of
https://github.com/moghtech/komodo.git
synced 2025-12-05 19:17:36 -06:00
1.18.0 (#555)
* ferretdb v2 now that they support arm64 * remove ignored for sqlite * tweak * mongo copier * 1.17.6 * primary name is ferretdb option * give doc counts * fmt * print document count * komodo util versioned seperately * add copy startup sleep * FerretDB v2 upgrade guide * tweak docs * tweak * tweak * add link to upgrade guide for ferretdb v1 users * fix copy batch size * multi arch util setup * util use workspace version * clarify behavior re root_directory * finished copying database log * update to rust:1.87.0 * fix: reset rename editor on navigate * loosen naming restrictions for most resource types * added support for ntfy email forwarding (#493) * fix alerter email option docs * remove logging directive in example compose - can be done at user discretion * more granular permissions * fix initial fe type errors * fix the new perm typing * add dedicated ws routes to connect to deployment / stack terminal, using the permissioning on those entities * frontend should convey / respect the perms * use IndexSet for SpecificPermission * finish IndexSet * match regex or wildcard resource name pattern * gen ts client * implement new terminal components which use the container / deployment / stack specific permissioned endpoints * user group backend "everyone" support * bump to 1.18.0 for significant permissioning changes * ts 1.18.0 * permissions FE in prog * FE permissions assignment working * user group all map uses ordered IndexMap for consistency * improve user group toml and fix execute bug * URL encode names in webhook urls * UI support configure 'everyone' User Group * sync handle toggling user group everyone * user group table show everyone enabled * sync will update user group "everyone" * Inspect Deployment / Stack containers directly * fix InspectStackContainer container name * Deployment / stack service inspect * Stack / Deployment inherit Logs, Inspect and Terminal from their attached server for user * fix compose down not capitalized * don't use tabs * more descriptive permission table titles * different localstorage for permissions show all * network / image / volume inspect don't require inspect perms * fix container inspect * fix list container undefined error * prcesses list gated UI * remove localstorage on permission table expansion * fix ug sync handling of all zero permissions * pretty log startup config * implement actually pretty logging initial config * fix user permissions when api returns string * fix container info table * util based on bullseye-slim * permission toml specific skip_serializing_if = "IndexSet::is_empty" * container tab permissions reversed * reorder pretty logging stuff to be together * update docs with permissioning info * tweak docs * update roadmap --------- Co-authored-by: FelixBreitweiser <felix.breitweiser@uni-siegen.de>
This commit is contained in:
141
Cargo.lock
generated
141
Cargo.lock
generated
@@ -165,9 +165,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "1.6.2"
|
||||
version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fcc63c9860579e4cb396239570e979376e70aab79e496621748a09913f8b36"
|
||||
checksum = "02a18fd934af6ae7ca52410d4548b98eb895aab0f1ea417d168d85db1434a141"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -254,9 +254,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ec2"
|
||||
version = "1.124.0"
|
||||
version = "1.134.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6746a315a5446304942f057e6a072347dad558d23bfbda64c42b9a236f824013"
|
||||
checksum = "a9a84e95f739e79d157409fa00e41008dabd181022193dabfabc68ddccbd6055"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -271,16 +271,15 @@ dependencies = [
|
||||
"aws-types",
|
||||
"fastrand",
|
||||
"http 0.2.12",
|
||||
"once_cell",
|
||||
"regex-lite",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.65.0"
|
||||
version = "1.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8efec445fb78df585327094fcef4cad895b154b58711e504db7a93c41aa27151"
|
||||
checksum = "83447efb7179d8e2ad2afb15ceb9c113debbc2ecdf109150e338e2e28b86190b"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -294,16 +293,15 @@ dependencies = [
|
||||
"bytes",
|
||||
"fastrand",
|
||||
"http 0.2.12",
|
||||
"once_cell",
|
||||
"regex-lite",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.66.0"
|
||||
version = "1.71.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e49cca619c10e7b002dc8e66928ceed66ab7f56c1a3be86c5437bf2d8d89bba"
|
||||
checksum = "c5f9bfbbda5e2b9fe330de098f14558ee8b38346408efe9f2e9cee82dc1636a4"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -317,16 +315,15 @@ dependencies = [
|
||||
"bytes",
|
||||
"fastrand",
|
||||
"http 0.2.12",
|
||||
"once_cell",
|
||||
"regex-lite",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.66.0"
|
||||
version = "1.71.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7420479eac0a53f776cc8f0d493841ffe58ad9d9783f3947be7265784471b47a"
|
||||
checksum = "e17b984a66491ec08b4f4097af8911251db79296b3e4a763060b45805746264f"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -341,7 +338,6 @@ dependencies = [
|
||||
"aws-types",
|
||||
"fastrand",
|
||||
"http 0.2.12",
|
||||
"once_cell",
|
||||
"regex-lite",
|
||||
"tracing",
|
||||
]
|
||||
@@ -419,7 +415,7 @@ dependencies = [
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-native-certs 0.8.1",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -651,7 +647,7 @@ dependencies = [
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -795,9 +791,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bollard"
|
||||
version = "0.18.1"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30"
|
||||
checksum = "af706e9dc793491dd382c99c22fde6e9934433d4cc0d6a4b34eb2cdc57a5c917"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bollard-stubs",
|
||||
@@ -828,20 +824,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bollard-stubs"
|
||||
version = "1.47.1-rc.27.3.1"
|
||||
version = "1.48.2-rc.28.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f179cfbddb6e77a5472703d4b30436bff32929c0aa8a9008ecf23d1d3cdd0da"
|
||||
checksum = "79cdf0fccd5341b38ae0be74b74410bdd5eceeea8876dc149a13edfe57e3b259"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bson"
|
||||
version = "2.14.0"
|
||||
version = "2.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8113ff51309e2779e8785a246c10fb783e8c2452f134d6257fd71cc03ccd6c"
|
||||
checksum = "7969a9ba84b0ff843813e7249eed1678d9b6607ce5a3b8f0a47af3fcf7978e6e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.22.1",
|
||||
@@ -893,7 +890,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cache"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"tokio",
|
||||
@@ -996,9 +993,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.37"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -1006,9 +1003,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.37"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -1060,7 +1057,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "command"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"formatting",
|
||||
@@ -1523,9 +1520,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "english-to-cron"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a13a7d5e0ab3872c3ee478366eae624d89ab953d30276b0eee08169774ceb73"
|
||||
checksum = "e26fb7377cbec9a94f60428e6e6afbe10c699a14639b4d3d4b67b25c0bbe0806"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
@@ -1544,7 +1541,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "environment_file"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
@@ -1624,7 +1621,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "formatting"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"serror",
|
||||
]
|
||||
@@ -1786,7 +1783,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "git"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cache",
|
||||
@@ -2160,7 +2157,7 @@ dependencies = [
|
||||
"http 1.3.1",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-native-certs 0.8.1",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -2523,7 +2520,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_cli"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2539,7 +2536,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_client"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2551,6 +2548,7 @@ dependencies = [
|
||||
"derive_variants",
|
||||
"envy",
|
||||
"futures",
|
||||
"indexmap 2.9.0",
|
||||
"mongo_indexed",
|
||||
"partial_derive2",
|
||||
"reqwest",
|
||||
@@ -2570,7 +2568,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_core"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -2599,6 +2597,7 @@ dependencies = [
|
||||
"git",
|
||||
"hex",
|
||||
"hmac",
|
||||
"indexmap 2.9.0",
|
||||
"jsonwebtoken",
|
||||
"komodo_client",
|
||||
"logger",
|
||||
@@ -2608,7 +2607,6 @@ dependencies = [
|
||||
"nom_pem",
|
||||
"octorust",
|
||||
"openidconnect",
|
||||
"ordered_hash_map",
|
||||
"partial_derive2",
|
||||
"periphery_client",
|
||||
"rand 0.9.1",
|
||||
@@ -2616,7 +2614,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"resolver_api",
|
||||
"response",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
@@ -2639,7 +2637,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_periphery"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2667,7 +2665,7 @@ dependencies = [
|
||||
"resolver_api",
|
||||
"response",
|
||||
"run_command",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
@@ -2681,6 +2679,21 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "komodo_util"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenvy",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"mungos",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -2757,7 +2770,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "logger"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -3512,13 +3525,13 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
"reqwest",
|
||||
"resolver_api",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_qs",
|
||||
@@ -3718,7 +3731,7 @@ dependencies = [
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"socket2",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
@@ -3737,7 +3750,7 @@ dependencies = [
|
||||
"rand 0.9.1",
|
||||
"ring",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.12",
|
||||
@@ -3921,7 +3934,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-native-certs 0.8.1",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
@@ -4040,7 +4053,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "response"
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4175,16 +4188,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.26"
|
||||
version = "0.23.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.103.1",
|
||||
"rustls-webpki 0.103.3",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -4252,9 +4265,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.1"
|
||||
version = "0.103.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
|
||||
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
@@ -4854,9 +4867,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.35.0"
|
||||
version = "0.35.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b897c8ea620e181c7955369a31be5f48d9a9121cb59fd33ecef9ff2a34323422"
|
||||
checksum = "79251336d17c72d9762b8b54be4befe38d2db56fbbc0241396d70f173c39d47a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memchr",
|
||||
@@ -5016,9 +5029,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.44.2"
|
||||
version = "1.45.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
|
||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -5059,7 +5072,7 @@ version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
|
||||
dependencies = [
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -5083,7 +5096,7 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-native-certs 0.8.1",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
@@ -5225,9 +5238,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.2"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
|
||||
checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bytes",
|
||||
@@ -5367,7 +5380,7 @@ dependencies = [
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.9.1",
|
||||
"rustls 0.23.26",
|
||||
"rustls 0.23.27",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror 2.0.12",
|
||||
@@ -5502,9 +5515,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
||||
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||
dependencies = [
|
||||
"getrandom 0.3.2",
|
||||
"js-sys",
|
||||
|
||||
26
Cargo.toml
26
Cargo.toml
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.17.5"
|
||||
version = "1.18.0"
|
||||
edition = "2024"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -45,7 +45,7 @@ svi = "1.0.1"
|
||||
|
||||
# ASYNC
|
||||
reqwest = { version = "0.12.15", default-features = false, features = ["json", "stream", "rustls-tls-native-roots"] }
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
||||
tokio = { version = "1.45.1", features = ["full"] }
|
||||
tokio-util = { version = "0.7.15", features = ["io", "codec"] }
|
||||
tokio-stream = { version = "0.1.17", features = ["sync"] }
|
||||
pin-project-lite = "0.2.16"
|
||||
@@ -56,12 +56,12 @@ arc-swap = "1.7.1"
|
||||
# SERVER
|
||||
tokio-tungstenite = { version = "0.26.2", features = ["rustls-tls-native-roots"] }
|
||||
axum-extra = { version = "0.10.1", features = ["typed-header"] }
|
||||
tower-http = { version = "0.6.2", features = ["fs", "cors"] }
|
||||
tower-http = { version = "0.6.4", features = ["fs", "cors"] }
|
||||
axum-server = { version = "0.7.2", features = ["tls-rustls"] }
|
||||
axum = { version = "0.8.4", features = ["ws", "json", "macros"] }
|
||||
|
||||
# SER/DE
|
||||
ordered_hash_map = { version = "0.4.0", features = ["serde"] }
|
||||
indexmap = { version = "2.9.0", features = ["serde"] }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
strum = { version = "0.27.1", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
@@ -83,19 +83,19 @@ opentelemetry = "0.29.1"
|
||||
tracing = "0.1.41"
|
||||
|
||||
# CONFIG
|
||||
clap = { version = "4.5.37", features = ["derive"] }
|
||||
clap = { version = "4.5.38", features = ["derive"] }
|
||||
dotenvy = "0.15.7"
|
||||
envy = "0.4.2"
|
||||
|
||||
# CRYPTO / AUTH
|
||||
uuid = { version = "1.16.0", features = ["v4", "fast-rng", "serde"] }
|
||||
uuid = { version = "1.17.0", features = ["v4", "fast-rng", "serde"] }
|
||||
jsonwebtoken = { version = "9.3.1", default-features = false }
|
||||
openidconnect = "4.0.0"
|
||||
urlencoding = "2.1.3"
|
||||
nom_pem = "4.0.0"
|
||||
bcrypt = "0.17.0"
|
||||
base64 = "0.22.1"
|
||||
rustls = "0.23.26"
|
||||
rustls = "0.23.27"
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.9"
|
||||
rand = "0.9.1"
|
||||
@@ -103,16 +103,16 @@ hex = "0.4.3"
|
||||
|
||||
# SYSTEM
|
||||
portable-pty = "0.9.0"
|
||||
bollard = "0.18.1"
|
||||
sysinfo = "0.35.0"
|
||||
bollard = "0.19.0"
|
||||
sysinfo = "0.35.1"
|
||||
|
||||
# CLOUD
|
||||
aws-config = "1.6.2"
|
||||
aws-sdk-ec2 = "1.124.0"
|
||||
aws-config = "1.6.3"
|
||||
aws-sdk-ec2 = "1.134.0"
|
||||
aws-credential-types = "1.2.3"
|
||||
|
||||
## CRON
|
||||
english-to-cron = "0.1.4"
|
||||
english-to-cron = "0.1.6"
|
||||
chrono-tz = "0.10.3"
|
||||
chrono = "0.4.41"
|
||||
croner = "2.1.0"
|
||||
@@ -126,4 +126,4 @@ wildcard = "0.3.0"
|
||||
colored = "3.0.0"
|
||||
regex = "1.11.1"
|
||||
bytes = "1.10.1"
|
||||
bson = "2.14.0"
|
||||
bson = "2.15.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Builds the Komodo Core and Periphery binaries
|
||||
## Builds the Komodo Core, Periphery, and Util binaries
|
||||
## for a specific architecture.
|
||||
|
||||
FROM rust:1.86.0-bullseye AS builder
|
||||
FROM rust:1.87.0-bullseye AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
@@ -10,17 +10,20 @@ COPY ./client/core/rs ./client/core/rs
|
||||
COPY ./client/periphery ./client/periphery
|
||||
COPY ./bin/core ./bin/core
|
||||
COPY ./bin/periphery ./bin/periphery
|
||||
COPY ./bin/util ./bin/util
|
||||
|
||||
# Compile bin
|
||||
RUN \
|
||||
cargo build -p komodo_core --release && \
|
||||
cargo build -p komodo_periphery --release
|
||||
cargo build -p komodo_periphery --release && \
|
||||
cargo build -p komodo_util --release
|
||||
|
||||
# Copy just the binaries to scratch image
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /builder/target/release/core /core
|
||||
COPY --from=builder /builder/target/release/periphery /periphery
|
||||
COPY --from=builder /builder/target/release/util /util
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/moghtech/komodo
|
||||
LABEL org.opencontainers.image.description="Komodo Binaries"
|
||||
|
||||
@@ -39,7 +39,6 @@ svi.workspace = true
|
||||
# external
|
||||
aws-credential-types.workspace = true
|
||||
tokio-tungstenite.workspace = true
|
||||
ordered_hash_map.workspace = true
|
||||
english-to-cron.workspace = true
|
||||
openidconnect.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
@@ -54,6 +53,7 @@ serde_json.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
typeshare.workspace = true
|
||||
chrono-tz.workspace = true
|
||||
indexmap.workspace = true
|
||||
octorust.workspace = true
|
||||
wildcard.workspace = true
|
||||
arc-swap.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
# Build Core
|
||||
FROM rust:1.86.0-bullseye AS core-builder
|
||||
FROM rust:1.87.0-bullseye AS core-builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
@@ -130,13 +130,15 @@ pub async fn send_alert_to_alerter(
|
||||
)
|
||||
})
|
||||
}
|
||||
AlerterEndpoint::Ntfy(NtfyAlerterEndpoint { url }) => {
|
||||
ntfy::send_alert(url, alert).await.with_context(|| {
|
||||
format!(
|
||||
"Failed to send alert to ntfy Alerter {}",
|
||||
alerter.name
|
||||
)
|
||||
})
|
||||
AlerterEndpoint::Ntfy(NtfyAlerterEndpoint { url, email }) => {
|
||||
ntfy::send_alert(url, email.as_deref(), alert)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to send alert to ntfy Alerter {}",
|
||||
alerter.name
|
||||
)
|
||||
})
|
||||
}
|
||||
AlerterEndpoint::Pushover(PushoverAlerterEndpoint { url }) => {
|
||||
pushover::send_alert(url, alert).await.with_context(|| {
|
||||
|
||||
@@ -5,6 +5,7 @@ use super::*;
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn send_alert(
|
||||
url: &str,
|
||||
email: Option<&str>,
|
||||
alert: &Alert,
|
||||
) -> anyhow::Result<()> {
|
||||
let level = fmt_level(alert.level);
|
||||
@@ -224,22 +225,27 @@ pub async fn send_alert(
|
||||
};
|
||||
|
||||
if !content.is_empty() {
|
||||
send_message(url, content).await?;
|
||||
send_message(url, email, content).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_message(
|
||||
url: &str,
|
||||
email: Option<&str>,
|
||||
content: String,
|
||||
) -> anyhow::Result<()> {
|
||||
let response = http_client()
|
||||
let mut request = http_client()
|
||||
.post(url)
|
||||
.header("Title", "ntfy Alert")
|
||||
.body(content)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to send message")?;
|
||||
.body(content);
|
||||
|
||||
if let Some(email) = email {
|
||||
request = request.header("X-Email", email);
|
||||
}
|
||||
|
||||
let response =
|
||||
request.send().await.context("Failed to send message")?;
|
||||
|
||||
let status = response.status();
|
||||
if status.is_success() {
|
||||
|
||||
@@ -39,7 +39,8 @@ use crate::{
|
||||
random_string,
|
||||
update::update_update,
|
||||
},
|
||||
resource::{self, refresh_action_state_cache},
|
||||
permission::get_check_permissions,
|
||||
resource::refresh_action_state_cache,
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
@@ -71,10 +72,10 @@ impl Resolve<ExecuteArgs> for RunAction {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut action = resource::get_check_permissions::<Action>(
|
||||
let mut action = get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
alert::send_alert_to_alerter, helpers::update::update_update,
|
||||
resource::get_check_permissions,
|
||||
permission::get_check_permissions,
|
||||
};
|
||||
|
||||
use super::ExecuteArgs;
|
||||
@@ -26,7 +26,7 @@ impl Resolve<ExecuteArgs> for TestAlerter {
|
||||
let alerter = get_check_permissions::<Alerter>(
|
||||
&self.alerter,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ use crate::{
|
||||
registry_token,
|
||||
update::{init_execution_update, update_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::{self, refresh_build_state_cache},
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
@@ -80,10 +81,10 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut build = resource::get_check_permissions::<Build>(
|
||||
let mut build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -513,10 +514,10 @@ impl Resolve<ExecuteArgs> for CancelBuild {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ use crate::{
|
||||
update::update_update,
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::action_states,
|
||||
};
|
||||
@@ -68,10 +69,10 @@ async fn setup_deployment_execution(
|
||||
deployment: &str,
|
||||
user: &User,
|
||||
) -> anyhow::Result<(Deployment, Server)> {
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use komodo_client::{
|
||||
api::execute::*,
|
||||
entities::{
|
||||
Operation,
|
||||
permission::PermissionLevel,
|
||||
update::{Log, Update},
|
||||
user::User,
|
||||
},
|
||||
@@ -86,18 +87,6 @@ pub enum ExecuteRequest {
|
||||
PruneBuildx(PruneBuildx),
|
||||
PruneSystem(PruneSystem),
|
||||
|
||||
// ==== DEPLOYMENT ====
|
||||
Deploy(Deploy),
|
||||
BatchDeploy(BatchDeploy),
|
||||
PullDeployment(PullDeployment),
|
||||
StartDeployment(StartDeployment),
|
||||
RestartDeployment(RestartDeployment),
|
||||
PauseDeployment(PauseDeployment),
|
||||
UnpauseDeployment(UnpauseDeployment),
|
||||
StopDeployment(StopDeployment),
|
||||
DestroyDeployment(DestroyDeployment),
|
||||
BatchDestroyDeployment(BatchDestroyDeployment),
|
||||
|
||||
// ==== STACK ====
|
||||
DeployStack(DeployStack),
|
||||
BatchDeployStack(BatchDeployStack),
|
||||
@@ -113,6 +102,18 @@ pub enum ExecuteRequest {
|
||||
DestroyStack(DestroyStack),
|
||||
BatchDestroyStack(BatchDestroyStack),
|
||||
|
||||
// ==== DEPLOYMENT ====
|
||||
Deploy(Deploy),
|
||||
BatchDeploy(BatchDeploy),
|
||||
PullDeployment(PullDeployment),
|
||||
StartDeployment(StartDeployment),
|
||||
RestartDeployment(RestartDeployment),
|
||||
PauseDeployment(PauseDeployment),
|
||||
UnpauseDeployment(UnpauseDeployment),
|
||||
StopDeployment(StopDeployment),
|
||||
DestroyDeployment(DestroyDeployment),
|
||||
BatchDestroyDeployment(BatchDestroyDeployment),
|
||||
|
||||
// ==== BUILD ====
|
||||
RunBuild(RunBuild),
|
||||
BatchRunBuild(BatchRunBuild),
|
||||
@@ -298,6 +299,7 @@ async fn batch_execute<E: BatchExecute>(
|
||||
pattern,
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -21,7 +21,8 @@ use tokio::sync::Mutex;
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
helpers::{procedure::execute_procedure, update::update_update},
|
||||
resource::{self, refresh_procedure_state_cache},
|
||||
permission::get_check_permissions,
|
||||
resource::refresh_procedure_state_cache,
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
@@ -70,10 +71,10 @@ fn resolve_inner(
|
||||
>,
|
||||
> {
|
||||
Box::pin(async move {
|
||||
let procedure = resource::get_check_permissions::<Procedure>(
|
||||
let procedure = get_check_permissions::<Procedure>(
|
||||
&procedure,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ use crate::{
|
||||
query::get_variables_and_secrets,
|
||||
update::update_update,
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::{self, refresh_repo_state_cache},
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
@@ -73,10 +74,10 @@ impl Resolve<ExecuteArgs> for CloneRepo {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut repo = resource::get_check_permissions::<Repo>(
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -185,10 +186,10 @@ impl Resolve<ExecuteArgs> for PullRepo {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut repo = resource::get_check_permissions::<Repo>(
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -340,10 +341,10 @@ impl Resolve<ExecuteArgs> for BuildRepo {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut repo = resource::get_check_permissions::<Repo>(
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -651,10 +652,10 @@ impl Resolve<ExecuteArgs> for CancelRepoBuild {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
helpers::{periphery_client, update::update_update},
|
||||
monitor::update_cache_for_server,
|
||||
resource,
|
||||
permission::get_check_permissions,
|
||||
state::action_states,
|
||||
};
|
||||
|
||||
@@ -27,10 +27,10 @@ impl Resolve<ExecuteArgs> for StartContainer {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -81,10 +81,10 @@ impl Resolve<ExecuteArgs> for RestartContainer {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -137,10 +137,10 @@ impl Resolve<ExecuteArgs> for PauseContainer {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -191,10 +191,10 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -247,10 +247,10 @@ impl Resolve<ExecuteArgs> for StopContainer {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -309,10 +309,10 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
|
||||
signal,
|
||||
time,
|
||||
} = self;
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -365,10 +365,10 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -415,10 +415,10 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -467,10 +467,10 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -517,10 +517,10 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -569,10 +569,10 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -619,10 +619,10 @@ impl Resolve<ExecuteArgs> for PruneContainers {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -675,10 +675,10 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -726,10 +726,10 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -780,10 +780,10 @@ impl Resolve<ExecuteArgs> for DeleteImage {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -828,10 +828,10 @@ impl Resolve<ExecuteArgs> for PruneImages {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -880,10 +880,10 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -931,10 +931,10 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -983,10 +983,10 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1035,10 +1035,10 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1087,10 +1087,10 @@ impl Resolve<ExecuteArgs> for PruneSystem {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ use crate::{
|
||||
update::{add_update_without_send, update_update},
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
stack::{execute::execute_compose, get_stack_and_server},
|
||||
state::{action_states, db_client},
|
||||
@@ -69,7 +70,7 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
let (mut stack, server) = get_stack_and_server(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
@@ -320,10 +321,10 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
RefreshStackCache {
|
||||
@@ -402,11 +403,8 @@ impl Resolve<ExecuteArgs> for BatchPullStack {
|
||||
ExecuteArgs { user, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchPullStack>(
|
||||
&self.pattern,
|
||||
user,
|
||||
)
|
||||
.await?,
|
||||
super::batch_execute::<BatchPullStack>(&self.pattern, user)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -498,7 +496,7 @@ impl Resolve<ExecuteArgs> for PullStack {
|
||||
let (stack, server) = get_stack_and_server(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -29,7 +29,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
api::write::WriteArgs,
|
||||
helpers::{query::get_id_to_tags, update::update_update},
|
||||
resource,
|
||||
permission::get_check_permissions,
|
||||
state::{action_states, db_client},
|
||||
sync::{
|
||||
AllResourcesById, ResourceSyncTrait,
|
||||
@@ -54,9 +54,11 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
resource_type: match_resource_type,
|
||||
resources: match_resources,
|
||||
} = self;
|
||||
let sync = resource::get_check_permissions::<
|
||||
entities::sync::ResourceSync,
|
||||
>(&sync, user, PermissionLevel::Execute)
|
||||
let sync = get_check_permissions::<entities::sync::ResourceSync>(
|
||||
&sync,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// get the action state for the sync (or insert default).
|
||||
|
||||
@@ -12,6 +12,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_state_cache, action_states},
|
||||
};
|
||||
@@ -24,10 +25,10 @@ impl Resolve<ReadArgs> for GetAction {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Action> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Action>(
|
||||
get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -45,8 +46,13 @@ impl Resolve<ReadArgs> for ListActions {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Action>(self.query, user, &all_tags)
|
||||
.await?,
|
||||
resource::list_for_user::<Action>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +69,10 @@ impl Resolve<ReadArgs> for ListFullActions {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Action>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -75,10 +84,10 @@ impl Resolve<ReadArgs> for GetActionActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ActionActionState> {
|
||||
let action = resource::get_check_permissions::<Action>(
|
||||
let action = get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -99,6 +108,7 @@ impl Resolve<ReadArgs> for GetActionsSummary {
|
||||
let actions = resource::list_full_for_user::<Action>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -16,7 +16,7 @@ use mungos::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
config::core_config, resource::get_resource_ids_for_user,
|
||||
config::core_config, permission::get_resource_ids_for_user,
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ use mungos::mongodb::bson::doc;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags, resource, state::db_client,
|
||||
helpers::query::get_all_tags, permission::get_check_permissions,
|
||||
resource, state::db_client,
|
||||
};
|
||||
|
||||
use super::ReadArgs;
|
||||
@@ -22,10 +23,10 @@ impl Resolve<ReadArgs> for GetAlerter {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Alerter> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
get_check_permissions::<Alerter>(
|
||||
&self.alerter,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -43,8 +44,13 @@ impl Resolve<ReadArgs> for ListAlerters {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Alerter>(self.query, user, &all_tags)
|
||||
.await?,
|
||||
resource::list_for_user::<Alerter>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -61,7 +67,10 @@ impl Resolve<ReadArgs> for ListFullAlerters {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Alerter>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{
|
||||
action_states, build_state_cache, db_client, github_client,
|
||||
@@ -36,10 +37,10 @@ impl Resolve<ReadArgs> for GetBuild {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Build> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Build>(
|
||||
get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -57,8 +58,13 @@ impl Resolve<ReadArgs> for ListBuilds {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Build>(self.query, user, &all_tags)
|
||||
.await?,
|
||||
resource::list_for_user::<Build>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -75,7 +81,10 @@ impl Resolve<ReadArgs> for ListFullBuilds {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Build>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -87,10 +96,10 @@ impl Resolve<ReadArgs> for GetBuildActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<BuildActionState> {
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -111,6 +120,7 @@ impl Resolve<ReadArgs> for GetBuildsSummary {
|
||||
let builds = resource::list_full_for_user::<Build>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -218,10 +228,10 @@ impl Resolve<ReadArgs> for ListBuildVersions {
|
||||
patch,
|
||||
limit,
|
||||
} = self;
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&build,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -274,7 +284,10 @@ impl Resolve<ReadArgs> for ListCommonBuildExtraArgs {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let builds = resource::list_full_for_user::<Build>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await
|
||||
.context("failed to get resources matching query")?;
|
||||
@@ -306,10 +319,10 @@ impl Resolve<ReadArgs> for GetBuildWebhookEnabled {
|
||||
});
|
||||
};
|
||||
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ use mungos::mongodb::bson::doc;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags, resource, state::db_client,
|
||||
helpers::query::get_all_tags, permission::get_check_permissions,
|
||||
resource, state::db_client,
|
||||
};
|
||||
|
||||
use super::ReadArgs;
|
||||
@@ -22,10 +23,10 @@ impl Resolve<ReadArgs> for GetBuilder {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Builder> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Builder>(
|
||||
get_check_permissions::<Builder>(
|
||||
&self.builder,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -43,8 +44,13 @@ impl Resolve<ReadArgs> for ListBuilders {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Builder>(self.query, user, &all_tags)
|
||||
.await?,
|
||||
resource::list_for_user::<Builder>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -61,7 +67,10 @@ impl Resolve<ReadArgs> for ListFullBuilders {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Builder>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
|
||||
@@ -8,19 +8,22 @@ use komodo_client::{
|
||||
Deployment, DeploymentActionState, DeploymentConfig,
|
||||
DeploymentListItem, DeploymentState,
|
||||
},
|
||||
docker::container::ContainerStats,
|
||||
docker::container::{Container, ContainerStats},
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
server::{Server, ServerState},
|
||||
update::Log,
|
||||
},
|
||||
};
|
||||
use periphery_client::api;
|
||||
use periphery_client::api::{self, container::InspectContainer};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{periphery_client, query::get_all_tags},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, deployment_status_cache},
|
||||
state::{
|
||||
action_states, deployment_status_cache, server_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
use super::ReadArgs;
|
||||
@@ -31,10 +34,10 @@ impl Resolve<ReadArgs> for GetDeployment {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Deployment> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -53,7 +56,10 @@ impl Resolve<ReadArgs> for ListDeployments {
|
||||
};
|
||||
let only_update_available = self.query.specific.update_available;
|
||||
let deployments = resource::list_for_user::<Deployment>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?;
|
||||
let deployments = if only_update_available {
|
||||
@@ -80,7 +86,10 @@ impl Resolve<ReadArgs> for ListFullDeployments {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Deployment>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -92,10 +101,10 @@ impl Resolve<ReadArgs> for GetDeploymentContainer {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetDeploymentContainerResponse> {
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let status = deployment_status_cache()
|
||||
@@ -126,10 +135,10 @@ impl Resolve<ReadArgs> for GetDeploymentLog {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = resource::get_check_permissions::<Deployment>(
|
||||
} = get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
if server_id.is_empty() {
|
||||
@@ -164,10 +173,10 @@ impl Resolve<ReadArgs> for SearchDeploymentLog {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = resource::get_check_permissions::<Deployment>(
|
||||
} = get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
if server_id.is_empty() {
|
||||
@@ -188,6 +197,50 @@ impl Resolve<ReadArgs> for SearchDeploymentLog {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectDeploymentContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Container> {
|
||||
let InspectDeploymentContainer { deployment } = self;
|
||||
let Deployment {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
user,
|
||||
PermissionLevel::Read.inspect(),
|
||||
)
|
||||
.await?;
|
||||
if server_id.is_empty() {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect deployment, not attached to any server"
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let server = resource::get::<Server>(&server_id).await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect container: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)?
|
||||
.request(InspectContainer { name })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetDeploymentStats {
|
||||
async fn resolve(
|
||||
self,
|
||||
@@ -197,10 +250,10 @@ impl Resolve<ReadArgs> for GetDeploymentStats {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = resource::get_check_permissions::<Deployment>(
|
||||
} = get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
if server_id.is_empty() {
|
||||
@@ -222,10 +275,10 @@ impl Resolve<ReadArgs> for GetDeploymentActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<DeploymentActionState> {
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -246,6 +299,7 @@ impl Resolve<ReadArgs> for GetDeploymentsSummary {
|
||||
let deployments = resource::list_full_for_user::<Deployment>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -289,7 +343,10 @@ impl Resolve<ReadArgs> for ListCommonDeploymentExtraArgs {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let deployments = resource::list_full_for_user::<Deployment>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await
|
||||
.context("failed to get resources matching query")?;
|
||||
|
||||
@@ -11,6 +11,7 @@ use komodo_client::{
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig},
|
||||
config::{DockerRegistry, GitProvider},
|
||||
permission::PermissionLevel,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
sync::ResourceSync,
|
||||
@@ -71,7 +72,7 @@ enum ReadRequest {
|
||||
|
||||
// ==== USER ====
|
||||
GetUsername(GetUsername),
|
||||
GetPermissionLevel(GetPermissionLevel),
|
||||
GetPermission(GetPermission),
|
||||
FindUser(FindUser),
|
||||
ListUsers(ListUsers),
|
||||
ListApiKeys(ListApiKeys),
|
||||
@@ -123,6 +124,25 @@ enum ReadRequest {
|
||||
ListComposeProjects(ListComposeProjects),
|
||||
ListTerminals(ListTerminals),
|
||||
|
||||
// ==== SERVER STATS ====
|
||||
GetSystemInformation(GetSystemInformation),
|
||||
GetSystemStats(GetSystemStats),
|
||||
ListSystemProcesses(ListSystemProcesses),
|
||||
|
||||
// ==== STACK ====
|
||||
GetStacksSummary(GetStacksSummary),
|
||||
GetStack(GetStack),
|
||||
GetStackActionState(GetStackActionState),
|
||||
GetStackWebhooksEnabled(GetStackWebhooksEnabled),
|
||||
GetStackLog(GetStackLog),
|
||||
SearchStackLog(SearchStackLog),
|
||||
InspectStackContainer(InspectStackContainer),
|
||||
ListStacks(ListStacks),
|
||||
ListFullStacks(ListFullStacks),
|
||||
ListStackServices(ListStackServices),
|
||||
ListCommonStackExtraArgs(ListCommonStackExtraArgs),
|
||||
ListCommonStackBuildExtraArgs(ListCommonStackBuildExtraArgs),
|
||||
|
||||
// ==== DEPLOYMENT ====
|
||||
GetDeploymentsSummary(GetDeploymentsSummary),
|
||||
GetDeployment(GetDeployment),
|
||||
@@ -131,6 +151,7 @@ enum ReadRequest {
|
||||
GetDeploymentStats(GetDeploymentStats),
|
||||
GetDeploymentLog(GetDeploymentLog),
|
||||
SearchDeploymentLog(SearchDeploymentLog),
|
||||
InspectDeploymentContainer(InspectDeploymentContainer),
|
||||
ListDeployments(ListDeployments),
|
||||
ListFullDeployments(ListFullDeployments),
|
||||
ListCommonDeploymentExtraArgs(ListCommonDeploymentExtraArgs),
|
||||
@@ -162,19 +183,6 @@ enum ReadRequest {
|
||||
ListResourceSyncs(ListResourceSyncs),
|
||||
ListFullResourceSyncs(ListFullResourceSyncs),
|
||||
|
||||
// ==== STACK ====
|
||||
GetStacksSummary(GetStacksSummary),
|
||||
GetStack(GetStack),
|
||||
GetStackActionState(GetStackActionState),
|
||||
GetStackWebhooksEnabled(GetStackWebhooksEnabled),
|
||||
GetStackLog(GetStackLog),
|
||||
SearchStackLog(SearchStackLog),
|
||||
ListStacks(ListStacks),
|
||||
ListFullStacks(ListFullStacks),
|
||||
ListStackServices(ListStackServices),
|
||||
ListCommonStackExtraArgs(ListCommonStackExtraArgs),
|
||||
ListCommonStackBuildExtraArgs(ListCommonStackBuildExtraArgs),
|
||||
|
||||
// ==== BUILDER ====
|
||||
GetBuildersSummary(GetBuildersSummary),
|
||||
GetBuilder(GetBuilder),
|
||||
@@ -203,11 +211,6 @@ enum ReadRequest {
|
||||
ListAlerts(ListAlerts),
|
||||
GetAlert(GetAlert),
|
||||
|
||||
// ==== SERVER STATS ====
|
||||
GetSystemInformation(GetSystemInformation),
|
||||
GetSystemStats(GetSystemStats),
|
||||
ListSystemProcesses(ListSystemProcesses),
|
||||
|
||||
// ==== VARIABLE ====
|
||||
GetVariable(GetVariable),
|
||||
ListVariables(ListVariables),
|
||||
@@ -396,16 +399,19 @@ impl Resolve<ReadArgs> for ListGitProvidersFromConfig {
|
||||
resource::list_full_for_user::<Build>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[]
|
||||
),
|
||||
resource::list_full_for_user::<Repo>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[]
|
||||
),
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[]
|
||||
),
|
||||
)?;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use komodo_client::{
|
||||
api::read::{
|
||||
GetPermissionLevel, GetPermissionLevelResponse, ListPermissions,
|
||||
GetPermission, GetPermissionResponse, ListPermissions,
|
||||
ListPermissionsResponse, ListUserTargetPermissions,
|
||||
ListUserTargetPermissionsResponse,
|
||||
},
|
||||
@@ -35,13 +35,13 @@ impl Resolve<ReadArgs> for ListPermissions {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetPermissionLevel {
|
||||
impl Resolve<ReadArgs> for GetPermission {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetPermissionLevelResponse> {
|
||||
) -> serror::Result<GetPermissionResponse> {
|
||||
if user.admin {
|
||||
return Ok(PermissionLevel::Write);
|
||||
return Ok(PermissionLevel::Write.all());
|
||||
}
|
||||
Ok(get_user_permission_on_target(user, &self.target).await?)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, procedure_state_cache},
|
||||
};
|
||||
@@ -22,10 +23,10 @@ impl Resolve<ReadArgs> for GetProcedure {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetProcedureResponse> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
get_check_permissions::<Procedure>(
|
||||
&self.procedure,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -44,7 +45,10 @@ impl Resolve<ReadArgs> for ListProcedures {
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Procedure>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -63,7 +67,10 @@ impl Resolve<ReadArgs> for ListFullProcedures {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Procedure>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -78,6 +85,7 @@ impl Resolve<ReadArgs> for GetProceduresSummary {
|
||||
let procedures = resource::list_full_for_user::<Procedure>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -120,10 +128,10 @@ impl Resolve<ReadArgs> for GetProcedureActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetProcedureActionStateResponse> {
|
||||
let procedure = resource::get_check_permissions::<Procedure>(
|
||||
let procedure = get_check_permissions::<Procedure>(
|
||||
&self.procedure,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
|
||||
@@ -12,6 +12,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, github_client, repo_state_cache},
|
||||
};
|
||||
@@ -24,10 +25,10 @@ impl Resolve<ReadArgs> for GetRepo {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Repo> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Repo>(
|
||||
get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -45,8 +46,13 @@ impl Resolve<ReadArgs> for ListRepos {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Repo>(self.query, user, &all_tags)
|
||||
.await?,
|
||||
resource::list_for_user::<Repo>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +69,10 @@ impl Resolve<ReadArgs> for ListFullRepos {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Repo>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -75,10 +84,10 @@ impl Resolve<ReadArgs> for GetRepoActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<RepoActionState> {
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -99,6 +108,7 @@ impl Resolve<ReadArgs> for GetReposSummary {
|
||||
let repos = resource::list_full_for_user::<Repo>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -160,10 +170,10 @@ impl Resolve<ReadArgs> for GetRepoWebhooksEnabled {
|
||||
});
|
||||
};
|
||||
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ use crate::{
|
||||
periphery_client,
|
||||
query::{get_all_tags, get_system_info},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
stack::compose_container_match_regex,
|
||||
state::{action_states, db_client, server_status_cache},
|
||||
@@ -66,6 +67,7 @@ impl Resolve<ReadArgs> for GetServersSummary {
|
||||
let servers = resource::list_for_user::<Server>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
@@ -93,10 +95,10 @@ impl Resolve<ReadArgs> for GetPeripheryVersion {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetPeripheryVersionResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let version = server_status_cache()
|
||||
@@ -114,10 +116,10 @@ impl Resolve<ReadArgs> for GetServer {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Server> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Server>(
|
||||
get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -135,8 +137,13 @@ impl Resolve<ReadArgs> for ListServers {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<Server>(self.query, user, &all_tags)
|
||||
.await?,
|
||||
resource::list_for_user::<Server>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -153,7 +160,10 @@ impl Resolve<ReadArgs> for ListFullServers {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Server>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -165,10 +175,10 @@ impl Resolve<ReadArgs> for GetServerState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetServerStateResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let status = server_status_cache()
|
||||
@@ -187,10 +197,10 @@ impl Resolve<ReadArgs> for GetServerActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ServerActionState> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -208,10 +218,10 @@ impl Resolve<ReadArgs> for GetSystemInformation {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<SystemInformation> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
get_system_info(&server).await.map_err(Into::into)
|
||||
@@ -223,10 +233,10 @@ impl Resolve<ReadArgs> for GetSystemStats {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetSystemStatsResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let status =
|
||||
@@ -255,10 +265,10 @@ impl Resolve<ReadArgs> for ListSystemProcesses {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListSystemProcessesResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.processes(),
|
||||
)
|
||||
.await?;
|
||||
let mut lock = processes_cache().lock().await;
|
||||
@@ -294,10 +304,10 @@ impl Resolve<ReadArgs> for GetHistoricalServerStats {
|
||||
granularity,
|
||||
page,
|
||||
} = self;
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let granularity =
|
||||
@@ -342,10 +352,10 @@ impl Resolve<ReadArgs> for ListDockerContainers {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerContainersResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -367,6 +377,7 @@ impl Resolve<ReadArgs> for ListAllDockerContainers {
|
||||
let servers = resource::list_for_user::<Server>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await?
|
||||
@@ -400,6 +411,7 @@ impl Resolve<ReadArgs> for GetDockerContainersSummary {
|
||||
let servers = resource::list_full_for_user::<Server>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -436,10 +448,10 @@ impl Resolve<ReadArgs> for InspectDockerContainer {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Container> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.inspect(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -476,10 +488,10 @@ impl Resolve<ReadArgs> for GetContainerLog {
|
||||
tail,
|
||||
timestamps,
|
||||
} = self;
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)?
|
||||
@@ -507,10 +519,10 @@ impl Resolve<ReadArgs> for SearchContainerLog {
|
||||
invert,
|
||||
timestamps,
|
||||
} = self;
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)?
|
||||
@@ -532,10 +544,10 @@ impl Resolve<ReadArgs> for GetResourceMatchingContainer {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetResourceMatchingContainerResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
// first check deployments
|
||||
@@ -593,10 +605,10 @@ impl Resolve<ReadArgs> for ListDockerNetworks {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerNetworksResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -615,10 +627,10 @@ impl Resolve<ReadArgs> for InspectDockerNetwork {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Network> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -645,10 +657,10 @@ impl Resolve<ReadArgs> for ListDockerImages {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerImagesResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -667,10 +679,10 @@ impl Resolve<ReadArgs> for InspectDockerImage {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Image> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -694,10 +706,10 @@ impl Resolve<ReadArgs> for ListDockerImageHistory {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Vec<ImageHistoryResponseItem>> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -724,10 +736,10 @@ impl Resolve<ReadArgs> for ListDockerVolumes {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerVolumesResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -746,10 +758,10 @@ impl Resolve<ReadArgs> for InspectDockerVolume {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Volume> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -773,10 +785,10 @@ impl Resolve<ReadArgs> for ListComposeProjects {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListComposeProjectsResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -832,10 +844,10 @@ impl Resolve<ReadArgs> for ListTerminals {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListTerminalsResponse> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.terminal(),
|
||||
)
|
||||
.await?;
|
||||
let cache = terminals_cache().get_or_insert(server.id.clone());
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{Context, anyhow};
|
||||
use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
config::core::CoreConfig,
|
||||
docker::container::Container,
|
||||
permission::PermissionLevel,
|
||||
server::{Server, ServerState},
|
||||
stack::{Stack, StackActionState, StackListItem, StackState},
|
||||
},
|
||||
};
|
||||
use periphery_client::api::compose::{
|
||||
GetComposeLog, GetComposeLogSearch,
|
||||
use periphery_client::api::{
|
||||
compose::{GetComposeLog, GetComposeLogSearch},
|
||||
container::InspectContainer,
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::{periphery_client, query::get_all_tags},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
stack::get_stack_and_server,
|
||||
state::{action_states, github_client, stack_status_cache},
|
||||
state::{
|
||||
action_states, github_client, server_status_cache,
|
||||
stack_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
use super::ReadArgs;
|
||||
@@ -30,10 +37,10 @@ impl Resolve<ReadArgs> for GetStack {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Stack> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<Stack>(
|
||||
get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -45,10 +52,10 @@ impl Resolve<ReadArgs> for ListStackServices {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListStackServicesResponse> {
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -75,9 +82,13 @@ impl Resolve<ReadArgs> for GetStackLog {
|
||||
tail,
|
||||
timestamps,
|
||||
} = self;
|
||||
let (stack, server) =
|
||||
get_stack_and_server(&stack, user, PermissionLevel::Read, true)
|
||||
.await?;
|
||||
let (stack, server) = get_stack_and_server(
|
||||
&stack,
|
||||
user,
|
||||
PermissionLevel::Read.logs(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)?
|
||||
.request(GetComposeLog {
|
||||
project: stack.project_name(false),
|
||||
@@ -104,9 +115,13 @@ impl Resolve<ReadArgs> for SearchStackLog {
|
||||
invert,
|
||||
timestamps,
|
||||
} = self;
|
||||
let (stack, server) =
|
||||
get_stack_and_server(&stack, user, PermissionLevel::Read, true)
|
||||
.await?;
|
||||
let (stack, server) = get_stack_and_server(
|
||||
&stack,
|
||||
user,
|
||||
PermissionLevel::Read.logs(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)?
|
||||
.request(GetComposeLogSearch {
|
||||
project: stack.project_name(false),
|
||||
@@ -122,6 +137,60 @@ impl Resolve<ReadArgs> for SearchStackLog {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectStackContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Container> {
|
||||
let InspectStackContainer { stack, service } = self;
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&stack,
|
||||
user,
|
||||
PermissionLevel::Read.inspect(),
|
||||
)
|
||||
.await?;
|
||||
if stack.config.server_id.is_empty() {
|
||||
return Err(
|
||||
anyhow!("Cannot inspect stack, not attached to any server")
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let server =
|
||||
resource::get::<Server>(&stack.config.server_id).await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect container: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let services = &stack_status_cache()
|
||||
.get(&stack.id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.curr
|
||||
.services;
|
||||
let Some(name) = services
|
||||
.into_iter()
|
||||
.find(|s| s.service == service)
|
||||
.and_then(|s| s.container.as_ref().map(|c| c.name.clone()))
|
||||
else {
|
||||
return Err(anyhow!(
|
||||
"No service found matching '{service}'. Was the stack last deployed manually?"
|
||||
).into());
|
||||
};
|
||||
let res = periphery_client(&server)?
|
||||
.request(InspectContainer { name })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListCommonStackExtraArgs {
|
||||
async fn resolve(
|
||||
self,
|
||||
@@ -133,7 +202,10 @@ impl Resolve<ReadArgs> for ListCommonStackExtraArgs {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let stacks = resource::list_full_for_user::<Stack>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await
|
||||
.context("failed to get resources matching query")?;
|
||||
@@ -164,7 +236,10 @@ impl Resolve<ReadArgs> for ListCommonStackBuildExtraArgs {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let stacks = resource::list_full_for_user::<Stack>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await
|
||||
.context("failed to get resources matching query")?;
|
||||
@@ -195,9 +270,13 @@ impl Resolve<ReadArgs> for ListStacks {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let only_update_available = self.query.specific.update_available;
|
||||
let stacks =
|
||||
resource::list_for_user::<Stack>(self.query, user, &all_tags)
|
||||
.await?;
|
||||
let stacks = resource::list_for_user::<Stack>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?;
|
||||
let stacks = if only_update_available {
|
||||
stacks
|
||||
.into_iter()
|
||||
@@ -228,7 +307,10 @@ impl Resolve<ReadArgs> for ListFullStacks {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<Stack>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -240,10 +322,10 @@ impl Resolve<ReadArgs> for GetStackActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<StackActionState> {
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -264,6 +346,7 @@ impl Resolve<ReadArgs> for GetStacksSummary {
|
||||
let stacks = resource::list_full_for_user::<Stack>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -302,10 +385,10 @@ impl Resolve<ReadArgs> for GetStackWebhooksEnabled {
|
||||
});
|
||||
};
|
||||
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ use resolver_api::Resolve;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, github_client},
|
||||
};
|
||||
@@ -26,10 +27,10 @@ impl Resolve<ReadArgs> for GetResourceSync {
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ResourceSync> {
|
||||
Ok(
|
||||
resource::get_check_permissions::<ResourceSync>(
|
||||
get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -48,7 +49,10 @@ impl Resolve<ReadArgs> for ListResourceSyncs {
|
||||
};
|
||||
Ok(
|
||||
resource::list_for_user::<ResourceSync>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -67,7 +71,10 @@ impl Resolve<ReadArgs> for ListFullResourceSyncs {
|
||||
};
|
||||
Ok(
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
self.query, user, &all_tags,
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -79,10 +86,10 @@ impl Resolve<ReadArgs> for GetResourceSyncActionState {
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ResourceSyncActionState> {
|
||||
let sync = resource::get_check_permissions::<ResourceSync>(
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let action_state = action_states()
|
||||
@@ -104,6 +111,7 @@ impl Resolve<ReadArgs> for GetResourceSyncsSummary {
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
@@ -160,10 +168,10 @@ impl Resolve<ReadArgs> for GetSyncWebhooksEnabled {
|
||||
});
|
||||
};
|
||||
|
||||
let sync = resource::get_check_permissions::<ResourceSync>(
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@ use crate::{
|
||||
helpers::query::{
|
||||
get_all_tags, get_id_to_tags, get_user_user_group_ids,
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::db_client,
|
||||
sync::{
|
||||
AllResourcesById,
|
||||
toml::{TOML_PRETTY_OPTIONS, ToToml, convert_resource},
|
||||
user_groups::convert_user_groups,
|
||||
toml::{ToToml, convert_resource},
|
||||
user_groups::{convert_user_groups, user_group_to_toml},
|
||||
variables::variable_to_toml,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -45,6 +47,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Alerter>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -55,6 +58,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Builder>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -65,6 +69,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Server>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -75,6 +80,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Stack>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -85,6 +91,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Deployment>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -95,6 +102,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Build>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -105,6 +113,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Repo>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -115,6 +124,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Procedure>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -125,6 +135,7 @@ async fn get_all_targets(
|
||||
resource::list_for_user::<Action>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -135,6 +146,7 @@ async fn get_all_targets(
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
@@ -198,10 +210,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
for target in targets {
|
||||
match target {
|
||||
ResourceTarget::Alerter(id) => {
|
||||
let alerter = resource::get_check_permissions::<Alerter>(
|
||||
let alerter = get_check_permissions::<Alerter>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
res.alerters.push(convert_resource::<Alerter>(
|
||||
@@ -212,10 +224,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::ResourceSync(id) => {
|
||||
let sync = resource::get_check_permissions::<ResourceSync>(
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
if sync.config.file_contents.is_empty()
|
||||
@@ -231,10 +243,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
}
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
res.servers.push(convert_resource::<Server>(
|
||||
@@ -245,13 +257,12 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
let mut builder =
|
||||
resource::get_check_permissions::<Builder>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
)
|
||||
.await?;
|
||||
let mut builder = get_check_permissions::<Builder>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Builder::replace_ids(&mut builder, &all);
|
||||
res.builders.push(convert_resource::<Builder>(
|
||||
builder,
|
||||
@@ -261,10 +272,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
let mut build = resource::get_check_permissions::<Build>(
|
||||
let mut build = get_check_permissions::<Build>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Build::replace_ids(&mut build, &all);
|
||||
@@ -276,10 +287,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
let mut deployment = resource::get_check_permissions::<
|
||||
Deployment,
|
||||
>(
|
||||
&id, user, PermissionLevel::Read
|
||||
let mut deployment = get_check_permissions::<Deployment>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Deployment::replace_ids(&mut deployment, &all);
|
||||
@@ -291,10 +302,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
let mut repo = resource::get_check_permissions::<Repo>(
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Repo::replace_ids(&mut repo, &all);
|
||||
@@ -306,10 +317,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Stack(id) => {
|
||||
let mut stack = resource::get_check_permissions::<Stack>(
|
||||
let mut stack = get_check_permissions::<Stack>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Stack::replace_ids(&mut stack, &all);
|
||||
@@ -321,10 +332,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Procedure(id) => {
|
||||
let mut procedure = resource::get_check_permissions::<
|
||||
Procedure,
|
||||
>(
|
||||
&id, user, PermissionLevel::Read
|
||||
let mut procedure = get_check_permissions::<Procedure>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Procedure::replace_ids(&mut procedure, &all);
|
||||
@@ -336,10 +347,10 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
));
|
||||
}
|
||||
ResourceTarget::Action(id) => {
|
||||
let mut action = resource::get_check_permissions::<Action>(
|
||||
let mut action = get_check_permissions::<Action>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Action::replace_ids(&mut action, &all);
|
||||
@@ -490,22 +501,14 @@ fn serialize_resources_toml(
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[variable]]\n");
|
||||
toml.push_str(
|
||||
&toml_pretty::to_string(variable, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize variables to toml")?,
|
||||
);
|
||||
toml.push_str(&variable_to_toml(variable)?);
|
||||
}
|
||||
|
||||
for user_group in &resources.user_groups {
|
||||
for user_group in resources.user_groups {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[user_group]]\n");
|
||||
toml.push_str(
|
||||
&toml_pretty::to_string(user_group, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize user_groups to toml")?,
|
||||
);
|
||||
toml.push_str(&user_group_to_toml(user_group)?);
|
||||
}
|
||||
|
||||
Ok(toml)
|
||||
|
||||
@@ -27,7 +27,11 @@ use mungos::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{config::core_config, resource, state::db_client};
|
||||
use crate::{
|
||||
config::core_config,
|
||||
permission::{get_check_permissions, get_resource_ids_for_user},
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
use super::ReadArgs;
|
||||
|
||||
@@ -41,18 +45,17 @@ impl Resolve<ReadArgs> for ListUpdates {
|
||||
let query = if user.admin || core_config().transparent_mode {
|
||||
self.query
|
||||
} else {
|
||||
let server_query =
|
||||
resource::get_resource_ids_for_user::<Server>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Server", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Server" });
|
||||
let server_query = get_resource_ids_for_user::<Server>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Server", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Server" });
|
||||
|
||||
let deployment_query =
|
||||
resource::get_resource_ids_for_user::<Deployment>(user)
|
||||
get_resource_ids_for_user::<Deployment>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
@@ -61,38 +64,35 @@ impl Resolve<ReadArgs> for ListUpdates {
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Deployment" });
|
||||
|
||||
let stack_query =
|
||||
resource::get_resource_ids_for_user::<Stack>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Stack", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Stack" });
|
||||
let stack_query = get_resource_ids_for_user::<Stack>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Stack", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Stack" });
|
||||
|
||||
let build_query =
|
||||
resource::get_resource_ids_for_user::<Build>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Build", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Build" });
|
||||
let build_query = get_resource_ids_for_user::<Build>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Build", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Build" });
|
||||
|
||||
let repo_query =
|
||||
resource::get_resource_ids_for_user::<Repo>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Repo", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Repo" });
|
||||
let repo_query = get_resource_ids_for_user::<Repo>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Repo", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Repo" });
|
||||
|
||||
let procedure_query =
|
||||
resource::get_resource_ids_for_user::<Procedure>(user)
|
||||
get_resource_ids_for_user::<Procedure>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
@@ -101,47 +101,43 @@ impl Resolve<ReadArgs> for ListUpdates {
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Procedure" });
|
||||
|
||||
let action_query =
|
||||
resource::get_resource_ids_for_user::<Action>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Action", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Action" });
|
||||
|
||||
let builder_query =
|
||||
resource::get_resource_ids_for_user::<Builder>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Builder", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Builder" });
|
||||
|
||||
let alerter_query =
|
||||
resource::get_resource_ids_for_user::<Alerter>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Alerter", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Alerter" });
|
||||
|
||||
let resource_sync_query =
|
||||
resource::get_resource_ids_for_user::<ResourceSync>(
|
||||
user,
|
||||
)
|
||||
let action_query = get_resource_ids_for_user::<Action>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "ResourceSync", "target.id": { "$in": ids }
|
||||
"target.type": "Action", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "ResourceSync" });
|
||||
.unwrap_or_else(|| doc! { "target.type": "Action" });
|
||||
|
||||
let builder_query = get_resource_ids_for_user::<Builder>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Builder", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Builder" });
|
||||
|
||||
let alerter_query = get_resource_ids_for_user::<Alerter>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "Alerter", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "Alerter" });
|
||||
|
||||
let resource_sync_query = get_resource_ids_for_user::<
|
||||
ResourceSync,
|
||||
>(user)
|
||||
.await?
|
||||
.map(|ids| {
|
||||
doc! {
|
||||
"target.type": "ResourceSync", "target.id": { "$in": ids }
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| doc! { "target.type": "ResourceSync" });
|
||||
|
||||
let mut query = self.query.unwrap_or_default();
|
||||
query.extend(doc! {
|
||||
@@ -233,82 +229,82 @@ impl Resolve<ReadArgs> for GetUpdate {
|
||||
);
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
resource::get_check_permissions::<Server>(
|
||||
get_check_permissions::<Server>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
get_check_permissions::<Deployment>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
resource::get_check_permissions::<Build>(
|
||||
get_check_permissions::<Build>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
resource::get_check_permissions::<Repo>(
|
||||
get_check_permissions::<Repo>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
resource::get_check_permissions::<Builder>(
|
||||
get_check_permissions::<Builder>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
get_check_permissions::<Alerter>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Procedure(id) => {
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
get_check_permissions::<Procedure>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Action(id) => {
|
||||
resource::get_check_permissions::<Action>(
|
||||
get_check_permissions::<Action>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::ResourceSync(id) => {
|
||||
resource::get_check_permissions::<ResourceSync>(
|
||||
get_check_permissions::<ResourceSync>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Stack(id) => {
|
||||
resource::get_check_permissions::<Stack>(
|
||||
get_check_permissions::<Stack>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ use serror::Json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::auth_request, helpers::periphery_client, resource,
|
||||
auth::auth_request, helpers::periphery_client,
|
||||
permission::get_check_permissions,
|
||||
};
|
||||
|
||||
pub fn router() -> Router {
|
||||
@@ -45,10 +46,10 @@ async fn execute_inner(
|
||||
info!("/terminal request | user: {}", user.username);
|
||||
|
||||
let res = async {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.terminal(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use komodo_client::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::resource;
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
use super::WriteArgs;
|
||||
|
||||
@@ -29,13 +29,12 @@ impl Resolve<WriteArgs> for CopyAction {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Action> {
|
||||
let Action { config, .. } =
|
||||
resource::get_check_permissions::<Action>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let Action { config, .. } = get_check_permissions::<Action>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
resource::create::<Action>(&self.name, config.into(), user)
|
||||
.await?,
|
||||
|
||||
@@ -6,7 +6,7 @@ use komodo_client::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::resource;
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
use super::WriteArgs;
|
||||
|
||||
@@ -29,13 +29,12 @@ impl Resolve<WriteArgs> for CopyAlerter {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Alerter> {
|
||||
let Alerter { config, .. } =
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let Alerter { config, .. } = get_check_permissions::<Alerter>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
resource::create::<Alerter>(&self.name, config.into(), user)
|
||||
.await?,
|
||||
|
||||
@@ -36,6 +36,7 @@ use crate::{
|
||||
query::get_server_with_state,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{db_client, github_client},
|
||||
};
|
||||
@@ -61,13 +62,12 @@ impl Resolve<WriteArgs> for CopyBuild {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Build> {
|
||||
let Build { mut config, .. } =
|
||||
resource::get_check_permissions::<Build>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let Build { mut config, .. } = get_check_permissions::<Build>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
// reset version to 0.0.0
|
||||
config.version = Default::default();
|
||||
Ok(
|
||||
@@ -107,10 +107,10 @@ impl Resolve<WriteArgs> for RenameBuild {
|
||||
impl Resolve<WriteArgs> for WriteBuildFileContents {
|
||||
#[instrument(name = "WriteBuildFileContents", skip(args))]
|
||||
async fn resolve(self, args: &WriteArgs) -> serror::Result<Update> {
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
&args.user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -294,10 +294,10 @@ impl Resolve<WriteArgs> for RefreshBuildCache {
|
||||
) -> serror::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// build should be able to do this.
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -493,10 +493,10 @@ impl Resolve<WriteArgs> for CreateBuildWebhook {
|
||||
|
||||
let WriteArgs { user } = args;
|
||||
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -606,10 +606,10 @@ impl Resolve<WriteArgs> for DeleteBuildWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use komodo_client::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::resource;
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
use super::WriteArgs;
|
||||
|
||||
@@ -29,13 +29,12 @@ impl Resolve<WriteArgs> for CopyBuilder {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Builder> {
|
||||
let Builder { config, .. } =
|
||||
resource::get_check_permissions::<Builder>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let Builder { config, .. } = get_check_permissions::<Builder>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
resource::create::<Builder>(&self.name, config.into(), user)
|
||||
.await?,
|
||||
|
||||
@@ -11,7 +11,7 @@ use komodo_client::{
|
||||
komodo_timestamp,
|
||||
permission::PermissionLevel,
|
||||
server::{Server, ServerState},
|
||||
to_komodo_name,
|
||||
to_docker_compatible_name,
|
||||
update::Update,
|
||||
},
|
||||
};
|
||||
@@ -25,6 +25,7 @@ use crate::{
|
||||
query::get_deployment_state,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, db_client, server_status_cache},
|
||||
};
|
||||
@@ -51,10 +52,10 @@ impl Resolve<WriteArgs> for CopyDeployment {
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Deployment> {
|
||||
let Deployment { config, .. } =
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
get_check_permissions::<Deployment>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
@@ -70,10 +71,10 @@ impl Resolve<WriteArgs> for CreateDeploymentFromContainer {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Deployment> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.inspect().attach(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
@@ -188,10 +189,10 @@ impl Resolve<WriteArgs> for RenameDeployment {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -206,7 +207,7 @@ impl Resolve<WriteArgs> for RenameDeployment {
|
||||
let _action_guard =
|
||||
action_state.update(|state| state.renaming = true)?;
|
||||
|
||||
let name = to_komodo_name(&self.name);
|
||||
let name = to_docker_compatible_name(&self.name);
|
||||
|
||||
let container_state = get_deployment_state(&deployment).await?;
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ pub enum WriteRequest {
|
||||
AddUserToUserGroup(AddUserToUserGroup),
|
||||
RemoveUserFromUserGroup(RemoveUserFromUserGroup),
|
||||
SetUsersInUserGroup(SetUsersInUserGroup),
|
||||
SetEveryoneUserGroup(SetEveryoneUserGroup),
|
||||
|
||||
// ==== PERMISSIONS ====
|
||||
UpdateUserAdmin(UpdateUserAdmin),
|
||||
@@ -89,6 +90,17 @@ pub enum WriteRequest {
|
||||
DeleteTerminal(DeleteTerminal),
|
||||
DeleteAllTerminals(DeleteAllTerminals),
|
||||
|
||||
// ==== STACK ====
|
||||
CreateStack(CreateStack),
|
||||
CopyStack(CopyStack),
|
||||
DeleteStack(DeleteStack),
|
||||
UpdateStack(UpdateStack),
|
||||
RenameStack(RenameStack),
|
||||
WriteStackFileContents(WriteStackFileContents),
|
||||
RefreshStackCache(RefreshStackCache),
|
||||
CreateStackWebhook(CreateStackWebhook),
|
||||
DeleteStackWebhook(DeleteStackWebhook),
|
||||
|
||||
// ==== DEPLOYMENT ====
|
||||
CreateDeployment(CreateDeployment),
|
||||
CopyDeployment(CopyDeployment),
|
||||
@@ -158,17 +170,6 @@ pub enum WriteRequest {
|
||||
CreateSyncWebhook(CreateSyncWebhook),
|
||||
DeleteSyncWebhook(DeleteSyncWebhook),
|
||||
|
||||
// ==== STACK ====
|
||||
CreateStack(CreateStack),
|
||||
CopyStack(CopyStack),
|
||||
DeleteStack(DeleteStack),
|
||||
UpdateStack(UpdateStack),
|
||||
RenameStack(RenameStack),
|
||||
WriteStackFileContents(WriteStackFileContents),
|
||||
RefreshStackCache(RefreshStackCache),
|
||||
CreateStackWebhook(CreateStackWebhook),
|
||||
DeleteStackWebhook(DeleteStackWebhook),
|
||||
|
||||
// ==== TAG ====
|
||||
CreateTag(CreateTag),
|
||||
DeleteTag(DeleteTag),
|
||||
|
||||
@@ -11,7 +11,7 @@ use komodo_client::{
|
||||
use mungos::{
|
||||
by_id::{find_one_by_id, update_one_by_id},
|
||||
mongodb::{
|
||||
bson::{Document, doc, oid::ObjectId},
|
||||
bson::{Document, doc, oid::ObjectId, to_bson},
|
||||
options::UpdateOptions,
|
||||
},
|
||||
};
|
||||
@@ -65,6 +65,10 @@ impl Resolve<WriteArgs> for UpdateUserBasePermissions {
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> serror::Result<UpdateUserBasePermissionsResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
let UpdateUserBasePermissions {
|
||||
user_id,
|
||||
enabled,
|
||||
@@ -72,10 +76,6 @@ impl Resolve<WriteArgs> for UpdateUserBasePermissions {
|
||||
create_builds,
|
||||
} = self;
|
||||
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
let user = find_one_by_id(&db_client().users, &user_id)
|
||||
.await
|
||||
.context("failed to query mongo for user")?
|
||||
@@ -122,16 +122,16 @@ impl Resolve<WriteArgs> for UpdatePermissionOnResourceType {
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> serror::Result<UpdatePermissionOnResourceTypeResponse> {
|
||||
let UpdatePermissionOnResourceType {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
let Self {
|
||||
user_target,
|
||||
resource_type,
|
||||
permission,
|
||||
} = self;
|
||||
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
// Some extra checks if user target is an actual User
|
||||
if let UserTarget::User(user_id) = &user_target {
|
||||
let user = get_user(user_id).await?;
|
||||
@@ -153,9 +153,11 @@ impl Resolve<WriteArgs> for UpdatePermissionOnResourceType {
|
||||
|
||||
let id = ObjectId::from_str(&user_target_id)
|
||||
.context("id is not ObjectId")?;
|
||||
let field = format!("all.{resource_type}");
|
||||
let filter = doc! { "_id": id };
|
||||
let update = doc! { "$set": { &field: permission.as_ref() } };
|
||||
let field = format!("all.{resource_type}");
|
||||
let set =
|
||||
to_bson(&permission).context("permission is not Bson")?;
|
||||
let update = doc! { "$set": { &field: &set } };
|
||||
|
||||
match user_target_variant {
|
||||
UserTargetVariant::User => {
|
||||
@@ -164,7 +166,7 @@ impl Resolve<WriteArgs> for UpdatePermissionOnResourceType {
|
||||
.update_one(filter, update)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to set {field}: {permission} on db")
|
||||
format!("failed to set {field}: {set} on db")
|
||||
})?;
|
||||
}
|
||||
UserTargetVariant::UserGroup => {
|
||||
@@ -173,7 +175,7 @@ impl Resolve<WriteArgs> for UpdatePermissionOnResourceType {
|
||||
.update_one(filter, update)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to set {field}: {permission} on db")
|
||||
format!("failed to set {field}: {set} on db")
|
||||
})?;
|
||||
}
|
||||
}
|
||||
@@ -188,19 +190,22 @@ impl Resolve<WriteArgs> for UpdatePermissionOnTarget {
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> serror::Result<UpdatePermissionOnTargetResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
let UpdatePermissionOnTarget {
|
||||
user_target,
|
||||
resource_target,
|
||||
permission,
|
||||
} = self;
|
||||
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
// Some extra checks if user target is an actual User
|
||||
// Some extra checks relevant if user target is an actual User
|
||||
if let UserTarget::User(user_id) = &user_target {
|
||||
let user = get_user(user_id).await?;
|
||||
if !user.enabled {
|
||||
return Err(anyhow!("user not enabled").into());
|
||||
}
|
||||
if user.admin {
|
||||
return Err(
|
||||
anyhow!(
|
||||
@@ -209,9 +214,6 @@ impl Resolve<WriteArgs> for UpdatePermissionOnTarget {
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
if !user.enabled {
|
||||
return Err(anyhow!("user not enabled").into());
|
||||
}
|
||||
}
|
||||
|
||||
let (user_target_variant, user_target_id) =
|
||||
@@ -223,6 +225,9 @@ impl Resolve<WriteArgs> for UpdatePermissionOnTarget {
|
||||
let (user_target_variant, resource_variant) =
|
||||
(user_target_variant.as_ref(), resource_variant.as_ref());
|
||||
|
||||
let specific = to_bson(&permission.specific)
|
||||
.context("permission.specific is not valid Bson")?;
|
||||
|
||||
db_client()
|
||||
.permissions
|
||||
.update_one(
|
||||
@@ -238,7 +243,8 @@ impl Resolve<WriteArgs> for UpdatePermissionOnTarget {
|
||||
"user_target.id": user_target_id,
|
||||
"resource_target.type": resource_variant,
|
||||
"resource_target.id": resource_id,
|
||||
"level": permission.as_ref(),
|
||||
"level": permission.level.as_ref(),
|
||||
"specific": specific
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ use komodo_client::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::resource;
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
use super::WriteArgs;
|
||||
|
||||
@@ -30,10 +30,10 @@ impl Resolve<WriteArgs> for CopyProcedure {
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<CopyProcedureResponse> {
|
||||
let Procedure { config, .. } =
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
get_check_permissions::<Procedure>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
|
||||
@@ -10,7 +10,7 @@ use komodo_client::{
|
||||
permission::PermissionLevel,
|
||||
repo::{PartialRepoConfig, Repo, RepoInfo},
|
||||
server::Server,
|
||||
to_komodo_name,
|
||||
to_path_compatible_name,
|
||||
update::{Log, Update},
|
||||
},
|
||||
};
|
||||
@@ -28,6 +28,7 @@ use crate::{
|
||||
git_token, periphery_client,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, db_client, github_client},
|
||||
};
|
||||
@@ -50,13 +51,12 @@ impl Resolve<WriteArgs> for CopyRepo {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Repo> {
|
||||
let Repo { config, .. } =
|
||||
resource::get_check_permissions::<Repo>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let Repo { config, .. } = get_check_permissions::<Repo>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
resource::create::<Repo>(&self.name, config.into(), user)
|
||||
.await?,
|
||||
@@ -87,10 +87,10 @@ impl Resolve<WriteArgs> for RenameRepo {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -111,7 +111,7 @@ impl Resolve<WriteArgs> for RenameRepo {
|
||||
let _action_guard =
|
||||
action_state.update(|state| state.renaming = true)?;
|
||||
|
||||
let name = to_komodo_name(&self.name);
|
||||
let name = to_path_compatible_name(&self.name);
|
||||
|
||||
let mut update = make_update(&repo, Operation::RenameRepo, user);
|
||||
|
||||
@@ -131,7 +131,7 @@ impl Resolve<WriteArgs> for RenameRepo {
|
||||
|
||||
let log = match periphery_client(&server)?
|
||||
.request(api::git::RenameRepo {
|
||||
curr_name: to_komodo_name(&repo.name),
|
||||
curr_name: to_path_compatible_name(&repo.name),
|
||||
new_name: name.clone(),
|
||||
})
|
||||
.await
|
||||
@@ -169,10 +169,10 @@ impl Resolve<WriteArgs> for RefreshRepoCache {
|
||||
) -> serror::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// repo should be able to do this.
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -257,10 +257,10 @@ impl Resolve<WriteArgs> for CreateRepoWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
&args.user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -380,10 +380,10 @@ impl Resolve<WriteArgs> for DeleteRepoWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use komodo_client::{
|
||||
NoData, Operation,
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
to_docker_compatible_name,
|
||||
update::{Update, UpdateStatus},
|
||||
},
|
||||
};
|
||||
@@ -17,6 +18,7 @@ use crate::{
|
||||
periphery_client,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
};
|
||||
|
||||
@@ -68,10 +70,10 @@ impl Resolve<WriteArgs> for CreateNetwork {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -84,7 +86,7 @@ impl Resolve<WriteArgs> for CreateNetwork {
|
||||
|
||||
match periphery
|
||||
.request(api::network::CreateNetwork {
|
||||
name: self.name,
|
||||
name: to_docker_compatible_name(&self.name),
|
||||
driver: None,
|
||||
})
|
||||
.await
|
||||
@@ -109,10 +111,10 @@ impl Resolve<WriteArgs> for CreateTerminal {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<NoData> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.terminal(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -137,10 +139,10 @@ impl Resolve<WriteArgs> for DeleteTerminal {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<NoData> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.terminal(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -163,10 +165,10 @@ impl Resolve<WriteArgs> for DeleteAllTerminals {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<NoData> {
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.terminal(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ use crate::{
|
||||
query::get_server_with_state,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
stack::{
|
||||
get_stack_and_server,
|
||||
@@ -60,13 +61,12 @@ impl Resolve<WriteArgs> for CopyStack {
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<Stack> {
|
||||
let Stack { config, .. } =
|
||||
resource::get_check_permissions::<Stack>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let Stack { config, .. } = get_check_permissions::<Stack>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
resource::create::<Stack>(&self.name, config.into(), user)
|
||||
.await?,
|
||||
@@ -115,7 +115,7 @@ impl Resolve<WriteArgs> for WriteStackFileContents {
|
||||
let (mut stack, server) = get_stack_and_server(
|
||||
&stack,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
@@ -229,10 +229,10 @@ impl Resolve<WriteArgs> for RefreshStackCache {
|
||||
) -> serror::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// stack should be able to do this.
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -432,10 +432,10 @@ impl Resolve<WriteArgs> for CreateStackWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -552,10 +552,10 @@ impl Resolve<WriteArgs> for DeleteStackWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use komodo_client::{
|
||||
PartialResourceSyncConfig, ResourceSync, ResourceSyncInfo,
|
||||
SyncDeployUpdate,
|
||||
},
|
||||
to_komodo_name,
|
||||
to_path_compatible_name,
|
||||
update::{Log, Update},
|
||||
user::sync_user,
|
||||
},
|
||||
@@ -48,6 +48,7 @@ use crate::{
|
||||
query::get_id_to_tags,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{db_client, github_client},
|
||||
sync::{
|
||||
@@ -78,10 +79,10 @@ impl Resolve<WriteArgs> for CopyResourceSync {
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> serror::Result<ResourceSync> {
|
||||
let ResourceSync { config, .. } =
|
||||
resource::get_check_permissions::<ResourceSync>(
|
||||
get_check_permissions::<ResourceSync>(
|
||||
&self.id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
Ok(
|
||||
@@ -134,10 +135,10 @@ impl Resolve<WriteArgs> for RenameResourceSync {
|
||||
impl Resolve<WriteArgs> for WriteSyncFileContents {
|
||||
#[instrument(name = "WriteSyncFileContents", skip(args))]
|
||||
async fn resolve(self, args: &WriteArgs) -> serror::Result<Update> {
|
||||
let sync = resource::get_check_permissions::<ResourceSync>(
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
&args.user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -178,7 +179,7 @@ async fn write_sync_file_contents_on_host(
|
||||
|
||||
let root = core_config()
|
||||
.sync_directory
|
||||
.join(to_komodo_name(&sync.name));
|
||||
.join(to_path_compatible_name(&sync.name));
|
||||
let file_path =
|
||||
file_path.parse::<PathBuf>().context("Invalid file path")?;
|
||||
let resource_path = resource_path
|
||||
@@ -345,9 +346,11 @@ impl Resolve<WriteArgs> for CommitSync {
|
||||
async fn resolve(self, args: &WriteArgs) -> serror::Result<Update> {
|
||||
let WriteArgs { user } = args;
|
||||
|
||||
let sync = resource::get_check_permissions::<
|
||||
entities::sync::ResourceSync,
|
||||
>(&self.sync, user, PermissionLevel::Write)
|
||||
let sync = get_check_permissions::<entities::sync::ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let file_contents_empty = sync.config.file_contents_empty();
|
||||
@@ -411,7 +414,7 @@ impl Resolve<WriteArgs> for CommitSync {
|
||||
};
|
||||
let file_path = core_config()
|
||||
.sync_directory
|
||||
.join(to_komodo_name(&sync.name))
|
||||
.join(to_path_compatible_name(&sync.name))
|
||||
.join(&resource_path);
|
||||
if let Some(parent) = file_path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
@@ -514,10 +517,13 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
|
||||
) -> serror::Result<ResourceSync> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// sync should be able to do this.
|
||||
let mut sync = resource::get_check_permissions::<
|
||||
entities::sync::ResourceSync,
|
||||
>(&self.sync, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
let mut sync =
|
||||
get_check_permissions::<entities::sync::ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !sync.config.managed
|
||||
&& !sync.config.files_on_host
|
||||
@@ -864,10 +870,10 @@ impl Resolve<WriteArgs> for CreateSyncWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let sync = resource::get_check_permissions::<ResourceSync>(
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -984,10 +990,10 @@ impl Resolve<WriteArgs> for DeleteSyncWebhook {
|
||||
);
|
||||
};
|
||||
|
||||
let sync = resource::get_check_permissions::<ResourceSync>(
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::{get_tag, get_tag_check_owner},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::db_client,
|
||||
};
|
||||
@@ -150,94 +151,94 @@ impl Resolve<WriteArgs> for UpdateTagsOnResource {
|
||||
return Err(anyhow!("Invalid target type: System").into());
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
resource::get_check_permissions::<Build>(
|
||||
get_check_permissions::<Build>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Build>(&id, self.tags, args).await?;
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
resource::get_check_permissions::<Builder>(
|
||||
get_check_permissions::<Builder>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Builder>(&id, self.tags, args).await?
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
get_check_permissions::<Deployment>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Deployment>(&id, self.tags, args)
|
||||
.await?
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
resource::get_check_permissions::<Server>(
|
||||
get_check_permissions::<Server>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Server>(&id, self.tags, args).await?
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
resource::get_check_permissions::<Repo>(
|
||||
get_check_permissions::<Repo>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Repo>(&id, self.tags, args).await?
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
get_check_permissions::<Alerter>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Alerter>(&id, self.tags, args).await?
|
||||
}
|
||||
ResourceTarget::Procedure(id) => {
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
get_check_permissions::<Procedure>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Procedure>(&id, self.tags, args)
|
||||
.await?
|
||||
}
|
||||
ResourceTarget::Action(id) => {
|
||||
resource::get_check_permissions::<Action>(
|
||||
get_check_permissions::<Action>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Action>(&id, self.tags, args).await?
|
||||
}
|
||||
ResourceTarget::ResourceSync(id) => {
|
||||
resource::get_check_permissions::<ResourceSync>(
|
||||
get_check_permissions::<ResourceSync>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<ResourceSync>(&id, self.tags, args)
|
||||
.await?
|
||||
}
|
||||
ResourceTarget::Stack(id) => {
|
||||
resource::get_check_permissions::<Stack>(
|
||||
get_check_permissions::<Stack>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
resource::update_tags::<Stack>(&id, self.tags, args).await?
|
||||
|
||||
@@ -2,10 +2,7 @@ use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use komodo_client::{
|
||||
api::write::{
|
||||
AddUserToUserGroup, CreateUserGroup, DeleteUserGroup,
|
||||
RemoveUserFromUserGroup, RenameUserGroup, SetUsersInUserGroup,
|
||||
},
|
||||
api::write::*,
|
||||
entities::{komodo_timestamp, user_group::UserGroup},
|
||||
};
|
||||
use mungos::{
|
||||
@@ -20,6 +17,7 @@ use crate::state::db_client;
|
||||
use super::WriteArgs;
|
||||
|
||||
impl Resolve<WriteArgs> for CreateUserGroup {
|
||||
#[instrument(name = "CreateUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
@@ -28,11 +26,12 @@ impl Resolve<WriteArgs> for CreateUserGroup {
|
||||
return Err(anyhow!("This call is admin-only").into());
|
||||
}
|
||||
let user_group = UserGroup {
|
||||
name: self.name,
|
||||
id: Default::default(),
|
||||
everyone: Default::default(),
|
||||
users: Default::default(),
|
||||
all: Default::default(),
|
||||
updated_at: komodo_timestamp(),
|
||||
name: self.name,
|
||||
};
|
||||
let db = db_client();
|
||||
let id = db
|
||||
@@ -53,6 +52,7 @@ impl Resolve<WriteArgs> for CreateUserGroup {
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for RenameUserGroup {
|
||||
#[instrument(name = "RenameUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
@@ -78,6 +78,7 @@ impl Resolve<WriteArgs> for RenameUserGroup {
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for DeleteUserGroup {
|
||||
#[instrument(name = "DeleteUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
@@ -110,6 +111,7 @@ impl Resolve<WriteArgs> for DeleteUserGroup {
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for AddUserToUserGroup {
|
||||
#[instrument(name = "AddUserToUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
@@ -153,6 +155,7 @@ impl Resolve<WriteArgs> for AddUserToUserGroup {
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for RemoveUserFromUserGroup {
|
||||
#[instrument(name = "RemoveUserFromUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
@@ -196,6 +199,7 @@ impl Resolve<WriteArgs> for RemoveUserFromUserGroup {
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for SetUsersInUserGroup {
|
||||
#[instrument(name = "SetUsersInUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
@@ -240,3 +244,33 @@ impl Resolve<WriteArgs> for SetUsersInUserGroup {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for SetEveryoneUserGroup {
|
||||
#[instrument(name = "SetEveryoneUserGroup", skip(admin), fields(admin = admin.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> serror::Result<UserGroup> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("This call is admin-only").into());
|
||||
}
|
||||
|
||||
let db = db_client();
|
||||
|
||||
let filter = match ObjectId::from_str(&self.user_group) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": &self.user_group },
|
||||
};
|
||||
db.user_groups
|
||||
.update_one(filter.clone(), doc! { "$set": { "everyone": self.everyone } })
|
||||
.await
|
||||
.context("failed to set everyone on user group")?;
|
||||
let res = db
|
||||
.user_groups
|
||||
.find_one(filter)
|
||||
.await
|
||||
.context("failed to query db for UserGroups")?
|
||||
.context("no user group with given id")?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +199,7 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
.komodo_logging_opentelemetry_service_name
|
||||
.unwrap_or(config.logging.opentelemetry_service_name),
|
||||
},
|
||||
pretty_startup_config: env.komodo_pretty_startup_config.unwrap_or(config.pretty_startup_config),
|
||||
ssl_enabled: env.komodo_ssl_enabled.unwrap_or(config.ssl_enabled),
|
||||
ssl_key_file: env.komodo_ssl_key_file.unwrap_or(config.ssl_key_file),
|
||||
ssl_cert_file: env.komodo_ssl_cert_file.unwrap_or(config.ssl_cert_file),
|
||||
|
||||
32
bin/core/src/helpers/matcher.rs
Normal file
32
bin/core/src/helpers/matcher.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use anyhow::Context;
|
||||
|
||||
pub enum Matcher<'a> {
|
||||
Wildcard(wildcard::Wildcard<'a>),
|
||||
Regex(regex::Regex),
|
||||
}
|
||||
|
||||
impl<'a> Matcher<'a> {
|
||||
pub fn new(pattern: &'a str) -> anyhow::Result<Self> {
|
||||
if pattern.starts_with('\\') && pattern.ends_with('\\') {
|
||||
let inner = &pattern[1..(pattern.len() - 1)];
|
||||
let regex = regex::Regex::new(inner)
|
||||
.with_context(|| format!("invalid regex. got: {inner}"))?;
|
||||
Ok(Self::Regex(regex))
|
||||
} else {
|
||||
let wildcard = wildcard::Wildcard::new(pattern.as_bytes())
|
||||
.with_context(|| {
|
||||
format!("invalid wildcard. got: {pattern}")
|
||||
})?;
|
||||
Ok(Self::Wildcard(wildcard))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_match(&self, source: &str) -> bool {
|
||||
match self {
|
||||
Matcher::Wildcard(wildcard) => {
|
||||
wildcard.is_match(source.as_bytes())
|
||||
}
|
||||
Matcher::Regex(regex) => regex.is_match(source),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::entities::{
|
||||
ResourceTarget,
|
||||
permission::{Permission, PermissionLevel, UserTarget},
|
||||
permission::{
|
||||
Permission, PermissionLevel, SpecificPermission, UserTarget,
|
||||
},
|
||||
server::Server,
|
||||
user::User,
|
||||
};
|
||||
@@ -19,6 +22,7 @@ pub mod builder;
|
||||
pub mod cache;
|
||||
pub mod channel;
|
||||
pub mod interpolate;
|
||||
pub mod matcher;
|
||||
pub mod procedure;
|
||||
pub mod prune;
|
||||
pub mod query;
|
||||
@@ -147,6 +151,7 @@ pub async fn create_permission<T>(
|
||||
user: &User,
|
||||
target: T,
|
||||
level: PermissionLevel,
|
||||
specific: IndexSet<SpecificPermission>,
|
||||
) where
|
||||
T: Into<ResourceTarget> + std::fmt::Debug,
|
||||
{
|
||||
@@ -162,6 +167,7 @@ pub async fn create_permission<T>(
|
||||
user_target: UserTarget::User(user.id.clone()),
|
||||
resource_target: target.clone(),
|
||||
level,
|
||||
specific,
|
||||
})
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ use komodo_client::{
|
||||
action::Action,
|
||||
build::Build,
|
||||
deployment::Deployment,
|
||||
permission::PermissionLevel,
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
stack::Stack,
|
||||
@@ -1189,6 +1190,7 @@ async fn extend_batch_exection<E: ExtendBatch>(
|
||||
pattern,
|
||||
Default::default(),
|
||||
procedure_user(),
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await?
|
||||
|
||||
@@ -14,7 +14,7 @@ use komodo_client::entities::{
|
||||
builder::Builder,
|
||||
deployment::{Deployment, DeploymentState},
|
||||
docker::container::{ContainerListItem, ContainerStateStatusEnum},
|
||||
permission::PermissionLevel,
|
||||
permission::{PermissionLevel, PermissionLevelAndSpecifics},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
server::{Server, ServerState},
|
||||
@@ -39,7 +39,8 @@ use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
resource::{self, get_user_permission_on_resource},
|
||||
permission::get_user_permission_on_resource,
|
||||
resource,
|
||||
stack::compose_container_match_regex,
|
||||
state::{db_client, deployment_status_cache, stack_status_cache},
|
||||
};
|
||||
@@ -238,7 +239,10 @@ pub async fn get_user_user_groups(
|
||||
find_collect(
|
||||
&db_client().user_groups,
|
||||
doc! {
|
||||
"users": user_id
|
||||
"$or": [
|
||||
{ "everyone": true },
|
||||
{ "users": user_id },
|
||||
]
|
||||
},
|
||||
None,
|
||||
)
|
||||
@@ -277,9 +281,9 @@ pub fn user_target_query(
|
||||
pub async fn get_user_permission_on_target(
|
||||
user: &User,
|
||||
target: &ResourceTarget,
|
||||
) -> anyhow::Result<PermissionLevel> {
|
||||
) -> anyhow::Result<PermissionLevelAndSpecifics> {
|
||||
match target {
|
||||
ResourceTarget::System(_) => Ok(PermissionLevel::None),
|
||||
ResourceTarget::System(_) => Ok(PermissionLevel::None.into()),
|
||||
ResourceTarget::Build(id) => {
|
||||
get_user_permission_on_resource::<Build>(user, id).await
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ mod db;
|
||||
mod helpers;
|
||||
mod listener;
|
||||
mod monitor;
|
||||
mod permission;
|
||||
mod resource;
|
||||
mod schedule;
|
||||
mod stack;
|
||||
@@ -43,7 +44,12 @@ async fn app() -> anyhow::Result<()> {
|
||||
};
|
||||
|
||||
info!("Komodo Core version: v{}", env!("CARGO_PKG_VERSION"));
|
||||
info!("{:?}", config.sanitized());
|
||||
|
||||
if core_config().pretty_startup_config {
|
||||
info!("{:#?}", config.sanitized());
|
||||
} else {
|
||||
info!("{:?}", config.sanitized());
|
||||
}
|
||||
|
||||
// Init jwt client to crash on failure
|
||||
state::jwt_client();
|
||||
@@ -55,7 +61,7 @@ async fn app() -> anyhow::Result<()> {
|
||||
);
|
||||
// Run after db connection.
|
||||
startup::on_startup().await;
|
||||
|
||||
|
||||
// Spawn background tasks
|
||||
monitor::spawn_monitor_loop();
|
||||
resource::spawn_resource_refresh_loop();
|
||||
|
||||
@@ -2,7 +2,8 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use komodo_client::entities::{
|
||||
resource::ResourceQuery, server::Server, user::User,
|
||||
permission::PermissionLevel, resource::ResourceQuery,
|
||||
server::Server, user::User,
|
||||
};
|
||||
|
||||
use crate::resource;
|
||||
@@ -39,6 +40,7 @@ async fn get_all_servers_map()
|
||||
admin: true,
|
||||
..Default::default()
|
||||
},
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
|
||||
229
bin/core/src/permission.rs
Normal file
229
bin/core/src/permission.rs
Normal file
@@ -0,0 +1,229 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use futures::{FutureExt, future::BoxFuture};
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::{
|
||||
api::read::GetPermission,
|
||||
entities::{
|
||||
permission::{PermissionLevel, PermissionLevelAndSpecifics},
|
||||
resource::Resource,
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mongo_indexed::doc;
|
||||
use mungos::find::find_collect;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
api::read::ReadArgs,
|
||||
config::core_config,
|
||||
helpers::query::{get_user_user_groups, user_target_query},
|
||||
resource::{KomodoResource, get},
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
pub async fn get_check_permissions<T: KomodoResource>(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
required_permissions: PermissionLevelAndSpecifics,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
let resource = get::<T>(id_or_name).await?;
|
||||
|
||||
// Allow all if admin
|
||||
if user.admin {
|
||||
return Ok(resource);
|
||||
}
|
||||
|
||||
let user_permissions =
|
||||
get_user_permission_on_resource::<T>(user, &resource.id).await?;
|
||||
|
||||
if (
|
||||
// Allow if its just read or below, and transparent mode enabled
|
||||
(required_permissions.level <= PermissionLevel::Read && core_config().transparent_mode)
|
||||
// Allow if resource has base permission level greater than or equal to required permission level
|
||||
|| resource.base_permission.level >= required_permissions.level
|
||||
) && user_permissions
|
||||
.fulfills_specific(&required_permissions.specific)
|
||||
{
|
||||
return Ok(resource);
|
||||
}
|
||||
|
||||
if user_permissions.fulfills(&required_permissions) {
|
||||
Ok(resource)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"User does not have required permissions on this {}. Must have at least {} permissions{}",
|
||||
T::resource_type(),
|
||||
required_permissions.level,
|
||||
if required_permissions.specific.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(
|
||||
", as well as these specific permissions: [{}]",
|
||||
required_permissions.specifics_for_log()
|
||||
)
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub fn get_user_permission_on_resource<'a, T: KomodoResource>(
|
||||
user: &'a User,
|
||||
resource_id: &'a str,
|
||||
) -> BoxFuture<'a, anyhow::Result<PermissionLevelAndSpecifics>> {
|
||||
Box::pin(async {
|
||||
// Admin returns early with max permissions
|
||||
if user.admin {
|
||||
return Ok(PermissionLevel::Write.all());
|
||||
}
|
||||
|
||||
let resource_type = T::resource_type();
|
||||
let resource = get::<T>(resource_id).await?;
|
||||
let initial_specific = if let Some(additional_target) =
|
||||
T::inherit_specific_permissions_from(&resource)
|
||||
{
|
||||
GetPermission {
|
||||
target: additional_target,
|
||||
}
|
||||
.resolve(&ReadArgs { user: user.clone() })
|
||||
.await
|
||||
.map_err(|e| e.error)
|
||||
.context("failed to get user permission on additional target")?
|
||||
.specific
|
||||
} else {
|
||||
IndexSet::new()
|
||||
};
|
||||
|
||||
let mut permission = PermissionLevelAndSpecifics {
|
||||
level: if core_config().transparent_mode {
|
||||
PermissionLevel::Read
|
||||
} else {
|
||||
PermissionLevel::None
|
||||
},
|
||||
specific: initial_specific,
|
||||
};
|
||||
|
||||
// Add in the resource level global base permissions
|
||||
if resource.base_permission.level > permission.level {
|
||||
permission.level = resource.base_permission.level;
|
||||
}
|
||||
permission
|
||||
.specific
|
||||
.extend(resource.base_permission.specific);
|
||||
|
||||
// Overlay users base on resource variant
|
||||
if let Some(user_permission) =
|
||||
user.all.get(&resource_type).cloned()
|
||||
{
|
||||
if user_permission.level > permission.level {
|
||||
permission.level = user_permission.level;
|
||||
}
|
||||
permission.specific.extend(user_permission.specific);
|
||||
}
|
||||
|
||||
// Overlay any user groups base on resource variant
|
||||
let groups = get_user_user_groups(&user.id).await?;
|
||||
for group in &groups {
|
||||
if let Some(group_permission) =
|
||||
group.all.get(&resource_type).cloned()
|
||||
{
|
||||
if group_permission.level > permission.level {
|
||||
permission.level = group_permission.level;
|
||||
}
|
||||
permission.specific.extend(group_permission.specific);
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay any specific permissions
|
||||
let permission = find_collect(
|
||||
&db_client().permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(&user.id, &groups)?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"resource_target.id": resource_id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for permissions")?
|
||||
.into_iter()
|
||||
// get the max resource permission user has between personal / any user groups
|
||||
.fold(permission, |mut permission, resource_permission| {
|
||||
if resource_permission.level > permission.level {
|
||||
permission.level = resource_permission.level
|
||||
}
|
||||
permission.specific.extend(resource_permission.specific);
|
||||
permission
|
||||
});
|
||||
Ok(permission)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns None if still no need to filter by resource id (eg transparent mode, group membership with all access).
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_resource_ids_for_user<T: KomodoResource>(
|
||||
user: &User,
|
||||
) -> anyhow::Result<Option<Vec<String>>> {
|
||||
// Check admin or transparent mode
|
||||
if user.admin || core_config().transparent_mode {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let resource_type = T::resource_type();
|
||||
|
||||
// Check user 'all' on variant
|
||||
if let Some(permission) = user.all.get(&resource_type).cloned() {
|
||||
if permission.level > PermissionLevel::None {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
// Check user groups 'all' on variant
|
||||
let groups = get_user_user_groups(&user.id).await?;
|
||||
for group in &groups {
|
||||
if let Some(permission) = group.all.get(&resource_type).cloned() {
|
||||
if permission.level > PermissionLevel::None {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (base, perms) = tokio::try_join!(
|
||||
// Get any resources with non-none base permission,
|
||||
find_collect(
|
||||
T::coll(),
|
||||
doc! { "$or": [
|
||||
{ "base_permission": { "$in": ["Read", "Execute", "Write"] } },
|
||||
{ "base_permission.level": { "$in": ["Read", "Execute", "Write"] } }
|
||||
] },
|
||||
None,
|
||||
)
|
||||
.map(|res| res.with_context(|| format!(
|
||||
"failed to query {resource_type} on db"
|
||||
))),
|
||||
// And any ids using the permissions table
|
||||
find_collect(
|
||||
&db_client().permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(&user.id, &groups)?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"level": { "$in": ["Read", "Execute", "Write"] }
|
||||
},
|
||||
None,
|
||||
)
|
||||
.map(|res| res.context("failed to query permissions on db"))
|
||||
)?;
|
||||
|
||||
// Add specific ids
|
||||
let ids = perms
|
||||
.into_iter()
|
||||
.map(|p| p.resource_target.extract_variant_id().1.to_string())
|
||||
// Chain in the ones with non-None base permissions
|
||||
.chain(base.into_iter().map(|res| res.id))
|
||||
// collect into hashset first to remove any duplicates
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(Some(ids.into_iter().collect()))
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use komodo_client::{
|
||||
environment_vars_from_str, optional_string,
|
||||
permission::PermissionLevel,
|
||||
resource::Resource,
|
||||
to_docker_compatible_name,
|
||||
update::Update,
|
||||
user::{User, build_user},
|
||||
},
|
||||
@@ -48,6 +49,10 @@ impl super::KomodoResource for Build {
|
||||
ResourceTarget::Build(id.into())
|
||||
}
|
||||
|
||||
fn validated_name(name: &str) -> String {
|
||||
to_docker_compatible_name(name)
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
{
|
||||
&db_client().builds
|
||||
@@ -214,7 +219,7 @@ async fn validate_config(
|
||||
let builder = super::get_check_permissions::<Builder>(
|
||||
builder_id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await
|
||||
.context("Cannot attach Build to this Builder")?;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::entities::{
|
||||
MergePartial, Operation, ResourceTarget, ResourceTargetVariant,
|
||||
builder::{
|
||||
@@ -6,7 +7,7 @@ use komodo_client::entities::{
|
||||
BuilderListItem, BuilderListItemInfo, BuilderQuerySpecifics,
|
||||
PartialBuilderConfig, PartialServerBuilderConfig,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
permission::{PermissionLevel, SpecificPermission},
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
update::Update,
|
||||
@@ -35,6 +36,10 @@ impl super::KomodoResource for Builder {
|
||||
ResourceTarget::Builder(id.into())
|
||||
}
|
||||
|
||||
fn creator_specific_permissions() -> IndexSet<SpecificPermission> {
|
||||
[SpecificPermission::Attach].into_iter().collect()
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
{
|
||||
&db_client().builders
|
||||
@@ -180,7 +185,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await?;
|
||||
*server_id = server.id;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use anyhow::Context;
|
||||
use formatting::format_serror;
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::entities::{
|
||||
Operation, ResourceTarget, ResourceTargetVariant,
|
||||
build::Build,
|
||||
@@ -10,9 +11,10 @@ use komodo_client::entities::{
|
||||
PartialDeploymentConfig, conversions_from_str,
|
||||
},
|
||||
environment_vars_from_str,
|
||||
permission::PermissionLevel,
|
||||
permission::{PermissionLevel, SpecificPermission},
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
to_docker_compatible_name,
|
||||
update::Update,
|
||||
user::User,
|
||||
};
|
||||
@@ -47,6 +49,26 @@ impl super::KomodoResource for Deployment {
|
||||
ResourceTarget::Deployment(id.into())
|
||||
}
|
||||
|
||||
fn validated_name(name: &str) -> String {
|
||||
to_docker_compatible_name(name)
|
||||
}
|
||||
|
||||
fn creator_specific_permissions() -> IndexSet<SpecificPermission> {
|
||||
[
|
||||
SpecificPermission::Inspect,
|
||||
SpecificPermission::Logs,
|
||||
SpecificPermission::Terminal,
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn inherit_specific_permissions_from(
|
||||
_self: &Resource<Self::Config, Self::Info>,
|
||||
) -> Option<ResourceTarget> {
|
||||
ResourceTarget::Server(_self.config.server_id.clone()).into()
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
{
|
||||
&db_client().deployments
|
||||
@@ -284,7 +306,7 @@ async fn validate_config(
|
||||
let server = get_check_permissions::<Server>(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await
|
||||
.context("Cannot attach Deployment to this Server")?;
|
||||
@@ -298,7 +320,7 @@ async fn validate_config(
|
||||
let build = get_check_permissions::<Build>(
|
||||
build_id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await
|
||||
.context(
|
||||
|
||||
@@ -5,16 +5,20 @@ use std::{
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use formatting::format_serror;
|
||||
use futures::{FutureExt, future::join_all};
|
||||
use futures::future::join_all;
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::{
|
||||
api::{read::ExportResourcesToToml, write::CreateTag},
|
||||
entities::{
|
||||
Operation, ResourceTarget, ResourceTargetVariant,
|
||||
komodo_timestamp,
|
||||
permission::PermissionLevel,
|
||||
permission::{
|
||||
PermissionLevel, PermissionLevelAndSpecifics,
|
||||
SpecificPermission,
|
||||
},
|
||||
resource::{AddFilters, Resource, ResourceQuery},
|
||||
tag::Tag,
|
||||
to_komodo_name,
|
||||
to_general_name,
|
||||
update::Update,
|
||||
user::{User, system_user},
|
||||
},
|
||||
@@ -35,15 +39,12 @@ use serde::{Serialize, de::DeserializeOwned};
|
||||
|
||||
use crate::{
|
||||
api::{read::ReadArgs, write::WriteArgs},
|
||||
config::core_config,
|
||||
helpers::{
|
||||
create_permission, flatten_document,
|
||||
query::{
|
||||
get_tag, get_user_user_groups, id_or_name_filter,
|
||||
user_target_query,
|
||||
},
|
||||
query::{get_tag, id_or_name_filter},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::{get_check_permissions, get_resource_ids_for_user},
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
@@ -117,6 +118,28 @@ pub trait KomodoResource {
|
||||
#[allow(clippy::ptr_arg)]
|
||||
async fn busy(id: &String) -> anyhow::Result<bool>;
|
||||
|
||||
/// Some resource types have restrictions on the allowed formatting for names.
|
||||
/// Stacks, Builds, and Deployments all require names to be "docker compatible",
|
||||
/// which means all lowercase, and no spaces or dots.
|
||||
fn validated_name(name: &str) -> String {
|
||||
to_general_name(name)
|
||||
}
|
||||
|
||||
/// These permissions go to the creator of the resource,
|
||||
/// and include full access to the resource.
|
||||
fn creator_specific_permissions() -> IndexSet<SpecificPermission> {
|
||||
IndexSet::new()
|
||||
}
|
||||
|
||||
/// For Stacks / Deployments, they should inherit specific
|
||||
/// permissions like `Logs`, `Inspect`, and `Terminal`
|
||||
/// from their attached Server.
|
||||
fn inherit_specific_permissions_from(
|
||||
_self: &Resource<Self::Config, Self::Info>,
|
||||
) -> Option<ResourceTarget> {
|
||||
None
|
||||
}
|
||||
|
||||
// =======
|
||||
// CREATE
|
||||
// =======
|
||||
@@ -213,106 +236,6 @@ pub async fn get<T: KomodoResource>(
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_check_permissions<T: KomodoResource>(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
permission_level: PermissionLevel,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
let resource = get::<T>(id_or_name).await?;
|
||||
if user.admin
|
||||
// Allow if its just read or below, and transparent mode enabled
|
||||
|| (permission_level <= PermissionLevel::Read
|
||||
&& core_config().transparent_mode)
|
||||
// Allow if resource has base permission level greater than or equal to required permission level
|
||||
|| resource.base_permission >= permission_level
|
||||
{
|
||||
return Ok(resource);
|
||||
}
|
||||
let permissions =
|
||||
get_user_permission_on_resource::<T>(user, &resource.id).await?;
|
||||
if permissions >= permission_level {
|
||||
Ok(resource)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"User does not have required permissions on this {}. Must have at least {permission_level} permissions",
|
||||
T::resource_type()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user_permission_on_resource<T: KomodoResource>(
|
||||
user: &User,
|
||||
resource_id: &str,
|
||||
) -> anyhow::Result<PermissionLevel> {
|
||||
if user.admin {
|
||||
return Ok(PermissionLevel::Write);
|
||||
}
|
||||
|
||||
let resource_type = T::resource_type();
|
||||
|
||||
// Start with base of Read or None
|
||||
let mut base = if core_config().transparent_mode {
|
||||
PermissionLevel::Read
|
||||
} else {
|
||||
PermissionLevel::None
|
||||
};
|
||||
|
||||
// Add in the resource level global base permission
|
||||
let resource_base = get::<T>(resource_id).await?.base_permission;
|
||||
if resource_base > base {
|
||||
base = resource_base;
|
||||
}
|
||||
|
||||
// Overlay users base on resource variant
|
||||
if let Some(level) = user.all.get(&resource_type).cloned() {
|
||||
if level > base {
|
||||
base = level;
|
||||
}
|
||||
}
|
||||
if base == PermissionLevel::Write {
|
||||
// No reason to keep going if already Write at this point.
|
||||
return Ok(PermissionLevel::Write);
|
||||
}
|
||||
|
||||
// Overlay any user groups base on resource variant
|
||||
let groups = get_user_user_groups(&user.id).await?;
|
||||
for group in &groups {
|
||||
if let Some(level) = group.all.get(&resource_type).cloned() {
|
||||
if level > base {
|
||||
base = level;
|
||||
}
|
||||
}
|
||||
}
|
||||
if base == PermissionLevel::Write {
|
||||
// No reason to keep going if already Write at this point.
|
||||
return Ok(PermissionLevel::Write);
|
||||
}
|
||||
|
||||
// Overlay any specific permissions
|
||||
let permission = find_collect(
|
||||
&db_client().permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(&user.id, &groups)?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"resource_target.id": resource_id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for permissions")?
|
||||
.into_iter()
|
||||
// get the max permission user has between personal / any user groups
|
||||
.fold(base, |level, permission| {
|
||||
if permission.level > level {
|
||||
permission.level
|
||||
} else {
|
||||
level
|
||||
}
|
||||
});
|
||||
Ok(permission)
|
||||
}
|
||||
|
||||
// ======
|
||||
// LIST
|
||||
// ======
|
||||
@@ -332,80 +255,17 @@ pub async fn get_resource_object_ids_for_user<T: KomodoResource>(
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns None if still no need to filter by resource id (eg transparent mode, group membership with all access).
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_resource_ids_for_user<T: KomodoResource>(
|
||||
user: &User,
|
||||
) -> anyhow::Result<Option<Vec<String>>> {
|
||||
// Check admin or transparent mode
|
||||
if user.admin || core_config().transparent_mode {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let resource_type = T::resource_type();
|
||||
|
||||
// Check user 'all' on variant
|
||||
if let Some(level) = user.all.get(&resource_type).cloned() {
|
||||
if level > PermissionLevel::None {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
// Check user groups 'all' on variant
|
||||
let groups = get_user_user_groups(&user.id).await?;
|
||||
for group in &groups {
|
||||
if let Some(level) = group.all.get(&resource_type).cloned() {
|
||||
if level > PermissionLevel::None {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (base, perms) = tokio::try_join!(
|
||||
// Get any resources with non-none base permission,
|
||||
find_collect(
|
||||
T::coll(),
|
||||
doc! { "base_permission": { "$exists": true, "$ne": "None" } },
|
||||
None,
|
||||
)
|
||||
.map(|res| res.with_context(|| format!(
|
||||
"failed to query {resource_type} on db"
|
||||
))),
|
||||
// And any ids using the permissions table
|
||||
find_collect(
|
||||
&db_client().permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(&user.id, &groups)?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"level": { "$exists": true, "$ne": "None" }
|
||||
},
|
||||
None,
|
||||
)
|
||||
.map(|res| res.context("failed to query permissions on db"))
|
||||
)?;
|
||||
|
||||
// Add specific ids
|
||||
let ids = perms
|
||||
.into_iter()
|
||||
.map(|p| p.resource_target.extract_variant_id().1.to_string())
|
||||
// Chain in the ones with non-None base permissions
|
||||
.chain(base.into_iter().map(|res| res.id))
|
||||
// collect into hashset first to remove any duplicates
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(Some(ids.into_iter().collect()))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn list_for_user<T: KomodoResource>(
|
||||
mut query: ResourceQuery<T::QuerySpecifics>,
|
||||
user: &User,
|
||||
permissions: PermissionLevelAndSpecifics,
|
||||
all_tags: &[Tag],
|
||||
) -> anyhow::Result<Vec<T::ListItem>> {
|
||||
validate_resource_query_tags(&mut query, all_tags)?;
|
||||
let mut filters = Document::new();
|
||||
query.add_filters(&mut filters);
|
||||
list_for_user_using_document::<T>(filters, user).await
|
||||
list_for_user_using_document::<T>(filters, user, permissions).await
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
@@ -413,10 +273,15 @@ pub async fn list_for_user_using_pattern<T: KomodoResource>(
|
||||
pattern: &str,
|
||||
query: ResourceQuery<T::QuerySpecifics>,
|
||||
user: &User,
|
||||
permissions: PermissionLevelAndSpecifics,
|
||||
all_tags: &[Tag],
|
||||
) -> anyhow::Result<Vec<T::ListItem>> {
|
||||
let list = list_full_for_user_using_pattern::<T>(
|
||||
pattern, query, user, all_tags,
|
||||
pattern,
|
||||
query,
|
||||
user,
|
||||
permissions,
|
||||
all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -428,6 +293,7 @@ pub async fn list_for_user_using_pattern<T: KomodoResource>(
|
||||
pub async fn list_for_user_using_document<T: KomodoResource>(
|
||||
filters: Document,
|
||||
user: &User,
|
||||
permissions: PermissionLevelAndSpecifics,
|
||||
) -> anyhow::Result<Vec<T::ListItem>> {
|
||||
let list = list_full_for_user_using_document::<T>(filters, user)
|
||||
.await?
|
||||
@@ -449,10 +315,12 @@ pub async fn list_full_for_user_using_pattern<T: KomodoResource>(
|
||||
pattern: &str,
|
||||
query: ResourceQuery<T::QuerySpecifics>,
|
||||
user: &User,
|
||||
permissions: PermissionLevelAndSpecifics,
|
||||
all_tags: &[Tag],
|
||||
) -> anyhow::Result<Vec<Resource<T::Config, T::Info>>> {
|
||||
let resources =
|
||||
list_full_for_user::<T>(query, user, all_tags).await?;
|
||||
list_full_for_user::<T>(query, user, permissions, all_tags)
|
||||
.await?;
|
||||
|
||||
let patterns = parse_string_list(pattern);
|
||||
let mut names = HashSet::<String>::new();
|
||||
@@ -489,6 +357,7 @@ pub async fn list_full_for_user_using_pattern<T: KomodoResource>(
|
||||
pub async fn list_full_for_user<T: KomodoResource>(
|
||||
mut query: ResourceQuery<T::QuerySpecifics>,
|
||||
user: &User,
|
||||
permissions: PermissionLevelAndSpecifics,
|
||||
all_tags: &[Tag],
|
||||
) -> anyhow::Result<Vec<Resource<T::Config, T::Info>>> {
|
||||
validate_resource_query_tags(&mut query, all_tags)?;
|
||||
@@ -590,7 +459,7 @@ pub async fn create<T: KomodoResource>(
|
||||
return Err(anyhow!("Must provide non-empty name for resource."));
|
||||
}
|
||||
|
||||
let name = to_komodo_name(name);
|
||||
let name = T::validated_name(name);
|
||||
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names."));
|
||||
@@ -598,11 +467,16 @@ pub async fn create<T: KomodoResource>(
|
||||
|
||||
// Ensure an existing resource with same name doesn't already exist
|
||||
// The database indexing also ensures this but doesn't give a good error message.
|
||||
if list_full_for_user::<T>(Default::default(), system_user(), &[])
|
||||
.await
|
||||
.context("Failed to list all resources for duplicate name check")?
|
||||
.into_iter()
|
||||
.any(|r| r.name == name)
|
||||
if list_full_for_user::<T>(
|
||||
Default::default(),
|
||||
system_user(),
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.context("Failed to list all resources for duplicate name check")?
|
||||
.into_iter()
|
||||
.any(|r| r.name == name)
|
||||
{
|
||||
return Err(anyhow!("Must provide unique name for resource."));
|
||||
}
|
||||
@@ -619,7 +493,7 @@ pub async fn create<T: KomodoResource>(
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: T::default_info().await?,
|
||||
base_permission: PermissionLevel::None,
|
||||
base_permission: PermissionLevel::None.into(),
|
||||
};
|
||||
|
||||
let resource_id = T::coll()
|
||||
@@ -636,8 +510,13 @@ pub async fn create<T: KomodoResource>(
|
||||
let resource = get::<T>(&resource_id).await?;
|
||||
let target = resource_target::<T>(resource_id);
|
||||
|
||||
create_permission(user, target.clone(), PermissionLevel::Write)
|
||||
.await;
|
||||
create_permission(
|
||||
user,
|
||||
target.clone(),
|
||||
PermissionLevel::Write,
|
||||
T::creator_specific_permissions(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut update = make_update(target, T::create_operation(), user);
|
||||
update.start_ts = start_ts;
|
||||
@@ -676,7 +555,7 @@ pub async fn update<T: KomodoResource>(
|
||||
let resource = get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -788,7 +667,7 @@ pub async fn update_description<T: KomodoResource>(
|
||||
get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
T::coll()
|
||||
@@ -852,7 +731,7 @@ pub async fn rename<T: KomodoResource>(
|
||||
let resource = get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -862,7 +741,7 @@ pub async fn rename<T: KomodoResource>(
|
||||
user,
|
||||
);
|
||||
|
||||
let name = to_komodo_name(name);
|
||||
let name = T::validated_name(name);
|
||||
|
||||
update_one_by_id(
|
||||
T::coll(),
|
||||
@@ -906,7 +785,7 @@ pub async fn delete<T: KomodoResource>(
|
||||
let resource = get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
&args.user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ async fn validate_config(
|
||||
let procedure = super::get_check_permissions::<Procedure>(
|
||||
¶ms.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
match id {
|
||||
@@ -204,7 +204,7 @@ async fn validate_config(
|
||||
let action = super::get_check_permissions::<Action>(
|
||||
¶ms.action,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.action = action.id;
|
||||
@@ -220,7 +220,7 @@ async fn validate_config(
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
@@ -236,7 +236,7 @@ async fn validate_config(
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
@@ -246,7 +246,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -263,7 +263,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -273,7 +273,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -283,7 +283,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -293,7 +293,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -303,7 +303,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -313,7 +313,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -323,7 +323,7 @@ async fn validate_config(
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
@@ -339,7 +339,7 @@ async fn validate_config(
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
@@ -355,7 +355,7 @@ async fn validate_config(
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
@@ -371,7 +371,7 @@ async fn validate_config(
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
@@ -387,7 +387,7 @@ async fn validate_config(
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
@@ -396,7 +396,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -405,7 +405,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -414,7 +414,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -423,7 +423,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -432,7 +432,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -441,7 +441,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -450,7 +450,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -459,7 +459,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -468,7 +468,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -477,7 +477,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -486,7 +486,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -495,7 +495,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -504,7 +504,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -513,7 +513,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -522,7 +522,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -531,7 +531,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -540,7 +540,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -549,7 +549,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -558,7 +558,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -567,7 +567,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -576,7 +576,7 @@ async fn validate_config(
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
@@ -585,7 +585,7 @@ async fn validate_config(
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
@@ -595,7 +595,7 @@ async fn validate_config(
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
@@ -604,7 +604,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -620,7 +620,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -636,7 +636,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -652,7 +652,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -661,7 +661,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -670,7 +670,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -679,7 +679,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -688,7 +688,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -697,7 +697,7 @@ async fn validate_config(
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
@@ -713,7 +713,7 @@ async fn validate_config(
|
||||
let alerter = super::get_check_permissions::<Alerter>(
|
||||
¶ms.alerter,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.alerter = alerter.id;
|
||||
|
||||
@@ -12,7 +12,7 @@ use komodo_client::entities::{
|
||||
},
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
to_komodo_name,
|
||||
to_path_compatible_name,
|
||||
update::Update,
|
||||
user::User,
|
||||
};
|
||||
@@ -48,6 +48,10 @@ impl super::KomodoResource for Repo {
|
||||
ResourceTarget::Repo(id.into())
|
||||
}
|
||||
|
||||
fn validated_name(name: &str) -> String {
|
||||
to_path_compatible_name(name)
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
{
|
||||
&db_client().repos
|
||||
@@ -170,7 +174,7 @@ impl super::KomodoResource for Repo {
|
||||
match periphery
|
||||
.request(DeleteRepo {
|
||||
name: if repo.config.path.is_empty() {
|
||||
to_komodo_name(&repo.name)
|
||||
to_path_compatible_name(&repo.name)
|
||||
} else {
|
||||
repo.config.path.clone()
|
||||
},
|
||||
@@ -226,7 +230,7 @@ async fn validate_config(
|
||||
let server = get_check_permissions::<Server>(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await
|
||||
.context("Cannot attach Repo to this Server")?;
|
||||
@@ -238,7 +242,7 @@ async fn validate_config(
|
||||
let builder = super::get_check_permissions::<Builder>(
|
||||
builder_id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await
|
||||
.context("Cannot attach Repo to this Builder")?;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::entities::{
|
||||
Operation, ResourceTarget, ResourceTargetVariant, komodo_timestamp,
|
||||
permission::SpecificPermission,
|
||||
resource::Resource,
|
||||
server::{
|
||||
PartialServerConfig, Server, ServerConfig, ServerConfigDiff,
|
||||
@@ -34,6 +36,18 @@ impl super::KomodoResource for Server {
|
||||
ResourceTarget::Server(id.into())
|
||||
}
|
||||
|
||||
fn creator_specific_permissions() -> IndexSet<SpecificPermission> {
|
||||
[
|
||||
SpecificPermission::Terminal,
|
||||
SpecificPermission::Inspect,
|
||||
SpecificPermission::Attach,
|
||||
SpecificPermission::Logs,
|
||||
SpecificPermission::Processes,
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
{
|
||||
&db_client().servers
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use anyhow::Context;
|
||||
use formatting::format_serror;
|
||||
use indexmap::IndexSet;
|
||||
use komodo_client::{
|
||||
api::write::RefreshStackCache,
|
||||
entities::{
|
||||
Operation, ResourceTarget, ResourceTargetVariant,
|
||||
permission::PermissionLevel,
|
||||
permission::{PermissionLevel, SpecificPermission},
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
stack::{
|
||||
@@ -12,6 +13,7 @@ use komodo_client::{
|
||||
StackInfo, StackListItem, StackListItemInfo,
|
||||
StackQuerySpecifics, StackServiceWithUpdate, StackState,
|
||||
},
|
||||
to_docker_compatible_name,
|
||||
update::Update,
|
||||
user::{User, stack_user},
|
||||
},
|
||||
@@ -48,6 +50,26 @@ impl super::KomodoResource for Stack {
|
||||
ResourceTarget::Stack(id.into())
|
||||
}
|
||||
|
||||
fn validated_name(name: &str) -> String {
|
||||
to_docker_compatible_name(name)
|
||||
}
|
||||
|
||||
fn creator_specific_permissions() -> IndexSet<SpecificPermission> {
|
||||
[
|
||||
SpecificPermission::Inspect,
|
||||
SpecificPermission::Logs,
|
||||
SpecificPermission::Terminal,
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn inherit_specific_permissions_from(
|
||||
_self: &Resource<Self::Config, Self::Info>,
|
||||
) -> Option<ResourceTarget> {
|
||||
ResourceTarget::Server(_self.config.server_id.clone()).into()
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
{
|
||||
&db_client().stacks
|
||||
@@ -314,7 +336,7 @@ async fn validate_config(
|
||||
let server = get_check_permissions::<Server>(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.attach(),
|
||||
)
|
||||
.await
|
||||
.context("Cannot attach stack to this Server")?;
|
||||
|
||||
@@ -36,9 +36,13 @@ pub async fn execute_compose<T: ExecuteCompose>(
|
||||
mut update: Update,
|
||||
extras: T::Extras,
|
||||
) -> anyhow::Result<Update> {
|
||||
let (stack, server) =
|
||||
get_stack_and_server(stack, user, PermissionLevel::Execute, true)
|
||||
.await?;
|
||||
let (stack, server) = get_stack_and_server(
|
||||
stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// get the action state for the stack (or insert default).
|
||||
let action_state =
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use komodo_client::entities::{
|
||||
permission::PermissionLevel,
|
||||
permission::PermissionLevelAndSpecifics,
|
||||
server::{Server, ServerState},
|
||||
stack::Stack,
|
||||
user::User,
|
||||
};
|
||||
use regex::Regex;
|
||||
|
||||
use crate::{helpers::query::get_server_with_state, resource};
|
||||
use crate::{
|
||||
helpers::query::get_server_with_state,
|
||||
permission::get_check_permissions,
|
||||
};
|
||||
|
||||
pub mod execute;
|
||||
pub mod remote;
|
||||
@@ -16,15 +19,11 @@ pub mod services;
|
||||
pub async fn get_stack_and_server(
|
||||
stack: &str,
|
||||
user: &User,
|
||||
permission_level: PermissionLevel,
|
||||
permissions: PermissionLevelAndSpecifics,
|
||||
block_if_server_unreachable: bool,
|
||||
) -> anyhow::Result<(Stack, Server)> {
|
||||
let stack = resource::get_check_permissions::<Stack>(
|
||||
stack,
|
||||
user,
|
||||
permission_level,
|
||||
)
|
||||
.await?;
|
||||
let stack =
|
||||
get_check_permissions::<Stack>(stack, user, permissions).await?;
|
||||
|
||||
if stack.config.server_id.is_empty() {
|
||||
return Err(anyhow!("Stack has no server configured"));
|
||||
|
||||
@@ -3,7 +3,7 @@ use git::GitRes;
|
||||
use komodo_client::entities::{
|
||||
CloneArgs,
|
||||
sync::{ResourceSync, SyncFileContents},
|
||||
to_komodo_name,
|
||||
to_path_compatible_name,
|
||||
toml::ResourcesToml,
|
||||
update::Log,
|
||||
};
|
||||
@@ -31,7 +31,7 @@ pub async fn get_remote_resources(
|
||||
// =============
|
||||
let root_path = core_config()
|
||||
.sync_directory
|
||||
.join(to_komodo_name(&sync.name));
|
||||
.join(to_path_compatible_name(&sync.name));
|
||||
let (mut logs, mut files, mut file_errors) =
|
||||
(Vec::new(), Vec::new(), Vec::new());
|
||||
let resources = super::file::read_resources(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexMap;
|
||||
use komodo_client::{
|
||||
api::execute::Execution,
|
||||
entities::{
|
||||
@@ -19,7 +20,6 @@ use komodo_client::{
|
||||
toml::ResourceToml,
|
||||
},
|
||||
};
|
||||
use ordered_hash_map::OrderedHashMap;
|
||||
use partial_derive2::{MaybeNone, PartialDiff};
|
||||
|
||||
use crate::resource::KomodoResource;
|
||||
@@ -44,8 +44,8 @@ pub trait ToToml: KomodoResource {
|
||||
|
||||
fn edit_config_object(
|
||||
_resource: &ResourceToml<Self::PartialConfig>,
|
||||
config: OrderedHashMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<OrderedHashMap<String, serde_json::Value>> {
|
||||
config: IndexMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<IndexMap<String, serde_json::Value>> {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
@@ -62,9 +62,9 @@ pub trait ToToml: KomodoResource {
|
||||
resource.config =
|
||||
Self::Config::default().minimize_partial(resource.config);
|
||||
|
||||
let mut resource_map: OrderedHashMap<String, serde_json::Value> =
|
||||
let mut resource_map: IndexMap<String, serde_json::Value> =
|
||||
serde_json::from_str(&serde_json::to_string(&resource)?)?;
|
||||
resource_map.remove("config");
|
||||
resource_map.shift_remove("config");
|
||||
|
||||
let config = serde_json::from_str(&serde_json::to_string(
|
||||
&resource.config,
|
||||
@@ -182,8 +182,8 @@ impl ToToml for Stack {
|
||||
|
||||
fn edit_config_object(
|
||||
_resource: &ResourceToml<Self::PartialConfig>,
|
||||
config: OrderedHashMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<OrderedHashMap<String, serde_json::Value>> {
|
||||
config: IndexMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<IndexMap<String, serde_json::Value>> {
|
||||
config
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
@@ -225,8 +225,8 @@ impl ToToml for Deployment {
|
||||
|
||||
fn edit_config_object(
|
||||
resource: &ResourceToml<Self::PartialConfig>,
|
||||
config: OrderedHashMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<OrderedHashMap<String, serde_json::Value>> {
|
||||
config: IndexMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<IndexMap<String, serde_json::Value>> {
|
||||
config
|
||||
.into_iter()
|
||||
.map(|(key, mut value)| {
|
||||
@@ -278,8 +278,8 @@ impl ToToml for Build {
|
||||
|
||||
fn edit_config_object(
|
||||
resource: &ResourceToml<Self::PartialConfig>,
|
||||
config: OrderedHashMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<OrderedHashMap<String, serde_json::Value>> {
|
||||
config: IndexMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<IndexMap<String, serde_json::Value>> {
|
||||
config
|
||||
.into_iter()
|
||||
.map(|(key, value)| match key.as_str() {
|
||||
@@ -330,8 +330,8 @@ impl ToToml for Repo {
|
||||
|
||||
fn edit_config_object(
|
||||
_resource: &ResourceToml<Self::PartialConfig>,
|
||||
config: OrderedHashMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<OrderedHashMap<String, serde_json::Value>> {
|
||||
config: IndexMap<String, serde_json::Value>,
|
||||
) -> anyhow::Result<IndexMap<String, serde_json::Value>> {
|
||||
config
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
@@ -791,7 +791,7 @@ impl ToToml for Procedure {
|
||||
resource.config =
|
||||
Self::Config::default().minimize_partial(resource.config);
|
||||
|
||||
let mut parsed: OrderedHashMap<String, serde_json::Value> =
|
||||
let mut parsed: IndexMap<String, serde_json::Value> =
|
||||
serde_json::from_str(&serde_json::to_string(&resource)?)?;
|
||||
|
||||
let config = parsed
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
use std::{cmp::Ordering, collections::HashMap};
|
||||
use std::{
|
||||
cmp::Ordering, collections::HashMap, fmt::Write, sync::OnceLock,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use formatting::{Color, bold, colored, muted};
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use komodo_client::{
|
||||
api::{
|
||||
read::ListUserTargetPermissions,
|
||||
write::{
|
||||
CreateUserGroup, DeleteUserGroup, SetUsersInUserGroup,
|
||||
UpdatePermissionOnResourceType, UpdatePermissionOnTarget,
|
||||
CreateUserGroup, DeleteUserGroup, SetEveryoneUserGroup,
|
||||
SetUsersInUserGroup, UpdatePermissionOnResourceType,
|
||||
UpdatePermissionOnTarget,
|
||||
},
|
||||
},
|
||||
entities::{
|
||||
ResourceTarget, ResourceTargetVariant,
|
||||
permission::{PermissionLevel, UserTarget},
|
||||
permission::{
|
||||
PermissionLevel, PermissionLevelAndSpecifics,
|
||||
SpecificPermission, UserTarget,
|
||||
},
|
||||
sync::DiffData,
|
||||
toml::{PermissionToml, UserGroupToml},
|
||||
update::Log,
|
||||
@@ -21,20 +28,109 @@ use komodo_client::{
|
||||
},
|
||||
};
|
||||
use mungos::find::find_collect;
|
||||
use regex::Regex;
|
||||
use resolver_api::Resolve;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
api::{read::ReadArgs, write::WriteArgs},
|
||||
helpers::matcher::Matcher,
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
use super::{AllResourcesById, toml::TOML_PRETTY_OPTIONS};
|
||||
|
||||
/// Used to serialize user group
|
||||
#[derive(Serialize)]
|
||||
struct BasicUserGroupToml {
|
||||
name: String,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
everyone: bool,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
users: Vec<String>,
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
/// Used to serialize user group
|
||||
#[derive(Serialize)]
|
||||
struct Permissions {
|
||||
permissions: Vec<PermissionToml>,
|
||||
}
|
||||
|
||||
pub fn user_group_to_toml(
|
||||
user_group: UserGroupToml,
|
||||
) -> anyhow::Result<String> {
|
||||
// Start with the basic body
|
||||
let basic = BasicUserGroupToml {
|
||||
name: user_group.name,
|
||||
everyone: user_group.everyone,
|
||||
users: if user_group.everyone {
|
||||
Vec::new()
|
||||
} else {
|
||||
user_group.users
|
||||
},
|
||||
};
|
||||
let basic = toml_pretty::to_string(&basic, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize user group to toml")?;
|
||||
let mut res = format!("[[user_group]]\n{basic}");
|
||||
|
||||
// Add "all" permissions
|
||||
for (variant, PermissionLevelAndSpecifics { level, specific }) in
|
||||
user_group.all
|
||||
{
|
||||
// skip 'zero' all permissions
|
||||
if level == PermissionLevel::None && specific.is_empty() {
|
||||
continue;
|
||||
}
|
||||
write!(&mut res, "\nall.{variant} = ")
|
||||
.context("failed to serialize user group 'all' to toml")?;
|
||||
if specific.is_empty() {
|
||||
res.push('"');
|
||||
res.push_str(level.as_ref());
|
||||
res.push('"');
|
||||
} else {
|
||||
let specific = serde_json::to_string(&specific)
|
||||
.context(
|
||||
"failed to serialize user group specifics to... json?",
|
||||
)?
|
||||
.replace(",", ", ");
|
||||
write!(
|
||||
&mut res,
|
||||
"{{ level = \"{level}\", specific = {specific} }}"
|
||||
)
|
||||
.context(
|
||||
"failed to serialize user group 'all' with specifics to toml",
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
// End with resource permissions array
|
||||
if !user_group.permissions.is_empty() {
|
||||
res.push('\n');
|
||||
res.push_str(
|
||||
&toml_pretty::to_string(
|
||||
&Permissions {
|
||||
permissions: user_group.permissions,
|
||||
},
|
||||
TOML_PRETTY_OPTIONS,
|
||||
)
|
||||
.context(
|
||||
"failed to serialize user group permissions to toml",
|
||||
)?,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub struct UpdateItem {
|
||||
user_group: UserGroupToml,
|
||||
update_users: bool,
|
||||
all_diff: HashMap<ResourceTargetVariant, PermissionLevel>,
|
||||
update_everyone: bool,
|
||||
all_diff:
|
||||
IndexMap<ResourceTargetVariant, PermissionLevelAndSpecifics>,
|
||||
}
|
||||
|
||||
pub struct DeleteItem {
|
||||
@@ -64,17 +160,17 @@ pub async fn get_updates_for_view(
|
||||
for (_id, user_group) in map.values() {
|
||||
if !user_groups.iter().any(|ug| ug.name == user_group.name) {
|
||||
diffs.push(DiffData::Delete {
|
||||
current: format!(
|
||||
"[[user_group]]\n{}",
|
||||
toml_pretty::to_string(user_group, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize user group to toml")?
|
||||
),
|
||||
current: user_group_to_toml(user_group.clone())?,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for mut user_group in user_groups {
|
||||
if user_group.everyone {
|
||||
user_group.users.clear();
|
||||
}
|
||||
|
||||
user_group
|
||||
.permissions
|
||||
.retain(|p| p.level > PermissionLevel::None);
|
||||
@@ -91,23 +187,17 @@ pub async fn get_updates_for_view(
|
||||
)
|
||||
})?;
|
||||
|
||||
let (_original_id, original) = match map
|
||||
.get(&user_group.name)
|
||||
.cloned()
|
||||
{
|
||||
Some(original) => original,
|
||||
None => {
|
||||
diffs.push(DiffData::Create {
|
||||
name: user_group.name.clone(),
|
||||
proposed: format!(
|
||||
"[[user_group]]\n{}",
|
||||
toml_pretty::to_string(&user_group, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize user group to toml")?
|
||||
),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let (_original_id, original) =
|
||||
match map.get(&user_group.name).cloned() {
|
||||
Some(original) => original,
|
||||
None => {
|
||||
diffs.push(DiffData::Create {
|
||||
name: user_group.name.clone(),
|
||||
proposed: user_group_to_toml(user_group.clone())?,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
};
|
||||
user_group.users.sort();
|
||||
|
||||
let all_diff = diff_group_all(&original.all, &user_group.all);
|
||||
@@ -115,23 +205,20 @@ pub async fn get_updates_for_view(
|
||||
user_group.permissions.sort_by(sort_permissions);
|
||||
|
||||
let update_users = user_group.users != original.users;
|
||||
let update_everyone = user_group.everyone != original.everyone;
|
||||
let update_all = !all_diff.is_empty();
|
||||
let update_permissions =
|
||||
user_group.permissions != original.permissions;
|
||||
|
||||
// only add log after diff detected
|
||||
if update_users || update_all || update_permissions {
|
||||
if update_users
|
||||
|| update_everyone
|
||||
|| update_all
|
||||
|| update_permissions
|
||||
{
|
||||
diffs.push(DiffData::Update {
|
||||
proposed: format!(
|
||||
"[[user_group]]\n{}",
|
||||
toml_pretty::to_string(&user_group, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize user group to toml")?
|
||||
),
|
||||
current: format!(
|
||||
"[[user_group]]\n{}",
|
||||
toml_pretty::to_string(&original, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize user group to toml")?
|
||||
),
|
||||
proposed: user_group_to_toml(user_group.clone())?,
|
||||
current: user_group_to_toml(original.clone())?,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -152,7 +239,15 @@ pub async fn get_updates_for_execution(
|
||||
.await
|
||||
.context("failed to query db for UserGroups")?
|
||||
.into_iter()
|
||||
.map(|ug| (ug.name.clone(), ug))
|
||||
.map(|mut ug| {
|
||||
if ug.everyone {
|
||||
ug.users.clear();
|
||||
}
|
||||
ug.all.retain(|_, p| {
|
||||
p.level > PermissionLevel::None || !p.specific.is_empty()
|
||||
});
|
||||
(ug.name.clone(), ug)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let mut to_create = Vec::<UserGroupToml>::new();
|
||||
@@ -182,6 +277,10 @@ pub async fn get_updates_for_execution(
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
for mut user_group in user_groups {
|
||||
if user_group.everyone {
|
||||
user_group.users.clear();
|
||||
}
|
||||
|
||||
user_group
|
||||
.permissions
|
||||
.retain(|p| p.level > PermissionLevel::None);
|
||||
@@ -193,7 +292,7 @@ pub async fn get_updates_for_execution(
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to expand user group {} permissions",
|
||||
"Failed to expand user group {} permissions",
|
||||
user_group.name
|
||||
)
|
||||
})?;
|
||||
@@ -303,6 +402,7 @@ pub async fn get_updates_for_execution(
|
||||
PermissionToml {
|
||||
target: p.resource_target,
|
||||
level: p.level,
|
||||
specific: p.specific,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -316,8 +416,10 @@ pub async fn get_updates_for_execution(
|
||||
original_permissions.sort_by(sort_permissions);
|
||||
|
||||
let update_users = user_group.users != original_users;
|
||||
let update_everyone = user_group.everyone != original.everyone;
|
||||
|
||||
// Extend permissions with any existing that have no target in incoming
|
||||
// This makes sure to set those permissions back to None.
|
||||
let to_remove = original_permissions
|
||||
.iter()
|
||||
.filter(|permission| {
|
||||
@@ -329,32 +431,37 @@ pub async fn get_updates_for_execution(
|
||||
.map(|permission| PermissionToml {
|
||||
target: permission.target.clone(),
|
||||
level: PermissionLevel::None,
|
||||
specific: IndexSet::new(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
user_group.permissions.extend(to_remove);
|
||||
|
||||
// remove any permissions that already exist on original
|
||||
user_group.permissions.retain(|permission| {
|
||||
let Some(level) = original_permissions
|
||||
let Some(original_permission) = original_permissions
|
||||
.iter()
|
||||
.find(|p| p.target == permission.target)
|
||||
.map(|p| p.level)
|
||||
else {
|
||||
// not in original, keep it
|
||||
return true;
|
||||
};
|
||||
// keep it if level doesn't match
|
||||
level != permission.level
|
||||
original_permission.level != permission.level
|
||||
|| !specific_equal(
|
||||
&original_permission.specific,
|
||||
&permission.specific,
|
||||
)
|
||||
});
|
||||
|
||||
// only push update after diff detected
|
||||
if update_users
|
||||
|| update_everyone
|
||||
|| !all_diff.is_empty()
|
||||
|| !user_group.permissions.is_empty()
|
||||
{
|
||||
to_update.push(UpdateItem {
|
||||
user_group,
|
||||
update_users,
|
||||
update_everyone,
|
||||
all_diff: all_diff
|
||||
.into_iter()
|
||||
.map(|(k, (_, v))| (k, v))
|
||||
@@ -432,6 +539,13 @@ pub async fn run_updates(
|
||||
&mut has_error,
|
||||
)
|
||||
.await;
|
||||
set_everyone(
|
||||
user_group.name.clone(),
|
||||
user_group.everyone,
|
||||
&mut log,
|
||||
&mut has_error,
|
||||
)
|
||||
.await;
|
||||
run_update_all(
|
||||
user_group.name.clone(),
|
||||
user_group.all,
|
||||
@@ -452,6 +566,7 @@ pub async fn run_updates(
|
||||
for UpdateItem {
|
||||
user_group,
|
||||
update_users,
|
||||
update_everyone,
|
||||
all_diff,
|
||||
} in to_update
|
||||
{
|
||||
@@ -464,6 +579,15 @@ pub async fn run_updates(
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if update_everyone {
|
||||
set_everyone(
|
||||
user_group.name.clone(),
|
||||
user_group.everyone,
|
||||
&mut log,
|
||||
&mut has_error,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if !all_diff.is_empty() {
|
||||
run_update_all(
|
||||
user_group.name.clone(),
|
||||
@@ -548,9 +672,44 @@ async fn set_users(
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_everyone(
|
||||
user_group: String,
|
||||
everyone: bool,
|
||||
log: &mut String,
|
||||
has_error: &mut bool,
|
||||
) {
|
||||
if let Err(e) = (SetEveryoneUserGroup {
|
||||
user_group: user_group.clone(),
|
||||
everyone,
|
||||
})
|
||||
.resolve(&WriteArgs {
|
||||
user: sync_user().to_owned(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
*has_error = true;
|
||||
log.push_str(&format!(
|
||||
"\n{}: failed to set everyone for group {} | {:#}",
|
||||
colored("ERROR", Color::Red),
|
||||
bold(&user_group),
|
||||
e.error
|
||||
))
|
||||
} else {
|
||||
log.push_str(&format!(
|
||||
"\n{}: {} user group '{}' everyone",
|
||||
muted("INFO"),
|
||||
colored("updated", Color::Blue),
|
||||
bold(&user_group)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_update_all(
|
||||
user_group: String,
|
||||
all_diff: HashMap<ResourceTargetVariant, PermissionLevel>,
|
||||
all_diff: IndexMap<
|
||||
ResourceTargetVariant,
|
||||
PermissionLevelAndSpecifics,
|
||||
>,
|
||||
log: &mut String,
|
||||
has_error: &mut bool,
|
||||
) {
|
||||
@@ -589,11 +748,16 @@ async fn run_update_permissions(
|
||||
log: &mut String,
|
||||
has_error: &mut bool,
|
||||
) {
|
||||
for PermissionToml { target, level } in permissions {
|
||||
for PermissionToml {
|
||||
target,
|
||||
level,
|
||||
specific,
|
||||
} in permissions
|
||||
{
|
||||
if let Err(e) = (UpdatePermissionOnTarget {
|
||||
user_target: UserTarget::UserGroup(user_group.clone()),
|
||||
resource_target: target.clone(),
|
||||
permission: level,
|
||||
permission: level.specifics(specific.clone()),
|
||||
})
|
||||
.resolve(&WriteArgs {
|
||||
user: sync_user().to_owned(),
|
||||
@@ -609,12 +773,14 @@ async fn run_update_permissions(
|
||||
))
|
||||
} else {
|
||||
log.push_str(&format!(
|
||||
"\n{}: {} user group '{}' permissions | {}: {target:?} | {}: {level}",
|
||||
"\n{}: {} user group '{}' permissions | {}: {target:?} | {}: {level} | {}: {}",
|
||||
muted("INFO"),
|
||||
colored("updated", Color::Blue),
|
||||
bold(&user_group),
|
||||
muted("target"),
|
||||
muted("level")
|
||||
muted("level"),
|
||||
muted("specific"),
|
||||
specific.into_iter().map(|s| s.into()).collect::<Vec<&'static str>>().join(", ")
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -633,171 +799,217 @@ async fn expand_user_group_permissions(
|
||||
if id.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if id.starts_with('\\') && id.ends_with('\\') {
|
||||
let inner = &id[1..(id.len() - 1)];
|
||||
let regex = Regex::new(inner)
|
||||
.with_context(|| format!("invalid regex. got: {inner}"))?;
|
||||
match variant {
|
||||
ResourceTargetVariant::Build => {
|
||||
let permissions = all_resources
|
||||
.builds
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Build(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Builder => {
|
||||
let permissions = all_resources
|
||||
.builders
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Builder(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Deployment => {
|
||||
let permissions = all_resources
|
||||
.deployments
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Deployment(
|
||||
resource.name.clone(),
|
||||
),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Server => {
|
||||
let permissions = all_resources
|
||||
.servers
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Server(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Repo => {
|
||||
let permissions = all_resources
|
||||
.repos
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Repo(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Alerter => {
|
||||
let permissions = all_resources
|
||||
.alerters
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Alerter(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Procedure => {
|
||||
let permissions = all_resources
|
||||
.procedures
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Procedure(
|
||||
resource.name.clone(),
|
||||
),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Action => {
|
||||
let permissions = all_resources
|
||||
.actions
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Action(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::ResourceSync => {
|
||||
let permissions = all_resources
|
||||
.syncs
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::ResourceSync(
|
||||
resource.name.clone(),
|
||||
),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Stack => {
|
||||
let permissions = all_resources
|
||||
.stacks
|
||||
.values()
|
||||
.filter(|resource| regex.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Stack(resource.name.clone()),
|
||||
level: permission.level,
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::System => {}
|
||||
let matcher = Matcher::new(&id)?;
|
||||
match variant {
|
||||
ResourceTargetVariant::Build => {
|
||||
let permissions = all_resources
|
||||
.builds
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Build(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
} else {
|
||||
// No regex
|
||||
expanded.push(permission);
|
||||
ResourceTargetVariant::Builder => {
|
||||
let permissions = all_resources
|
||||
.builders
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Builder(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Deployment => {
|
||||
let permissions = all_resources
|
||||
.deployments
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Deployment(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Server => {
|
||||
let permissions = all_resources
|
||||
.servers
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Server(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Repo => {
|
||||
let permissions = all_resources
|
||||
.repos
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Repo(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Alerter => {
|
||||
let permissions = all_resources
|
||||
.alerters
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Alerter(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Procedure => {
|
||||
let permissions = all_resources
|
||||
.procedures
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Procedure(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Action => {
|
||||
let permissions = all_resources
|
||||
.actions
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Action(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::ResourceSync => {
|
||||
let permissions = all_resources
|
||||
.syncs
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::ResourceSync(
|
||||
resource.name.clone(),
|
||||
),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::Stack => {
|
||||
let permissions = all_resources
|
||||
.stacks
|
||||
.values()
|
||||
.filter(|resource| matcher.is_match(&resource.name))
|
||||
.map(|resource| PermissionToml {
|
||||
target: ResourceTarget::Stack(resource.name.clone()),
|
||||
level: permission.level,
|
||||
specific: permission.specific.clone(),
|
||||
});
|
||||
expanded.extend(permissions);
|
||||
}
|
||||
ResourceTargetVariant::System => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded)
|
||||
}
|
||||
|
||||
type AllDiff =
|
||||
HashMap<ResourceTargetVariant, (PermissionLevel, PermissionLevel)>;
|
||||
type AllDiff = IndexMap<
|
||||
ResourceTargetVariant,
|
||||
(PermissionLevelAndSpecifics, PermissionLevelAndSpecifics),
|
||||
>;
|
||||
|
||||
fn default_permission() -> &'static PermissionLevelAndSpecifics {
|
||||
static DEFAULT_PERMISSION: OnceLock<PermissionLevelAndSpecifics> =
|
||||
OnceLock::new();
|
||||
DEFAULT_PERMISSION.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
/// diffs user_group.all
|
||||
fn diff_group_all(
|
||||
original: &HashMap<ResourceTargetVariant, PermissionLevel>,
|
||||
incoming: &HashMap<ResourceTargetVariant, PermissionLevel>,
|
||||
original: &IndexMap<
|
||||
ResourceTargetVariant,
|
||||
PermissionLevelAndSpecifics,
|
||||
>,
|
||||
incoming: &IndexMap<
|
||||
ResourceTargetVariant,
|
||||
PermissionLevelAndSpecifics,
|
||||
>,
|
||||
) -> AllDiff {
|
||||
let mut to_update = HashMap::new();
|
||||
let mut to_update = IndexMap::new();
|
||||
|
||||
// need to compare both forward and backward because either hashmap could be sparse.
|
||||
|
||||
// forward direction
|
||||
for (variant, level) in incoming {
|
||||
let original_level = original.get(variant).unwrap_or_default();
|
||||
if level == original_level {
|
||||
continue;
|
||||
for (variant, permission) in incoming {
|
||||
let original_permission =
|
||||
original.get(variant).unwrap_or(default_permission());
|
||||
if permission.level != original_permission.level
|
||||
|| !specific_equal(
|
||||
&original_permission.specific,
|
||||
&permission.specific,
|
||||
)
|
||||
{
|
||||
to_update.insert(
|
||||
*variant,
|
||||
(original_permission.clone(), permission.clone()),
|
||||
);
|
||||
}
|
||||
to_update.insert(*variant, (*original_level, *level));
|
||||
}
|
||||
|
||||
// backward direction
|
||||
for (variant, level) in original {
|
||||
let incoming_level = incoming.get(variant).unwrap_or_default();
|
||||
if level == incoming_level {
|
||||
continue;
|
||||
for (variant, permission) in original {
|
||||
let incoming_permission =
|
||||
incoming.get(variant).unwrap_or(default_permission());
|
||||
if permission.level != incoming_permission.level
|
||||
|| !specific_equal(
|
||||
&incoming_permission.specific,
|
||||
&permission.specific,
|
||||
)
|
||||
{
|
||||
to_update.insert(
|
||||
*variant,
|
||||
(permission.clone(), incoming_permission.clone()),
|
||||
);
|
||||
}
|
||||
to_update.insert(*variant, (*level, *incoming_level));
|
||||
}
|
||||
|
||||
to_update
|
||||
}
|
||||
|
||||
fn specific_equal(
|
||||
a: &IndexSet<SpecificPermission>,
|
||||
b: &IndexSet<SpecificPermission>,
|
||||
) -> bool {
|
||||
for item in a {
|
||||
if !b.contains(item) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for item in b {
|
||||
if !a.contains(item) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub async fn convert_user_groups(
|
||||
user_groups: impl Iterator<Item = UserGroup>,
|
||||
all: &AllResourcesById,
|
||||
@@ -811,7 +1023,11 @@ pub async fn convert_user_groups(
|
||||
.map(|user| (user.id, user.username))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
for user_group in user_groups {
|
||||
for mut user_group in user_groups {
|
||||
user_group.all.retain(|_, p| {
|
||||
p.level > PermissionLevel::None || !p.specific.is_empty()
|
||||
});
|
||||
|
||||
// this method is admin only, but we already know user can see user group if above does not return Err
|
||||
let mut permissions = (ListUserTargetPermissions {
|
||||
user_target: UserTarget::UserGroup(user_group.id.clone()),
|
||||
@@ -825,6 +1041,7 @@ pub async fn convert_user_groups(
|
||||
.await
|
||||
.map_err(|e| e.error)?
|
||||
.into_iter()
|
||||
.filter(|permission| permission.level > PermissionLevel::None)
|
||||
.map(|mut permission| {
|
||||
match &mut permission.resource_target {
|
||||
ResourceTarget::Build(id) => {
|
||||
@@ -902,14 +1119,20 @@ pub async fn convert_user_groups(
|
||||
PermissionToml {
|
||||
target: permission.resource_target,
|
||||
level: permission.level,
|
||||
specific: permission.specific,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut users = user_group
|
||||
.users
|
||||
.into_iter()
|
||||
.filter_map(|user_id| usernames.get(&user_id).cloned())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut users = if user_group.everyone {
|
||||
Vec::new()
|
||||
} else {
|
||||
user_group
|
||||
.users
|
||||
.into_iter()
|
||||
.filter_map(|user_id| usernames.get(&user_id).cloned())
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
permissions.sort_by(sort_permissions);
|
||||
users.sort();
|
||||
@@ -918,8 +1141,9 @@ pub async fn convert_user_groups(
|
||||
user_group.id,
|
||||
UserGroupToml {
|
||||
name: user_group.name,
|
||||
users,
|
||||
everyone: user_group.everyone,
|
||||
all: user_group.all,
|
||||
users,
|
||||
permissions,
|
||||
},
|
||||
));
|
||||
|
||||
@@ -15,6 +15,14 @@ use crate::{api::write::WriteArgs, state::db_client};
|
||||
|
||||
use super::toml::TOML_PRETTY_OPTIONS;
|
||||
|
||||
pub fn variable_to_toml(
|
||||
variable: &Variable,
|
||||
) -> anyhow::Result<String> {
|
||||
let inner = toml_pretty::to_string(variable, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize variable to toml")?;
|
||||
Ok(format!("[[variable]]\n{inner}"))
|
||||
}
|
||||
|
||||
pub struct ToUpdateItem {
|
||||
pub variable: Variable,
|
||||
pub update_value: bool,
|
||||
@@ -39,11 +47,7 @@ pub async fn get_updates_for_view(
|
||||
for variable in map.values() {
|
||||
if !variables.iter().any(|v| v.name == variable.name) {
|
||||
diffs.push(DiffData::Delete {
|
||||
current: format!(
|
||||
"[[variable]]\n{}",
|
||||
toml_pretty::to_string(&variable, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize variable to toml")?
|
||||
),
|
||||
current: variable_to_toml(variable)?,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -58,26 +62,14 @@ pub async fn get_updates_for_view(
|
||||
continue;
|
||||
}
|
||||
diffs.push(DiffData::Update {
|
||||
proposed: format!(
|
||||
"[[variable]]\n{}",
|
||||
toml_pretty::to_string(variable, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize variable to toml")?
|
||||
),
|
||||
current: format!(
|
||||
"[[variable]]\n{}",
|
||||
toml_pretty::to_string(original, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize variable to toml")?
|
||||
),
|
||||
proposed: variable_to_toml(variable)?,
|
||||
current: variable_to_toml(original)?,
|
||||
});
|
||||
}
|
||||
None => {
|
||||
diffs.push(DiffData::Create {
|
||||
name: variable.name.clone(),
|
||||
proposed: format!(
|
||||
"[[variable]]\n{}",
|
||||
toml_pretty::to_string(variable, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize variable to toml")?
|
||||
),
|
||||
proposed: variable_to_toml(variable)?,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,10 @@ use komodo_client::{
|
||||
entities::{permission::PermissionLevel, server::Server},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
helpers::periphery_client, resource, ws::core_periphery_forward_ws,
|
||||
};
|
||||
use crate::permission::get_check_permissions;
|
||||
|
||||
#[instrument(name = "ConnectContainerExec", skip(ws))]
|
||||
pub async fn handler(
|
||||
pub async fn terminal(
|
||||
Query(ConnectContainerExecQuery {
|
||||
server,
|
||||
container,
|
||||
@@ -22,60 +20,36 @@ pub async fn handler(
|
||||
ws: WebSocketUpgrade,
|
||||
) -> impl IntoResponse {
|
||||
ws.on_upgrade(|socket| async move {
|
||||
let Some((mut client_socket, user)) = super::ws_login(socket).await
|
||||
let Some((mut client_socket, user)) =
|
||||
super::ws_login(socket).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let server = match resource::get_check_permissions::<Server>(
|
||||
let server = match get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.terminal(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(server) => server,
|
||||
Err(e) => {
|
||||
debug!("could not get server | {e:#}");
|
||||
let _ =
|
||||
client_socket.send(Message::text(format!("ERROR: {e:#}"))).await;
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let periphery = match periphery_client(&server) {
|
||||
Ok(periphery) => periphery,
|
||||
Err(e) => {
|
||||
debug!("couldn't get periphery | {e:#}");
|
||||
let _ =
|
||||
client_socket.send(Message::text(format!("ERROR: {e:#}"))).await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!("connecting to periphery container exec websocket");
|
||||
|
||||
let periphery_socket = match periphery
|
||||
.connect_container_exec(
|
||||
container,
|
||||
shell
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(ws) => ws,
|
||||
Err(e) => {
|
||||
debug!("Failed connect to periphery container exec websocket | {e:#}");
|
||||
let _ =
|
||||
client_socket.send(Message::text(format!("ERROR: {e:#}"))).await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!("connected to periphery container exec websocket");
|
||||
|
||||
core_periphery_forward_ws(client_socket, periphery_socket).await
|
||||
super::handle_container_terminal(
|
||||
client_socket,
|
||||
&server,
|
||||
container,
|
||||
shell,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
69
bin/core/src/ws/deployment.rs
Normal file
69
bin/core/src/ws/deployment.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use axum::{
|
||||
extract::{Query, WebSocketUpgrade, ws::Message},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use futures::SinkExt;
|
||||
use komodo_client::{
|
||||
api::terminal::ConnectDeploymentExecQuery,
|
||||
entities::{
|
||||
deployment::Deployment, permission::PermissionLevel,
|
||||
server::Server,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{permission::get_check_permissions, resource::get};
|
||||
|
||||
#[instrument(name = "ConnectDeploymentExec", skip(ws))]
|
||||
pub async fn terminal(
|
||||
Query(ConnectDeploymentExecQuery { deployment, shell }): Query<
|
||||
ConnectDeploymentExecQuery,
|
||||
>,
|
||||
ws: WebSocketUpgrade,
|
||||
) -> impl IntoResponse {
|
||||
ws.on_upgrade(|socket| async move {
|
||||
let Some((mut client_socket, user)) =
|
||||
super::ws_login(socket).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let deployment = match get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read.terminal(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(deployment) => deployment,
|
||||
Err(e) => {
|
||||
debug!("could not get deployment | {e:#}");
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let server =
|
||||
match get::<Server>(&deployment.config.server_id).await {
|
||||
Ok(server) => server,
|
||||
Err(e) => {
|
||||
debug!("could not get server | {e:#}");
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
super::handle_container_terminal(
|
||||
client_socket,
|
||||
&server,
|
||||
deployment.name,
|
||||
shell,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
@@ -9,7 +9,10 @@ use axum::{
|
||||
routing::get,
|
||||
};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use komodo_client::{entities::user::User, ws::WsLoginMessage};
|
||||
use komodo_client::{
|
||||
entities::{server::Server, user::User},
|
||||
ws::WsLoginMessage,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_tungstenite::{
|
||||
MaybeTlsStream, WebSocketStream, tungstenite,
|
||||
@@ -17,6 +20,8 @@ use tokio_tungstenite::{
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
mod container;
|
||||
mod deployment;
|
||||
mod stack;
|
||||
mod terminal;
|
||||
mod update;
|
||||
|
||||
@@ -24,7 +29,9 @@ pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/update", get(update::handler))
|
||||
.route("/terminal", get(terminal::handler))
|
||||
.route("/container", get(container::handler))
|
||||
.route("/container/terminal", get(container::terminal))
|
||||
.route("/deployment/terminal", get(deployment::terminal))
|
||||
.route("/stack/terminal", get(stack::terminal))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
@@ -118,6 +125,48 @@ async fn check_user_valid(user_id: &str) -> anyhow::Result<User> {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
async fn handle_container_terminal(
|
||||
mut client_socket: WebSocket,
|
||||
server: &Server,
|
||||
container: String,
|
||||
shell: String,
|
||||
) {
|
||||
let periphery = match crate::helpers::periphery_client(server) {
|
||||
Ok(periphery) => periphery,
|
||||
Err(e) => {
|
||||
debug!("couldn't get periphery | {e:#}");
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!("connecting to periphery container exec websocket");
|
||||
|
||||
let periphery_socket = match periphery
|
||||
.connect_container_exec(container, shell)
|
||||
.await
|
||||
{
|
||||
Ok(ws) => ws,
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Failed connect to periphery container exec websocket | {e:#}"
|
||||
);
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
trace!("connected to periphery container exec websocket");
|
||||
|
||||
core_periphery_forward_ws(client_socket, periphery_socket).await
|
||||
}
|
||||
|
||||
async fn core_periphery_forward_ws(
|
||||
client_socket: axum::extract::ws::WebSocket,
|
||||
periphery_socket: WebSocketStream<MaybeTlsStream<TcpStream>>,
|
||||
@@ -143,9 +192,7 @@ async fn core_periphery_forward_ws(
|
||||
if let Err(e) =
|
||||
periphery_send.send(axum_to_tungstenite(msg)).await
|
||||
{
|
||||
debug!(
|
||||
"Failed to send terminal message | {e:?}",
|
||||
);
|
||||
debug!("Failed to send terminal message | {e:?}",);
|
||||
cancel.cancel();
|
||||
break;
|
||||
};
|
||||
|
||||
90
bin/core/src/ws/stack.rs
Normal file
90
bin/core/src/ws/stack.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use axum::{
|
||||
extract::{Query, WebSocketUpgrade, ws::Message},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use futures::SinkExt;
|
||||
use komodo_client::{
|
||||
api::terminal::ConnectStackExecQuery,
|
||||
entities::{
|
||||
permission::PermissionLevel, server::Server, stack::Stack,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{permission::get_check_permissions, resource::get};
|
||||
|
||||
#[instrument(name = "ConnectStackExec", skip(ws))]
|
||||
pub async fn terminal(
|
||||
Query(ConnectStackExecQuery {
|
||||
stack,
|
||||
service,
|
||||
shell,
|
||||
}): Query<ConnectStackExecQuery>,
|
||||
ws: WebSocketUpgrade,
|
||||
) -> impl IntoResponse {
|
||||
ws.on_upgrade(|socket| async move {
|
||||
let Some((mut client_socket, user)) =
|
||||
super::ws_login(socket).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let stack = match get_check_permissions::<Stack>(
|
||||
&stack,
|
||||
&user,
|
||||
PermissionLevel::Read.terminal(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(stack) => stack,
|
||||
Err(e) => {
|
||||
debug!("could not get stack | {e:#}");
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let server = match get::<Server>(&stack.config.server_id).await {
|
||||
Ok(server) => server,
|
||||
Err(e) => {
|
||||
debug!("could not get server | {e:#}");
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!("ERROR: {e:#}")))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let services = stack
|
||||
.info
|
||||
.deployed_services
|
||||
.unwrap_or(stack.info.latest_services);
|
||||
|
||||
let container = match services
|
||||
.into_iter()
|
||||
.find(|s| s.service_name == service)
|
||||
{
|
||||
Some(service) => service.container_name,
|
||||
None => {
|
||||
let _ = client_socket
|
||||
.send(Message::text(format!(
|
||||
"ERROR: Service {service} could not be found"
|
||||
)))
|
||||
.await;
|
||||
let _ = client_socket.close().await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
super::handle_container_terminal(
|
||||
client_socket,
|
||||
&server,
|
||||
container,
|
||||
shell,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
@@ -9,7 +9,8 @@ use komodo_client::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
helpers::periphery_client, resource, ws::core_periphery_forward_ws,
|
||||
helpers::periphery_client, permission::get_check_permissions,
|
||||
ws::core_periphery_forward_ws,
|
||||
};
|
||||
|
||||
#[instrument(name = "ConnectTerminal", skip(ws))]
|
||||
@@ -26,10 +27,10 @@ pub async fn handler(
|
||||
return;
|
||||
};
|
||||
|
||||
let server = match resource::get_check_permissions::<Server>(
|
||||
let server = match get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Read.terminal(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -90,9 +90,9 @@ async fn user_can_see_update(
|
||||
if user.admin {
|
||||
return Ok(());
|
||||
}
|
||||
let permissions =
|
||||
let permission =
|
||||
get_user_permission_on_target(user, update_target).await?;
|
||||
if permissions > PermissionLevel::None {
|
||||
if permission.level > PermissionLevel::None {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
FROM rust:1.86.0-bullseye AS builder
|
||||
FROM rust:1.87.0-bullseye AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
@@ -14,7 +14,7 @@ use komodo_client::{
|
||||
EnvironmentVar, Version,
|
||||
build::{Build, BuildConfig},
|
||||
environment_vars_from_str, get_image_name, optional_string,
|
||||
to_komodo_name,
|
||||
to_docker_compatible_name, to_path_compatible_name,
|
||||
update::Log,
|
||||
},
|
||||
parsers::QUOTE_PATTERN,
|
||||
@@ -45,8 +45,9 @@ impl Resolve<super::Args> for GetDockerfileContentsOnHost {
|
||||
dockerfile_path,
|
||||
} = self;
|
||||
|
||||
let root =
|
||||
periphery_config().build_dir().join(to_komodo_name(&name));
|
||||
let root = periphery_config()
|
||||
.build_dir()
|
||||
.join(to_path_compatible_name(&name));
|
||||
let build_dir =
|
||||
root.join(&build_path).components().collect::<PathBuf>();
|
||||
|
||||
@@ -92,7 +93,7 @@ impl Resolve<super::Args> for WriteDockerfileContentsToHost {
|
||||
} = self;
|
||||
let full_path = periphery_config()
|
||||
.build_dir()
|
||||
.join(to_komodo_name(&name))
|
||||
.join(to_path_compatible_name(&name))
|
||||
.join(&build_path)
|
||||
.join(dockerfile_path)
|
||||
.components()
|
||||
@@ -177,7 +178,7 @@ impl Resolve<super::Args> for build::Build {
|
||||
}
|
||||
};
|
||||
|
||||
let name = to_komodo_name(name);
|
||||
let name = to_docker_compatible_name(name);
|
||||
|
||||
let build_path =
|
||||
periphery_config().build_dir().join(&name).join(build_path);
|
||||
|
||||
@@ -5,7 +5,8 @@ use command::run_komodo_command;
|
||||
use formatting::format_serror;
|
||||
use git::{GitRes, write_commit_file};
|
||||
use komodo_client::entities::{
|
||||
FileContents, stack::ComposeProject, to_komodo_name, update::Log,
|
||||
FileContents, stack::ComposeProject, to_path_compatible_name,
|
||||
update::Log,
|
||||
};
|
||||
use periphery_client::api::{compose::*, git::RepoActionResponse};
|
||||
use resolver_api::Resolve;
|
||||
@@ -137,8 +138,9 @@ impl Resolve<super::Args> for GetComposeContentsOnHost {
|
||||
run_directory,
|
||||
file_paths,
|
||||
} = self;
|
||||
let root =
|
||||
periphery_config().stack_dir().join(to_komodo_name(&name));
|
||||
let root = periphery_config()
|
||||
.stack_dir()
|
||||
.join(to_path_compatible_name(&name));
|
||||
let run_directory =
|
||||
root.join(&run_directory).components().collect::<PathBuf>();
|
||||
|
||||
@@ -197,7 +199,7 @@ impl Resolve<super::Args> for WriteComposeContentsToHost {
|
||||
} = self;
|
||||
let file_path = periphery_config()
|
||||
.stack_dir()
|
||||
.join(to_komodo_name(&name))
|
||||
.join(to_path_compatible_name(&name))
|
||||
.join(&run_directory)
|
||||
.join(file_path)
|
||||
.components()
|
||||
|
||||
@@ -3,7 +3,7 @@ use command::run_komodo_command;
|
||||
use futures::future::join_all;
|
||||
use komodo_client::entities::{
|
||||
docker::container::{Container, ContainerListItem, ContainerStats},
|
||||
to_komodo_name,
|
||||
to_docker_compatible_name,
|
||||
update::Log,
|
||||
};
|
||||
use periphery_client::api::container::*;
|
||||
@@ -234,7 +234,7 @@ impl Resolve<super::Args> for RenameContainer {
|
||||
curr_name,
|
||||
new_name,
|
||||
} = self;
|
||||
let new = to_komodo_name(&new_name);
|
||||
let new = to_docker_compatible_name(&new_name);
|
||||
let command = format!("docker rename {curr_name} {new}");
|
||||
Ok(run_komodo_command("Docker Rename", None, command).await)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use komodo_client::{
|
||||
Conversion, Deployment, DeploymentConfig, DeploymentImage,
|
||||
RestartMode, conversions_from_str, extract_registry_domain,
|
||||
},
|
||||
environment_vars_from_str, to_komodo_name,
|
||||
environment_vars_from_str, to_docker_compatible_name,
|
||||
update::Log,
|
||||
},
|
||||
parsers::QUOTE_PATTERN,
|
||||
@@ -129,7 +129,7 @@ fn docker_run_command(
|
||||
}: &Deployment,
|
||||
image: &str,
|
||||
) -> anyhow::Result<String> {
|
||||
let name = to_komodo_name(name);
|
||||
let name = to_docker_compatible_name(name);
|
||||
let ports = parse_conversions(
|
||||
&conversions_from_str(ports).context("Invalid ports")?,
|
||||
"-p",
|
||||
|
||||
@@ -14,7 +14,7 @@ use komodo_client::entities::{
|
||||
ComposeFile, ComposeService, ComposeServiceDeploy, Stack,
|
||||
StackServiceNames,
|
||||
},
|
||||
to_komodo_name,
|
||||
to_path_compatible_name,
|
||||
update::Log,
|
||||
};
|
||||
use periphery_client::api::{
|
||||
@@ -431,7 +431,7 @@ pub async fn write_stack(
|
||||
)> {
|
||||
let root = periphery_config()
|
||||
.stack_dir()
|
||||
.join(to_komodo_name(&stack.name));
|
||||
.join(to_path_compatible_name(&stack.name));
|
||||
let run_directory = root.join(&stack.config.run_directory);
|
||||
// This will remove any intermediate '/./' in the path, which is a problem for some OS.
|
||||
// Cannot use 'canonicalize' yet as directory may not exist.
|
||||
@@ -694,7 +694,7 @@ async fn compose_down(
|
||||
format!(" {}", services.join(" "))
|
||||
};
|
||||
let log = run_komodo_command(
|
||||
"compose down",
|
||||
"Compose Down",
|
||||
None,
|
||||
format!("{docker_compose} -p {project} down{service_args}"),
|
||||
)
|
||||
|
||||
@@ -73,6 +73,9 @@ pub fn periphery_config() -> &'static PeripheryConfig {
|
||||
.periphery_logging_opentelemetry_service_name
|
||||
.unwrap_or(config.logging.opentelemetry_service_name),
|
||||
},
|
||||
pretty_startup_config: env
|
||||
.periphery_pretty_startup_config
|
||||
.unwrap_or(config.pretty_startup_config),
|
||||
allowed_ips: env
|
||||
.periphery_allowed_ips
|
||||
.unwrap_or(config.allowed_ips),
|
||||
|
||||
@@ -3,8 +3,11 @@ use std::{collections::HashMap, sync::OnceLock};
|
||||
use anyhow::{Context, anyhow};
|
||||
use bollard::{
|
||||
Docker,
|
||||
container::{InspectContainerOptions, ListContainersOptions},
|
||||
network::InspectNetworkOptions,
|
||||
query_parameters::{
|
||||
InspectContainerOptions, InspectNetworkOptions,
|
||||
ListContainersOptions, ListImagesOptions, ListNetworksOptions,
|
||||
ListVolumesOptions,
|
||||
},
|
||||
};
|
||||
use command::run_komodo_command;
|
||||
use komodo_client::entities::{
|
||||
@@ -13,7 +16,7 @@ use komodo_client::entities::{
|
||||
ContainerConfig, GraphDriverData, HealthConfig, PortBinding,
|
||||
container::*, image::*, network::*, volume::*,
|
||||
},
|
||||
to_komodo_name,
|
||||
to_docker_compatible_name,
|
||||
update::Log,
|
||||
};
|
||||
use run_command::async_run_command;
|
||||
@@ -42,7 +45,7 @@ impl DockerClient {
|
||||
) -> anyhow::Result<Vec<ContainerListItem>> {
|
||||
let mut containers = self
|
||||
.docker
|
||||
.list_containers(Some(ListContainersOptions::<String> {
|
||||
.list_containers(Some(ListContainersOptions {
|
||||
all: true,
|
||||
..Default::default()
|
||||
}))
|
||||
@@ -63,11 +66,16 @@ impl DockerClient {
|
||||
created: container.created,
|
||||
size_rw: container.size_rw,
|
||||
size_root_fs: container.size_root_fs,
|
||||
state: container
|
||||
.state
|
||||
.context("no container state")?
|
||||
.parse()
|
||||
.context("failed to parse container state")?,
|
||||
state: match container.state.context("no container state")? {
|
||||
bollard::secret::ContainerSummaryStateEnum::EMPTY => ContainerStateStatusEnum::Empty,
|
||||
bollard::secret::ContainerSummaryStateEnum::CREATED => ContainerStateStatusEnum::Created,
|
||||
bollard::secret::ContainerSummaryStateEnum::RUNNING => ContainerStateStatusEnum::Running,
|
||||
bollard::secret::ContainerSummaryStateEnum::PAUSED => ContainerStateStatusEnum::Paused,
|
||||
bollard::secret::ContainerSummaryStateEnum::RESTARTING => ContainerStateStatusEnum::Restarting,
|
||||
bollard::secret::ContainerSummaryStateEnum::EXITED => ContainerStateStatusEnum::Exited,
|
||||
bollard::secret::ContainerSummaryStateEnum::REMOVING => ContainerStateStatusEnum::Removing,
|
||||
bollard::secret::ContainerSummaryStateEnum::DEAD => ContainerStateStatusEnum::Dead,
|
||||
},
|
||||
status: container.status,
|
||||
network_mode: container
|
||||
.host_config
|
||||
@@ -371,6 +379,7 @@ impl DockerClient {
|
||||
bollard::secret::MountTypeEnum::EMPTY => MountTypeEnum::Empty,
|
||||
bollard::secret::MountTypeEnum::BIND => MountTypeEnum::Bind,
|
||||
bollard::secret::MountTypeEnum::VOLUME => MountTypeEnum::Volume,
|
||||
bollard::secret::MountTypeEnum::IMAGE => MountTypeEnum::Image,
|
||||
bollard::secret::MountTypeEnum::TMPFS => MountTypeEnum::Tmpfs,
|
||||
bollard::secret::MountTypeEnum::NPIPE => MountTypeEnum::Npipe,
|
||||
bollard::secret::MountTypeEnum::CLUSTER => MountTypeEnum::Cluster,
|
||||
@@ -456,6 +465,7 @@ impl DockerClient {
|
||||
bollard::secret::MountPointTypeEnum::EMPTY => MountTypeEnum::Empty,
|
||||
bollard::secret::MountPointTypeEnum::BIND => MountTypeEnum::Bind,
|
||||
bollard::secret::MountPointTypeEnum::VOLUME => MountTypeEnum::Volume,
|
||||
bollard::secret::MountPointTypeEnum::IMAGE => MountTypeEnum::Image,
|
||||
bollard::secret::MountPointTypeEnum::TMPFS => MountTypeEnum::Tmpfs,
|
||||
bollard::secret::MountPointTypeEnum::NPIPE => MountTypeEnum::Npipe,
|
||||
bollard::secret::MountPointTypeEnum::CLUSTER => MountTypeEnum::Cluster,
|
||||
@@ -543,7 +553,7 @@ impl DockerClient {
|
||||
) -> anyhow::Result<Vec<NetworkListItem>> {
|
||||
let networks = self
|
||||
.docker
|
||||
.list_networks::<String>(None)
|
||||
.list_networks(Option::<ListNetworksOptions>::None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|network| {
|
||||
@@ -593,7 +603,7 @@ impl DockerClient {
|
||||
) -> anyhow::Result<Network> {
|
||||
let network = self
|
||||
.docker
|
||||
.inspect_network::<String>(
|
||||
.inspect_network(
|
||||
network_name,
|
||||
InspectNetworkOptions {
|
||||
verbose: true,
|
||||
@@ -653,7 +663,7 @@ impl DockerClient {
|
||||
) -> anyhow::Result<Vec<ImageListItem>> {
|
||||
let images = self
|
||||
.docker
|
||||
.list_images::<String>(None)
|
||||
.list_images(Option::<ListImagesOptions>::None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|image| {
|
||||
@@ -787,7 +797,7 @@ impl DockerClient {
|
||||
) -> anyhow::Result<Vec<VolumeListItem>> {
|
||||
let volumes = self
|
||||
.docker
|
||||
.list_volumes::<String>(None)
|
||||
.list_volumes(Option::<ListVolumesOptions>::None)
|
||||
.await?
|
||||
.volumes
|
||||
.unwrap_or_default()
|
||||
@@ -977,7 +987,7 @@ pub fn stop_container_command(
|
||||
signal: Option<TerminationSignal>,
|
||||
time: Option<i32>,
|
||||
) -> String {
|
||||
let container_name = to_komodo_name(container_name);
|
||||
let container_name = to_docker_compatible_name(container_name);
|
||||
let signal = signal
|
||||
.map(|signal| format!(" --signal {signal}"))
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -4,7 +4,7 @@ use anyhow::{Context, anyhow};
|
||||
use komodo_client::{
|
||||
entities::{
|
||||
CloneArgs, EnvironmentVar, SearchCombinator, stack::Stack,
|
||||
to_komodo_name,
|
||||
to_path_compatible_name,
|
||||
},
|
||||
parsers::QUOTE_PATTERN,
|
||||
};
|
||||
@@ -102,7 +102,7 @@ pub async fn pull_or_clone_stack(
|
||||
|
||||
let root = periphery_config()
|
||||
.stack_dir()
|
||||
.join(to_komodo_name(&stack.name));
|
||||
.join(to_path_compatible_name(&stack.name));
|
||||
|
||||
let mut args: CloneArgs = stack.into();
|
||||
// Set the clone destination to the one created for this run
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use anyhow::Context;
|
||||
use axum_server::tls_rustls::RustlsConfig;
|
||||
use config::periphery_config;
|
||||
|
||||
mod api;
|
||||
mod compose;
|
||||
@@ -22,7 +23,12 @@ async fn app() -> anyhow::Result<()> {
|
||||
logger::init(&config.logging)?;
|
||||
|
||||
info!("Komodo Periphery version: v{}", env!("CARGO_PKG_VERSION"));
|
||||
info!("{:?}", config.sanitized());
|
||||
|
||||
if periphery_config().pretty_startup_config {
|
||||
info!("{:#?}", config.sanitized());
|
||||
} else {
|
||||
info!("{:?}", config.sanitized());
|
||||
}
|
||||
|
||||
stats::spawn_system_stats_polling_thread();
|
||||
|
||||
|
||||
23
bin/util/Cargo.toml
Normal file
23
bin/util/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "komodo_util"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "util"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
tracing-subscriber.workspace = true
|
||||
futures-util.workspace = true
|
||||
dotenvy.workspace = true
|
||||
tracing.workspace = true
|
||||
anyhow.workspace = true
|
||||
mungos.workspace = true
|
||||
tokio.workspace = true
|
||||
serde.workspace = true
|
||||
envy.workspace = true
|
||||
22
bin/util/aio.Dockerfile
Normal file
22
bin/util/aio.Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM rust:1.87.0-bullseye AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
COPY ./lib ./lib
|
||||
COPY ./client/core/rs ./client/core/rs
|
||||
COPY ./client/periphery ./client/periphery
|
||||
COPY ./bin/util ./bin/util
|
||||
|
||||
# Compile bin
|
||||
RUN cargo build -p komodo_util --release
|
||||
|
||||
# Copy binaries to distroless base
|
||||
FROM gcr.io/distroless/cc
|
||||
|
||||
COPY --from=builder /builder/target/release/util /usr/local/bin/util
|
||||
|
||||
CMD [ "util" ]
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/moghtech/komodo
|
||||
LABEL org.opencontainers.image.description="Komodo Util"
|
||||
LABEL org.opencontainers.image.licenses=GPL-3.0
|
||||
139
bin/util/docs/copy-database.md
Normal file
139
bin/util/docs/copy-database.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Copy Database Utility
|
||||
|
||||
Copy the Komodo database contents between running, mongo-compatible databases.
|
||||
Can be used to move between MongoDB / FerretDB, or upgrade from FerretDB v1 to v2.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
|
||||
copy_database:
|
||||
image: ghcr.io/moghtech/komodo-util
|
||||
environment:
|
||||
MODE: CopyDatabase
|
||||
SOURCE_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@source:27017
|
||||
SOURCE_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
TARGET_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@target:27017
|
||||
TARGET_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
|
||||
```
|
||||
|
||||
## FerretDB v2 Update Guide
|
||||
|
||||
Up to Komodo 1.17.5, users who wanted to use Postgres / Sqlite were instructed to deploy FerretDB v1.
|
||||
Now that v2 is out however, v1 will go largely unsupported. Users are recommended to migrate to v2 for
|
||||
the best performance and ongoing support / updates, however the internal data structures
|
||||
have changed and this cannot be done in-place.
|
||||
|
||||
Also note that FerretDB v2 no longer supports Sqlite, and only supports
|
||||
a [customized Postgres distribution](https://docs.ferretdb.io/installation/documentdb/docker/).
|
||||
Nonetheless, it remains a solid option for hosts which [do not support mongo](https://github.com/moghtech/komodo/issues/59).
|
||||
|
||||
Also note, the same basic process outlined below can also be used to move between MongoDB and FerretDB, just replace FerretDB v2
|
||||
with the database you wish to move to.
|
||||
|
||||
### **Step 1**: *Add* the new database to the top of your existing Komodo compose file.
|
||||
|
||||
**Don't forget to also add the new volumes.**
|
||||
|
||||
```yaml
|
||||
## In Komodo compose.yaml
|
||||
services:
|
||||
postgres2:
|
||||
# Recommended: Pin to a specific version
|
||||
# https://github.com/FerretDB/documentdb/pkgs/container/postgres-documentdb
|
||||
image: ghcr.io/ferretdb/postgres-documentdb
|
||||
labels:
|
||||
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: ${COMPOSE_LOGGING_DRIVER:-local}
|
||||
# ports:
|
||||
# - 5432:5432
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: ${KOMODO_DB_USERNAME}
|
||||
POSTGRES_PASSWORD: ${KOMODO_DB_PASSWORD}
|
||||
POSTGRES_DB: postgres
|
||||
|
||||
ferretdb2:
|
||||
# Recommended: Pin to a specific version
|
||||
# https://github.com/FerretDB/FerretDB/pkgs/container/ferretdb
|
||||
image: ghcr.io/ferretdb/ferretdb
|
||||
labels:
|
||||
komodo.skip: # Prevent Komodo from stopping with StopAllContainers
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- postgres2
|
||||
logging:
|
||||
driver: ${COMPOSE_LOGGING_DRIVER:-local}
|
||||
# ports:
|
||||
# - 27017:27017
|
||||
volumes:
|
||||
- ferretdb-state:/state
|
||||
environment:
|
||||
FERRETDB_POSTGRESQL_URL: postgres://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@postgres2:5432/postgres
|
||||
|
||||
...(unchanged)
|
||||
|
||||
volumes:
|
||||
...(unchanged)
|
||||
postgres-data:
|
||||
ferretdb-state:
|
||||
```
|
||||
|
||||
### **Step 2**: *Add* the database copy utility to Komodo compose file.
|
||||
|
||||
The SOURCE_URI points to the existing database, ie the old FerretDB v1, and it depends
|
||||
on whether it was deployed using Postgres or Sqlite. The example below uses the Postgres one,
|
||||
but if you use Sqlite it should just be something like `mongodb://ferretdb:27017`.
|
||||
|
||||
```yaml
|
||||
## In Komodo compose.yaml
|
||||
services:
|
||||
...(new database)
|
||||
|
||||
copy_database:
|
||||
image: ghcr.io/moghtech/komodo-util
|
||||
environment:
|
||||
MODE: CopyDatabase
|
||||
SOURCE_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@ferretdb:27017/${KOMODO_DATABASE_DB_NAME:-komodo}?authMechanism=PLAIN
|
||||
SOURCE_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
TARGET_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@ferretdb2:27017
|
||||
TARGET_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
|
||||
...(unchanged)
|
||||
```
|
||||
|
||||
### **Step 3**: *Compose Up* the new additions
|
||||
|
||||
Run `docker compose -p komodo --env-file compose.env -f xxxxx.compose.yaml up -d`, filling in the name of your compose.yaml.
|
||||
This will start up both the old and new database, and copy the data to the new one.
|
||||
|
||||
Wait a few moments for the `copy_database` service to finish. When it exits,
|
||||
confirm the logs show the data was moved successfully, and move on to the next step.
|
||||
|
||||
### **Step 4**: Point Komodo Core to the new database
|
||||
|
||||
In your Komodo compose.yaml, first *comment out* the `copy_database` service and old ferretdb v1 service/s.
|
||||
Then update the `core` service environment to point to `ferretdb2`.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
...
|
||||
|
||||
core:
|
||||
...(unchanged)
|
||||
environment:
|
||||
KOMODO_DATABASE_ADDRESS: ferretdb2:27017
|
||||
KOMODO_DATABASE_USERNAME: ${KOMODO_DB_USERNAME}
|
||||
KOMODO_DATABASE_PASSWORD: ${KOMODO_DB_PASSWORD}
|
||||
```
|
||||
|
||||
### **Step 5**: Final *Compose Up*
|
||||
|
||||
Repeat the same `docker compose` command as before to apply the changes, and then try navigating to your Komodo web page.
|
||||
If it works, congrats, **you are done**. You can clean up the compose file if you would like, removing the old volumes etc.
|
||||
|
||||
If it does not work, check the logs for any obvious issues, and if necessary you can undo the previous steps
|
||||
to go back to using the previous database.
|
||||
27
bin/util/multi-arch.Dockerfile
Normal file
27
bin/util/multi-arch.Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
## Assumes the latest binaries for x86_64 and aarch64 are already built (by binaries.Dockerfile).
|
||||
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
|
||||
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
|
||||
|
||||
# This is required to work with COPY --from
|
||||
FROM ${X86_64_BINARIES} AS x86_64
|
||||
FROM ${AARCH64_BINARIES} AS aarch64
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
## Copy both binaries initially, but only keep appropriate one for the TARGETPLATFORM.
|
||||
COPY --from=x86_64 /util /app/arch/linux/amd64
|
||||
COPY --from=aarch64 /util /app/arch/linux/arm64
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN mv /app/arch/${TARGETPLATFORM} /usr/local/bin/util && rm -r /app/arch
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/moghtech/komodo
|
||||
LABEL org.opencontainers.image.description="Komodo Util"
|
||||
LABEL org.opencontainers.image.licenses=GPL-3.0
|
||||
|
||||
CMD [ "util" ]
|
||||
16
bin/util/single-arch.Dockerfile
Normal file
16
bin/util/single-arch.Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
|
||||
# This is required to work with COPY --from
|
||||
FROM ${BINARIES_IMAGE} AS binaries
|
||||
|
||||
FROM gcr.io/distroless/cc
|
||||
|
||||
COPY --from=binaries /util /usr/local/bin/util
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/moghtech/komodo
|
||||
LABEL org.opencontainers.image.description="Komodo Util"
|
||||
LABEL org.opencontainers.image.licenses=GPL-3.0
|
||||
|
||||
CMD [ "util" ]
|
||||
131
bin/util/src/copy_database.rs
Normal file
131
bin/util/src/copy_database.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
use futures_util::{TryStreamExt, future::join_all};
|
||||
use mungos::{
|
||||
init::MongoBuilder,
|
||||
mongodb::{
|
||||
bson::{Document, RawDocumentBuf},
|
||||
options::InsertManyOptions,
|
||||
},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Env {
|
||||
/// Provide the source mongo uri to copy from
|
||||
source_uri: String,
|
||||
/// Provide the source db name to copy from.
|
||||
/// Default: komodo
|
||||
#[serde(default = "default_db_name")]
|
||||
source_db_name: String,
|
||||
/// Provide the source mongo uri to copy to
|
||||
target_uri: String,
|
||||
/// Provide the target db name to copy to.
|
||||
/// Default: komodo
|
||||
#[serde(default = "default_db_name")]
|
||||
target_db_name: String,
|
||||
/// Give the target database some time to initialize.
|
||||
#[serde(default = "default_startup_sleep_seconds")]
|
||||
startup_sleep_seconds: u64,
|
||||
}
|
||||
|
||||
fn default_db_name() -> String {
|
||||
String::from("komodo")
|
||||
}
|
||||
|
||||
fn default_startup_sleep_seconds() -> u64 {
|
||||
5
|
||||
}
|
||||
|
||||
pub async fn main() -> anyhow::Result<()> {
|
||||
let env = envy::from_env::<Env>()?;
|
||||
|
||||
info!("Sleeping for {} seconds...", env.startup_sleep_seconds);
|
||||
tokio::time::sleep(Duration::from_secs(env.startup_sleep_seconds))
|
||||
.await;
|
||||
|
||||
info!("Copying database...");
|
||||
|
||||
let source_db = MongoBuilder::default()
|
||||
.uri(env.source_uri)
|
||||
.build()
|
||||
.await
|
||||
.context("Invalid SOURCE_URI")?
|
||||
.database(&env.source_db_name);
|
||||
let target_db = MongoBuilder::default()
|
||||
.uri(env.target_uri)
|
||||
.build()
|
||||
.await
|
||||
.context("Invalid SOURCE_URI")?
|
||||
.database(&env.target_db_name);
|
||||
|
||||
let mut handles = Vec::new();
|
||||
|
||||
for collection in source_db
|
||||
.list_collection_names()
|
||||
.await
|
||||
.context("Failed to list collections on source db")?
|
||||
{
|
||||
let source = source_db.collection::<RawDocumentBuf>(&collection);
|
||||
let target = target_db.collection::<RawDocumentBuf>(&collection);
|
||||
|
||||
handles.push(tokio::spawn(async move {
|
||||
let res = async {
|
||||
let mut buffer = Vec::<RawDocumentBuf>::new();
|
||||
let mut count = 0;
|
||||
let mut cursor = source
|
||||
.find(Document::new())
|
||||
.await
|
||||
.context("Failed to query source collection")?;
|
||||
while let Some(doc) = cursor
|
||||
.try_next()
|
||||
.await
|
||||
.context("Failed to get next document")?
|
||||
{
|
||||
count += 1;
|
||||
buffer.push(doc);
|
||||
if buffer.len() >= 20_000 {
|
||||
if let Err(e) = target
|
||||
.insert_many(&buffer)
|
||||
.with_options(
|
||||
InsertManyOptions::builder().ordered(false).build(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Failed to flush document batch in {collection} collection | {e:#}");
|
||||
};
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
if !buffer.is_empty() {
|
||||
target
|
||||
.insert_many(&buffer)
|
||||
.with_options(
|
||||
InsertManyOptions::builder().ordered(false).build(),
|
||||
)
|
||||
.await
|
||||
.context("Failed to flush documents")?;
|
||||
}
|
||||
anyhow::Ok(count)
|
||||
}
|
||||
.await;
|
||||
match res {
|
||||
Ok(count) => {
|
||||
if count > 0 {
|
||||
info!("Finished copying {collection} collection | Copied {count}");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to copy {collection} collection | {e:#}")
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
join_all(handles).await;
|
||||
|
||||
info!("Finished copying database ✅");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
42
bin/util/src/main.rs
Normal file
42
bin/util/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
mod copy_database;
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
enum Mode {
|
||||
#[default]
|
||||
CopyDatabase,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Env {
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
async fn app() -> anyhow::Result<()> {
|
||||
dotenvy::dotenv().ok();
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let env = envy::from_env::<Env>()?;
|
||||
|
||||
info!("Komodo Util version: v{}", env!("CARGO_PKG_VERSION"));
|
||||
info!("Mode: {:?}", env.mode);
|
||||
|
||||
match env.mode {
|
||||
Mode::CopyDatabase => copy_database::main().await,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let mut term_signal = tokio::signal::unix::signal(
|
||||
tokio::signal::unix::SignalKind::terminate(),
|
||||
)?;
|
||||
tokio::select! {
|
||||
res = tokio::spawn(app()) => res?,
|
||||
_ = term_signal.recv() => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ serde_json.workspace = true
|
||||
tokio-util.workspace = true
|
||||
thiserror.workspace = true
|
||||
typeshare.workspace = true
|
||||
indexmap.workspace = true
|
||||
futures.workspace = true
|
||||
reqwest.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::entities::{
|
||||
Deployment, DeploymentActionState, DeploymentListItem,
|
||||
DeploymentQuery, DeploymentState,
|
||||
},
|
||||
docker::container::{ContainerListItem, ContainerStats},
|
||||
docker::container::{Container, ContainerListItem, ContainerStats},
|
||||
update::Log,
|
||||
};
|
||||
|
||||
@@ -105,6 +105,26 @@ pub struct GetDeploymentContainerResponse {
|
||||
|
||||
//
|
||||
|
||||
/// Inspect the docker container associated with the Deployment.
|
||||
/// Response: [Container].
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoReadRequest)]
|
||||
#[response(InspectDeploymentContainerResponse)]
|
||||
#[error(serror::Error)]
|
||||
pub struct InspectDeploymentContainer {
|
||||
/// Id or name
|
||||
#[serde(alias = "id", alias = "name")]
|
||||
pub deployment: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type InspectDeploymentContainerResponse = Container;
|
||||
|
||||
//
|
||||
|
||||
/// Get the deployment log's tail, split by stdout/stderr.
|
||||
/// Response: [Log].
|
||||
///
|
||||
|
||||
@@ -5,7 +5,7 @@ use typeshare::typeshare;
|
||||
|
||||
use crate::entities::{
|
||||
ResourceTarget,
|
||||
permission::{Permission, PermissionLevel, UserTarget},
|
||||
permission::{Permission, PermissionLevelAndSpecifics, UserTarget},
|
||||
};
|
||||
|
||||
use super::KomodoReadRequest;
|
||||
@@ -35,15 +35,15 @@ pub type ListPermissionsResponse = Vec<Permission>;
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoReadRequest)]
|
||||
#[response(GetPermissionLevelResponse)]
|
||||
#[response(GetPermissionResponse)]
|
||||
#[error(serror::Error)]
|
||||
pub struct GetPermissionLevel {
|
||||
pub struct GetPermission {
|
||||
/// The target to get user permission on.
|
||||
pub target: ResourceTarget,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type GetPermissionLevelResponse = PermissionLevel;
|
||||
pub type GetPermissionResponse = PermissionLevelAndSpecifics;
|
||||
|
||||
//
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use typeshare::typeshare;
|
||||
|
||||
use crate::entities::{
|
||||
SearchCombinator, U64,
|
||||
docker::container::Container,
|
||||
stack::{
|
||||
Stack, StackActionState, StackListItem, StackQuery, StackService,
|
||||
},
|
||||
@@ -53,6 +54,28 @@ pub type ListStackServicesResponse = Vec<StackService>;
|
||||
|
||||
//
|
||||
|
||||
/// Inspect the docker container associated with the Stack.
|
||||
/// Response: [Container].
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoReadRequest)]
|
||||
#[response(InspectStackContainerResponse)]
|
||||
#[error(serror::Error)]
|
||||
pub struct InspectStackContainer {
|
||||
/// Id or name
|
||||
#[serde(alias = "id", alias = "name")]
|
||||
pub stack: String,
|
||||
/// The service name to inspect
|
||||
pub service: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type InspectStackContainerResponse = Container;
|
||||
|
||||
//
|
||||
|
||||
/// Get a stack's logs. Filter down included services. Response: [GetStackLogResponse].
|
||||
///
|
||||
/// Note. This call will hit the underlying server directly for most up to date log.
|
||||
|
||||
@@ -28,6 +28,32 @@ pub struct ConnectContainerExecQuery {
|
||||
pub shell: String,
|
||||
}
|
||||
|
||||
/// Query to connect to a container exec session (interactive shell over websocket) on the given Deployment.
|
||||
/// This call will use access to the Deployment Terminal to permission the call.
|
||||
/// TODO: Document calling.
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ConnectDeploymentExecQuery {
|
||||
/// Deployment Id or name
|
||||
pub deployment: String,
|
||||
/// The shell to connect to
|
||||
pub shell: String,
|
||||
}
|
||||
|
||||
/// Query to connect to a container exec session (interactive shell over websocket) on the given Stack / service.
|
||||
/// This call will use access to the Stack Terminal to permission the call.
|
||||
/// TODO: Document calling.
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ConnectStackExecQuery {
|
||||
/// Stack Id or name
|
||||
pub stack: String,
|
||||
/// The service name to connect to
|
||||
pub service: String,
|
||||
/// The shell to connect to
|
||||
pub shell: String,
|
||||
}
|
||||
|
||||
/// Execute a terminal command on the given server.
|
||||
/// TODO: Document calling.
|
||||
#[typeshare]
|
||||
|
||||
@@ -5,7 +5,7 @@ use typeshare::typeshare;
|
||||
|
||||
use crate::entities::{
|
||||
NoData, ResourceTarget, ResourceTargetVariant,
|
||||
permission::{PermissionLevel, UserTarget},
|
||||
permission::{PermissionLevelAndSpecifics, UserTarget},
|
||||
};
|
||||
|
||||
use super::KomodoWriteRequest;
|
||||
@@ -25,7 +25,7 @@ pub struct UpdatePermissionOnTarget {
|
||||
/// Specify the target resource.
|
||||
pub resource_target: ResourceTarget,
|
||||
/// Specify the permission level.
|
||||
pub permission: PermissionLevel,
|
||||
pub permission: PermissionLevelAndSpecifics,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
@@ -48,7 +48,7 @@ pub struct UpdatePermissionOnResourceType {
|
||||
/// The resource type: eg. Server, Build, Deployment, etc.
|
||||
pub resource_type: ResourceTargetVariant,
|
||||
/// The base permission level.
|
||||
pub permission: PermissionLevel,
|
||||
pub permission: PermissionLevelAndSpecifics,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -88,7 +88,7 @@ pub struct RemoveUserFromUserGroup {
|
||||
|
||||
//
|
||||
|
||||
/// **Admin only.** Completely override the user in the group.
|
||||
/// **Admin only.** Completely override the users in the group.
|
||||
/// Response: [UserGroup]
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
@@ -103,3 +103,21 @@ pub struct SetUsersInUserGroup {
|
||||
/// The user ids or usernames to hard set as the group's users.
|
||||
pub users: Vec<String>,
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/// **Admin only.** Set `everyone` property of User Group.
|
||||
/// Response: [UserGroup]
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoWriteRequest)]
|
||||
#[response(UserGroup)]
|
||||
#[error(serror::Error)]
|
||||
pub struct SetEveryoneUserGroup {
|
||||
/// Id or name.
|
||||
pub user_group: String,
|
||||
/// Whether this user group applies to everyone.
|
||||
pub everyone: bool,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ mod environment;
|
||||
mod file_contents;
|
||||
mod labels;
|
||||
mod maybe_string_i64;
|
||||
mod permission;
|
||||
mod string_list;
|
||||
mod term_signal_labels;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user