From 4facf3ca3ee10e07a82668f8594372b9bd6d8b63 Mon Sep 17 00:00:00 2001 From: wendrul <53628737+wendrul@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:55:48 +0100 Subject: [PATCH] feat: Add a devops role to act as a "readonly admin" (#4775) * Prepare sqlx * Add devops role as bool * Add devops reloe and use it for certain endpoints * Critical alerts: complete devops role visiblity * Update ee repo ref * Prepare sqlx --- ...43061932dc5b4b17cf13d5cf8d833aedbe6d5.json | 2 +- ...9177a87e1accd192402e21db5ae09c3498ab0.json | 3 +- ...8c47886545796424f8612135b711e2b9ddb6c.json | 3 +- ...aec3609328b1e3fe6e4fac20fb6fe91ff1732.json | 22 -------- ...5801b58221898175c5d00dbdaaa085f769ac8.json | 22 ++++++++ ...98a03f751b246c40daf056fced0fd91f6dd73.json | 3 +- ...fc62bc93b0530efba09fc3c32a8f93eaabb1.json} | 14 +++-- ...3600939c65c75be8894322b90d9da18dcb824.json | 15 +++++ ...3cee096e37bdde7bb05c11ee0c4792f70dc60.json | 23 -------- ...4deb95a348dc804ee23a02698adb9aa32b375.json | 29 ---------- ...1218ac19ab2a4bc5f8b42deb2476f85d0deac.json | 3 +- ...7f5033b9c9afc344d9c3e385ba20a3ad2197a.json | 2 +- ...d841271bc08401573f4d1be8e8896cd77507b.json | 22 -------- ...62c3c267ca336a8b6bec5b29d4409030ed561.json | 3 +- ...94f91df7e588d4d2431bc85f4d8734920c8bf.json | 3 +- ...8b3ca6d7fa05600067f379659b59b6f2bf418.json | 3 +- ...eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json | 3 +- ...593b97b4e007fa9a499b49c3d1ebf9b645a3b.json | 35 ------------ ...36e65991b1d21a3306390fb1c7ca38babafe3.json | 3 +- ...f5bedefcaa15b7887402c0d5d33cb7b5fb362.json | 35 ------------ ...53ff66b37ed8110a0b67aedb9f25e6a7383e1.json | 3 +- ...f749fa3bbbf2c0e43d5d4ea148b5bc990067.json} | 18 ++++-- ...b016751c9b5a1dab632dc4abdb1dd6aa1fcbc.json | 23 -------- ...08b763293a27adf30d02df605317791d8126.json} | 16 ++++-- ...a2de1feecbfde74e9a1374b3668ec72d7bac1.json | 23 -------- ...1e273d25543401639a68f868e1e4fd8b0640a.json | 3 +- backend/ee-repo-ref.txt | 2 +- .../20241121105236_add_devops_role.down.sql | 2 + .../20241121105236_add_devops_role.up.sql | 2 + backend/windmill-api/openapi.yaml | 4 ++ backend/windmill-api/src/service_logs.rs | 11 ++-- backend/windmill-api/src/settings.rs | 7 +-- backend/windmill-api/src/users.rs | 21 +++++-- backend/windmill-api/src/utils.rs | 16 +++++- backend/windmill-api/src/workspaces.rs | 10 ++-- backend/windmill-common/src/auth.rs | 14 +++++ backend/windmill-common/src/utils.rs | 11 ++++ .../lib/components/SuperadminSettings.svelte | 46 +++++++++++---- .../sidebar/CriticalAlertModal.svelte | 6 +- .../sidebar/CriticalAlertModalInner.svelte | 56 +++++++++---------- .../components/sidebar/SidebarContent.svelte | 9 ++- frontend/src/lib/refreshUser.ts | 16 +++++- frontend/src/lib/stores.ts | 3 +- .../src/routes/(root)/(logged)/+layout.svelte | 7 ++- .../(root)/(logged)/service_logs/+page.svelte | 6 +- 45 files changed, 255 insertions(+), 328 deletions(-) delete mode 100644 backend/.sqlx/query-202a4fee4e70ff74d150a187a8daec3609328b1e3fe6e4fac20fb6fe91ff1732.json create mode 100644 backend/.sqlx/query-2c615128e28a3fb1ecb4b7a40f45801b58221898175c5d00dbdaaa085f769ac8.json rename backend/.sqlx/{query-2eec077cc9e27d7ccd160cbaac118c321c422705f79e69550bb60f377083bcef.json => query-3895cee539a24b4c6ea89fa7a835fc62bc93b0530efba09fc3c32a8f93eaabb1.json} (78%) create mode 100644 backend/.sqlx/query-411788111afccd826ce78b266153600939c65c75be8894322b90d9da18dcb824.json delete mode 100644 backend/.sqlx/query-4996b5af348bc335a5ec14496683cee096e37bdde7bb05c11ee0c4792f70dc60.json delete mode 100644 backend/.sqlx/query-4bbf4dbf5b18d8dfd62c0a029ec4deb95a348dc804ee23a02698adb9aa32b375.json delete mode 100644 backend/.sqlx/query-6673aeb02e1c39616c6f38fd2bad841271bc08401573f4d1be8e8896cd77507b.json delete mode 100644 backend/.sqlx/query-a1a339c9e744b5661d2b4d1354e593b97b4e007fa9a499b49c3d1ebf9b645a3b.json delete mode 100644 backend/.sqlx/query-c05f21e7027a985e5a657b689a0f5bedefcaa15b7887402c0d5d33cb7b5fb362.json rename backend/.sqlx/{query-2c14d3a88193f16ad3b8cd590749cb5537995f2499f6cb8f0f316fb62902d542.json => query-df5b933f81ca7e3bbb3fb522baedf749fa3bbbf2c0e43d5d4ea148b5bc990067.json} (75%) delete mode 100644 backend/.sqlx/query-e0e895527d6807699c918ca87e0b016751c9b5a1dab632dc4abdb1dd6aa1fcbc.json rename backend/.sqlx/{query-1a4d291c2f239f7b50c116594cebb031862e1a18ad9204e02a0194817db26d6a.json => query-f07a705df1a988827e099d146f5308b763293a27adf30d02df605317791d8126.json} (77%) delete mode 100644 backend/.sqlx/query-f5c94d89eb8c86916d7c64ffa8ea2de1feecbfde74e9a1374b3668ec72d7bac1.json create mode 100644 backend/migrations/20241121105236_add_devops_role.down.sql create mode 100644 backend/migrations/20241121105236_add_devops_role.up.sql diff --git a/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json b/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json index c8b5e3086f..043d236a6b 100644 --- a/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json +++ b/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "?column?", + "name": "bool", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json b/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json index 109f357f74..fc94db0309 100644 --- a/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json +++ b/backend/.sqlx/query-0ad36c1598ff4ece0c325eaeb9a9177a87e1accd192402e21db5ae09c3498ab0.json @@ -72,8 +72,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-1a612eb0b64eddd2c5657ef73598c47886545796424f8612135b711e2b9ddb6c.json b/backend/.sqlx/query-1a612eb0b64eddd2c5657ef73598c47886545796424f8612135b711e2b9ddb6c.json index a82d32768e..e74be644c7 100644 --- a/backend/.sqlx/query-1a612eb0b64eddd2c5657ef73598c47886545796424f8612135b711e2b9ddb6c.json +++ b/backend/.sqlx/query-1a612eb0b64eddd2c5657ef73598c47886545796424f8612135b711e2b9ddb6c.json @@ -52,8 +52,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-202a4fee4e70ff74d150a187a8daec3609328b1e3fe6e4fac20fb6fe91ff1732.json b/backend/.sqlx/query-202a4fee4e70ff74d150a187a8daec3609328b1e3fe6e4fac20fb6fe91ff1732.json deleted file mode 100644 index bab66359b6..0000000000 --- a/backend/.sqlx/query-202a4fee4e70ff74d150a187a8daec3609328b1e3fe6e4fac20fb6fe91ff1732.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM queue_view\n WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "202a4fee4e70ff74d150a187a8daec3609328b1e3fe6e4fac20fb6fe91ff1732" -} diff --git a/backend/.sqlx/query-2c615128e28a3fb1ecb4b7a40f45801b58221898175c5d00dbdaaa085f769ac8.json b/backend/.sqlx/query-2c615128e28a3fb1ecb4b7a40f45801b58221898175c5d00dbdaaa085f769ac8.json new file mode 100644 index 0000000000..6b352d3ff1 --- /dev/null +++ b/backend/.sqlx/query-2c615128e28a3fb1ecb4b7a40f45801b58221898175c5d00dbdaaa085f769ac8.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT devops FROM password WHERE email = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "devops", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "2c615128e28a3fb1ecb4b7a40f45801b58221898175c5d00dbdaaa085f769ac8" +} diff --git a/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json b/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json index 8a12aca84a..a23bb3827f 100644 --- a/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json +++ b/backend/.sqlx/query-337f31c2172194cd594042c561998a03f751b246c40daf056fced0fd91f6dd73.json @@ -58,8 +58,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-2eec077cc9e27d7ccd160cbaac118c321c422705f79e69550bb60f377083bcef.json b/backend/.sqlx/query-3895cee539a24b4c6ea89fa7a835fc62bc93b0530efba09fc3c32a8f93eaabb1.json similarity index 78% rename from backend/.sqlx/query-2eec077cc9e27d7ccd160cbaac118c321c422705f79e69550bb60f377083bcef.json rename to backend/.sqlx/query-3895cee539a24b4c6ea89fa7a835fc62bc93b0530efba09fc3c32a8f93eaabb1.json index f3eecb9006..66431239db 100644 --- a/backend/.sqlx/query-2eec077cc9e27d7ccd160cbaac118c321c422705f79e69550bb60f377083bcef.json +++ b/backend/.sqlx/query-3895cee539a24b4c6ea89fa7a835fc62bc93b0530efba09fc3c32a8f93eaabb1.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, name, company, username\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC\n LIMIT $1 OFFSET $2", + "query": "WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')),\n authors as (SELECT distinct email FROM usr WHERE usr.operator IS false)\n SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, devops, name, company, username\n FROM password\n WHERE email IN (SELECT email FROM active_users)\n ORDER BY super_admin DESC, devops DESC\n LIMIT $1 OFFSET $2", "describe": { "columns": [ { @@ -30,16 +30,21 @@ }, { "ordinal": 5, + "name": "devops", + "type_info": "Bool" + }, + { + "ordinal": 6, "name": "name", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 7, "name": "company", "type_info": "Varchar" }, { - "ordinal": 7, + "ordinal": 8, "name": "username", "type_info": "Varchar" } @@ -56,10 +61,11 @@ null, false, false, + false, true, true, true ] }, - "hash": "2eec077cc9e27d7ccd160cbaac118c321c422705f79e69550bb60f377083bcef" + "hash": "3895cee539a24b4c6ea89fa7a835fc62bc93b0530efba09fc3c32a8f93eaabb1" } diff --git a/backend/.sqlx/query-411788111afccd826ce78b266153600939c65c75be8894322b90d9da18dcb824.json b/backend/.sqlx/query-411788111afccd826ce78b266153600939c65c75be8894322b90d9da18dcb824.json new file mode 100644 index 0000000000..56edad4b2c --- /dev/null +++ b/backend/.sqlx/query-411788111afccd826ce78b266153600939c65c75be8894322b90d9da18dcb824.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE password SET devops = $1 WHERE email = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bool", + "Text" + ] + }, + "nullable": [] + }, + "hash": "411788111afccd826ce78b266153600939c65c75be8894322b90d9da18dcb824" +} diff --git a/backend/.sqlx/query-4996b5af348bc335a5ec14496683cee096e37bdde7bb05c11ee0c4792f70dc60.json b/backend/.sqlx/query-4996b5af348bc335a5ec14496683cee096e37bdde7bb05c11ee0c4792f70dc60.json deleted file mode 100644 index f21ea12278..0000000000 --- a/backend/.sqlx/query-4996b5af348bc335a5ec14496683cee096e37bdde7bb05c11ee0c4792f70dc60.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow AS \"raw_flow!: Json>\"\n FROM queue_view WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_flow!: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "4996b5af348bc335a5ec14496683cee096e37bdde7bb05c11ee0c4792f70dc60" -} diff --git a/backend/.sqlx/query-4bbf4dbf5b18d8dfd62c0a029ec4deb95a348dc804ee23a02698adb9aa32b375.json b/backend/.sqlx/query-4bbf4dbf5b18d8dfd62c0a029ec4deb95a348dc804ee23a02698adb9aa32b375.json deleted file mode 100644 index 8e540c3a90..0000000000 --- a/backend/.sqlx/query-4bbf4dbf5b18d8dfd62c0a029ec4deb95a348dc804ee23a02698adb9aa32b375.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT\n flow_status AS \"flow_status!: Json>\",\n raw_flow->'modules'->(flow_status->'step')::int AS \"module: Json>\"\n FROM queue_view WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "flow_status!: Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "module: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - null - ] - }, - "hash": "4bbf4dbf5b18d8dfd62c0a029ec4deb95a348dc804ee23a02698adb9aa32b375" -} diff --git a/backend/.sqlx/query-64ce3eabed6de7a4f99884640c21218ac19ab2a4bc5f8b42deb2476f85d0deac.json b/backend/.sqlx/query-64ce3eabed6de7a4f99884640c21218ac19ab2a4bc5f8b42deb2476f85d0deac.json index b35a48638a..6bc0fb8a10 100644 --- a/backend/.sqlx/query-64ce3eabed6de7a4f99884640c21218ac19ab2a4bc5f8b42deb2476f85d0deac.json +++ b/backend/.sqlx/query-64ce3eabed6de7a4f99884640c21218ac19ab2a4bc5f8b42deb2476f85d0deac.json @@ -57,8 +57,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json index 75b8108281..1fa370e682 100644 --- a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json +++ b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "?column?", + "name": "bool", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-6673aeb02e1c39616c6f38fd2bad841271bc08401573f4d1be8e8896cd77507b.json b/backend/.sqlx/query-6673aeb02e1c39616c6f38fd2bad841271bc08401573f4d1be8e8896cd77507b.json deleted file mode 100644 index c231cf880f..0000000000 --- a/backend/.sqlx/query-6673aeb02e1c39616c6f38fd2bad841271bc08401573f4d1be8e8896cd77507b.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow->'failure_module' != 'null'::jsonb\n FROM completed_job_view\n WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "6673aeb02e1c39616c6f38fd2bad841271bc08401573f4d1be8e8896cd77507b" -} diff --git a/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json b/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json index 36579b3062..5605518c9c 100644 --- a/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json +++ b/backend/.sqlx/query-6f4817fad2739a11d89b6704edf62c3c267ca336a8b6bec5b29d4409030ed561.json @@ -52,8 +52,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json index 7cbd85f23b..c4a2ff0a10 100644 --- a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json +++ b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json @@ -30,8 +30,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-9d25fbd21a63e6e9ccfdbf0460c8b3ca6d7fa05600067f379659b59b6f2bf418.json b/backend/.sqlx/query-9d25fbd21a63e6e9ccfdbf0460c8b3ca6d7fa05600067f379659b59b6f2bf418.json index b955a01fb6..4f8a78ca66 100644 --- a/backend/.sqlx/query-9d25fbd21a63e6e9ccfdbf0460c8b3ca6d7fa05600067f379659b59b6f2bf418.json +++ b/backend/.sqlx/query-9d25fbd21a63e6e9ccfdbf0460c8b3ca6d7fa05600067f379659b59b6f2bf418.json @@ -57,8 +57,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json b/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json index 3019cb0068..2393bc83df 100644 --- a/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json +++ b/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json @@ -52,8 +52,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-a1a339c9e744b5661d2b4d1354e593b97b4e007fa9a499b49c3d1ebf9b645a3b.json b/backend/.sqlx/query-a1a339c9e744b5661d2b4d1354e593b97b4e007fa9a499b49c3d1ebf9b645a3b.json deleted file mode 100644 index 8d4654b641..0000000000 --- a/backend/.sqlx/query-a1a339c9e744b5661d2b4d1354e593b97b4e007fa9a499b49c3d1ebf9b645a3b.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\"\n FROM queue_view WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_code", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "raw_lock", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "a1a339c9e744b5661d2b4d1354e593b97b4e007fa9a499b49c3d1ebf9b645a3b" -} diff --git a/backend/.sqlx/query-b49b1eaf58e62063c7a03039d0c36e65991b1d21a3306390fb1c7ca38babafe3.json b/backend/.sqlx/query-b49b1eaf58e62063c7a03039d0c36e65991b1d21a3306390fb1c7ca38babafe3.json index 8b3824a2a2..cc825ff56f 100644 --- a/backend/.sqlx/query-b49b1eaf58e62063c7a03039d0c36e65991b1d21a3306390fb1c7ca38babafe3.json +++ b/backend/.sqlx/query-b49b1eaf58e62063c7a03039d0c36e65991b1d21a3306390fb1c7ca38babafe3.json @@ -38,8 +38,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-c05f21e7027a985e5a657b689a0f5bedefcaa15b7887402c0d5d33cb7b5fb362.json b/backend/.sqlx/query-c05f21e7027a985e5a657b689a0f5bedefcaa15b7887402c0d5d33cb7b5fb362.json deleted file mode 100644 index d108bb7203..0000000000 --- a/backend/.sqlx/query-c05f21e7027a985e5a657b689a0f5bedefcaa15b7887402c0d5d33cb7b5fb362.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_code, raw_lock, raw_flow AS \"raw_flow: Json>\"\n FROM queue_view WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_code", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "raw_lock", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "raw_flow: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true, - true, - true - ] - }, - "hash": "c05f21e7027a985e5a657b689a0f5bedefcaa15b7887402c0d5d33cb7b5fb362" -} diff --git a/backend/.sqlx/query-d5a8614286c170e0d175903cd1b53ff66b37ed8110a0b67aedb9f25e6a7383e1.json b/backend/.sqlx/query-d5a8614286c170e0d175903cd1b53ff66b37ed8110a0b67aedb9f25e6a7383e1.json index fafd943f04..164157b02f 100644 --- a/backend/.sqlx/query-d5a8614286c170e0d175903cd1b53ff66b37ed8110a0b67aedb9f25e6a7383e1.json +++ b/backend/.sqlx/query-d5a8614286c170e0d175903cd1b53ff66b37ed8110a0b67aedb9f25e6a7383e1.json @@ -78,8 +78,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/.sqlx/query-2c14d3a88193f16ad3b8cd590749cb5537995f2499f6cb8f0f316fb62902d542.json b/backend/.sqlx/query-df5b933f81ca7e3bbb3fb522baedf749fa3bbbf2c0e43d5d4ea148b5bc990067.json similarity index 75% rename from backend/.sqlx/query-2c14d3a88193f16ad3b8cd590749cb5537995f2499f6cb8f0f316fb62902d542.json rename to backend/.sqlx/query-df5b933f81ca7e3bbb3fb522baedf749fa3bbbf2c0e43d5d4ea148b5bc990067.json index 93017f5aec..7918b97267 100644 --- a/backend/.sqlx/query-2c14d3a88193f16ad3b8cd590749cb5537995f2499f6cb8f0f316fb62902d542.json +++ b/backend/.sqlx/query-df5b933f81ca7e3bbb3fb522baedf749fa3bbbf2c0e43d5d4ea148b5bc990067.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT email, login_type::TEXT, super_admin, verified, name, company, username, NULL::bool as operator_only FROM password WHERE email = $1", + "query": "SELECT email, login_type::TEXT, super_admin, devops, verified, name, company, username, NULL::bool as operator_only FROM password WHERE email = $1", "describe": { "columns": [ { @@ -20,26 +20,31 @@ }, { "ordinal": 3, - "name": "verified", + "name": "devops", "type_info": "Bool" }, { "ordinal": 4, + "name": "verified", + "type_info": "Bool" + }, + { + "ordinal": 5, "name": "name", "type_info": "Varchar" }, { - "ordinal": 5, + "ordinal": 6, "name": "company", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 7, "name": "username", "type_info": "Varchar" }, { - "ordinal": 7, + "ordinal": 8, "name": "operator_only", "type_info": "Bool" } @@ -54,11 +59,12 @@ null, false, false, + false, true, true, true, null ] }, - "hash": "2c14d3a88193f16ad3b8cd590749cb5537995f2499f6cb8f0f316fb62902d542" + "hash": "df5b933f81ca7e3bbb3fb522baedf749fa3bbbf2c0e43d5d4ea148b5bc990067" } diff --git a/backend/.sqlx/query-e0e895527d6807699c918ca87e0b016751c9b5a1dab632dc4abdb1dd6aa1fcbc.json b/backend/.sqlx/query-e0e895527d6807699c918ca87e0b016751c9b5a1dab632dc4abdb1dd6aa1fcbc.json deleted file mode 100644 index 841a7e8402..0000000000 --- a/backend/.sqlx/query-e0e895527d6807699c918ca87e0b016751c9b5a1dab632dc4abdb1dd6aa1fcbc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow AS \"raw_flow!: Json>\"\n FROM completed_job_view WHERE id = $1 AND workspace_id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "raw_flow!: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "e0e895527d6807699c918ca87e0b016751c9b5a1dab632dc4abdb1dd6aa1fcbc" -} diff --git a/backend/.sqlx/query-1a4d291c2f239f7b50c116594cebb031862e1a18ad9204e02a0194817db26d6a.json b/backend/.sqlx/query-f07a705df1a988827e099d146f5308b763293a27adf30d02df605317791d8126.json similarity index 77% rename from backend/.sqlx/query-1a4d291c2f239f7b50c116594cebb031862e1a18ad9204e02a0194817db26d6a.json rename to backend/.sqlx/query-f07a705df1a988827e099d146f5308b763293a27adf30d02df605317791d8126.json index b8c5b89128..0153bbf579 100644 --- a/backend/.sqlx/query-1a4d291c2f239f7b50c116594cebb031862e1a18ad9204e02a0194817db26d6a.json +++ b/backend/.sqlx/query-f07a705df1a988827e099d146f5308b763293a27adf30d02df605317791d8126.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT email, login_type::text, verified, super_admin, name, company, username, NULL::bool as operator_only FROM password ORDER BY super_admin DESC, email LIMIT $1 OFFSET $2", + "query": "SELECT email, login_type::text, verified, super_admin, devops, name, company, username, NULL::bool as operator_only FROM password ORDER BY super_admin DESC, devops DESC, email LIMIT $1 OFFSET $2", "describe": { "columns": [ { @@ -25,21 +25,26 @@ }, { "ordinal": 4, + "name": "devops", + "type_info": "Bool" + }, + { + "ordinal": 5, "name": "name", "type_info": "Varchar" }, { - "ordinal": 5, + "ordinal": 6, "name": "company", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 7, "name": "username", "type_info": "Varchar" }, { - "ordinal": 7, + "ordinal": 8, "name": "operator_only", "type_info": "Bool" } @@ -55,11 +60,12 @@ null, false, false, + false, true, true, true, null ] }, - "hash": "1a4d291c2f239f7b50c116594cebb031862e1a18ad9204e02a0194817db26d6a" + "hash": "f07a705df1a988827e099d146f5308b763293a27adf30d02df605317791d8126" } diff --git a/backend/.sqlx/query-f5c94d89eb8c86916d7c64ffa8ea2de1feecbfde74e9a1374b3668ec72d7bac1.json b/backend/.sqlx/query-f5c94d89eb8c86916d7c64ffa8ea2de1feecbfde74e9a1374b3668ec72d7bac1.json deleted file mode 100644 index 53302e6616..0000000000 --- a/backend/.sqlx/query-f5c94d89eb8c86916d7c64ffa8ea2de1feecbfde74e9a1374b3668ec72d7bac1.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT raw_flow->'modules'->($1)::text->'value'->>'type' = 'flow' FROM queue_view WHERE id = $2 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid" - ] - }, - "nullable": [ - null - ] - }, - "hash": "f5c94d89eb8c86916d7c64ffa8ea2de1feecbfde74e9a1374b3668ec72d7bac1" -} diff --git a/backend/.sqlx/query-fc7ac95d8d5f4532a2b0e50f84c1e273d25543401639a68f868e1e4fd8b0640a.json b/backend/.sqlx/query-fc7ac95d8d5f4532a2b0e50f84c1e273d25543401639a68f868e1e4fd8b0640a.json index 8f459f96fd..5e4f522147 100644 --- a/backend/.sqlx/query-fc7ac95d8d5f4532a2b0e50f84c1e273d25543401639a68f868e1e4fd8b0640a.json +++ b/backend/.sqlx/query-fc7ac95d8d5f4532a2b0e50f84c1e273d25543401639a68f868e1e4fd8b0640a.json @@ -57,8 +57,7 @@ "php", "bunnative", "rust", - "ansible", - "csharp" + "ansible" ] } } diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index a25899362b..083da864f7 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -ce2e482a5fa4f5a8ae2300276256784da8d1cf26 +4a0e64ab860eb88ff0d21b8705e647c9b76c46ee diff --git a/backend/migrations/20241121105236_add_devops_role.down.sql b/backend/migrations/20241121105236_add_devops_role.down.sql new file mode 100644 index 0000000000..5b78cad56a --- /dev/null +++ b/backend/migrations/20241121105236_add_devops_role.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE password DROP COLUMN devops; diff --git a/backend/migrations/20241121105236_add_devops_role.up.sql b/backend/migrations/20241121105236_add_devops_role.up.sql new file mode 100644 index 0000000000..8b6040dfdb --- /dev/null +++ b/backend/migrations/20241121105236_add_devops_role.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE password ADD COLUMN devops BOOLEAN NOT NULL DEFAULT false; diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index d67337e195..25d4a72047 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -407,6 +407,8 @@ paths: properties: is_super_admin: type: boolean + is_devops: + type: boolean name: type: string responses: @@ -12580,6 +12582,8 @@ components: enum: ["password", "github"] super_admin: type: boolean + devops: + type: boolean verified: type: boolean name: diff --git a/backend/windmill-api/src/service_logs.rs b/backend/windmill-api/src/service_logs.rs index eb5b2f44f2..277a08bca9 100644 --- a/backend/windmill-api/src/service_logs.rs +++ b/backend/windmill-api/src/service_logs.rs @@ -6,7 +6,7 @@ * LICENSE-AGPL for a copy of the license. */ -use crate::utils::content_plain; +use crate::utils::{content_plain, require_devops_role}; use axum::{body::Body, extract::Query, response::Response, routing::get, Extension, Json, Router}; use serde::Serialize; @@ -15,10 +15,7 @@ use windmill_common::{ utils::Pagination, }; -use crate::{ - db::{ApiAuthed, DB}, - utils::require_super_admin, -}; +use crate::db::{ApiAuthed, DB}; pub fn global_service() -> Router { Router::new() @@ -51,7 +48,7 @@ async fn list_files( Query(pagination): Query, Query(lq): Query, ) -> JsonResult> { - require_super_admin(&db, &email).await?; + require_devops_role(&db, &email).await?; let (per_page, offset) = windmill_common::utils::paginate(pagination); let mut sqlb = sql_builder::SqlBuilder::select_from("log_file") @@ -98,7 +95,7 @@ async fn get_log_file( ) -> windmill_common::error::Result { use windmill_common::tracing_init::TMP_WINDMILL_LOGS_SERVICE; - require_super_admin(&db, &email).await?; + require_devops_role(&db, &email).await?; let path = path.to_path(); #[cfg(feature = "parquet")] let s3_client = windmill_common::s3_helpers::OBJECT_STORE_CACHE_SETTINGS diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index 673acb132c..7081d124b7 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -11,7 +11,7 @@ use std::time::Duration; use crate::{ db::{ApiAuthed, DB}, ee::validate_license_key, - utils::{generate_instance_username_for_all_users, require_super_admin}, + utils::{require_devops_role, generate_instance_username_for_all_users, require_super_admin}, HTTP_CLIENT, }; @@ -440,7 +440,7 @@ pub async fn get_critical_alerts( authed: ApiAuthed, Query(params): Query, ) -> JsonResult> { - require_super_admin(&db, &authed.email).await?; + require_devops_role(&db, &authed.email).await?; crate::utils::get_critical_alerts(db, params, None).await } @@ -456,8 +456,7 @@ pub async fn acknowledge_critical_alert( authed: ApiAuthed, Path(id): Path, ) -> error::Result { - require_super_admin(&db, &authed.email).await?; - + require_devops_role(&db, &authed.email).await?; crate::utils::acknowledge_critical_alert(db, None, id).await } diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 6d0f0b4ad1..bd3a2d4411 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -796,6 +796,7 @@ pub struct GlobalUserInfo { email: String, login_type: Option, super_admin: bool, + devops: bool, verified: bool, name: Option, company: Option, @@ -853,6 +854,7 @@ pub struct DeclineInvite { #[derive(Deserialize)] pub struct EditUser { pub is_super_admin: Option, + pub is_devops: Option, pub name: Option, } @@ -1042,10 +1044,10 @@ async fn list_users_as_super_admin( GlobalUserInfo, "WITH active_users AS (SELECT distinct username as email FROM audit WHERE timestamp > NOW() - INTERVAL '1 month' AND (operation = 'users.login' OR operation = 'oauth.login')), authors as (SELECT distinct email FROM usr WHERE usr.operator IS false) - SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, name, company, username + SELECT email, email NOT IN (SELECT email FROM authors) as operator_only, login_type::text, verified, super_admin, devops, name, company, username FROM password WHERE email IN (SELECT email FROM active_users) - ORDER BY super_admin DESC + ORDER BY super_admin DESC, devops DESC LIMIT $1 OFFSET $2", per_page as i32, offset as i32 @@ -1055,7 +1057,7 @@ async fn list_users_as_super_admin( } else { sqlx::query_as!( GlobalUserInfo, - "SELECT email, login_type::text, verified, super_admin, name, company, username, NULL::bool as operator_only FROM password ORDER BY super_admin DESC, email LIMIT \ + "SELECT email, login_type::text, verified, super_admin, devops, name, company, username, NULL::bool as operator_only FROM password ORDER BY super_admin DESC, devops DESC, email LIMIT \ $1 OFFSET $2", per_page as i32, offset as i32 @@ -1218,7 +1220,7 @@ async fn global_whoami( ) -> JsonResult { let user = sqlx::query_as!( GlobalUserInfo, - "SELECT email, login_type::TEXT, super_admin, verified, name, company, username, NULL::bool as operator_only FROM password WHERE \ + "SELECT email, login_type::TEXT, super_admin, devops, verified, name, company, username, NULL::bool as operator_only FROM password WHERE \ email = $1", email ) @@ -1233,6 +1235,7 @@ async fn global_whoami( email: email.clone(), login_type: Some("superadmin_secret".to_string()), super_admin: true, + devops: false, verified: true, name: None, company: None, @@ -1876,6 +1879,16 @@ async fn update_user( .await?; } + if let Some(dv) = eu.is_devops { + sqlx::query_scalar!( + "UPDATE password SET devops = $1 WHERE email = $2", + dv, + &email_to_update + ) + .execute(&mut *tx) + .await?; + } + if let Some(n) = eu.name { sqlx::query_scalar!( "UPDATE password SET name = $1 WHERE email = $2", diff --git a/backend/windmill-api/src/utils.rs b/backend/windmill-api/src/utils.rs index 4302189c4e..cc6360b994 100644 --- a/backend/windmill-api/src/utils.rs +++ b/backend/windmill-api/src/utils.rs @@ -11,7 +11,7 @@ use regex::Regex; use serde::Deserialize; use sqlx::{Postgres, Transaction}; use windmill_common::{ - auth::is_super_admin_email, + auth::{is_devops_email, is_super_admin_email}, error::{self, Error}, DB, }; @@ -32,13 +32,25 @@ pub async fn require_super_admin(db: &DB, email: &str) -> error::Result<()> { if !is_admin { Err(Error::NotAuthorized( - "This endpoint require caller to be a super admin".to_owned(), + "This endpoint requires the caller to be a super admin".to_owned(), )) } else { Ok(()) } } +pub async fn require_devops_role(db: &DB, email: &str) -> error::Result<()> { + let is_devops = is_devops_email(db, email).await?; + + if is_devops { + Ok(()) + } else { + Err(Error::NotAuthorized( + "This endpoint requires the caller to have the `devops` role".to_string(), + )) + } +} + lazy_static::lazy_static! { pub static ref INVALID_USERNAME_CHARS: Regex = Regex::new(r"[^A-Za-z0-9_]").unwrap(); } diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 0fc98863d7..b561c671f7 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -54,7 +54,7 @@ use windmill_common::{ global_settings::AUTOMATE_USERNAME_CREATION_SETTING, oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH, scripts::{Schema, Script, ScriptLang}, - utils::{paginate, rd_string, require_admin, Pagination}, + utils::{paginate, rd_string, require_admin, require_admin_or_devops, Pagination}, variables::ExportableListableVariable, }; use windmill_git_sync::handle_deployment_metadata; @@ -3092,7 +3092,7 @@ pub async fn get_critical_alerts( authed: ApiAuthed, Query(params): Query, ) -> JsonResult> { - require_admin(authed.is_admin, &authed.username)?; + require_admin_or_devops(authed.is_admin, &authed.username, &authed.email, &db).await?; crate::utils::get_critical_alerts(db, params, Some(w_id)).await } @@ -3108,8 +3108,8 @@ pub async fn acknowledge_critical_alert( Path((w_id, id)): Path<(String, i32)>, authed: ApiAuthed, ) -> Result { - require_admin(authed.is_admin, &authed.username)?; - crate::utils::acknowledge_critical_alert(db, Some(w_id), id).await + require_admin_or_devops(authed.is_admin, &authed.username, &authed.email, &db).await?; + crate::utils::acknowledge_critical_alert(db, Some(w_id), id).await } #[cfg(not(feature = "enterprise"))] @@ -3173,4 +3173,4 @@ async fn mute_critical_alerts( #[cfg(not(feature = "enterprise"))] pub async fn mute_critical_alerts() -> Error { Error::NotFound("Critical Alerts require EE".to_string()) -} \ No newline at end of file +} diff --git a/backend/windmill-common/src/auth.rs b/backend/windmill-common/src/auth.rs index 22cdd4d52a..fcd528e9b0 100644 --- a/backend/windmill-common/src/auth.rs +++ b/backend/windmill-common/src/auth.rs @@ -73,6 +73,20 @@ pub async fn is_super_admin_email(db: &DB, email: &str) -> Result { Ok(is_admin) } +pub async fn is_devops_email(db: &DB, email: &str) -> Result { + if is_super_admin_email(db, email).await? { + return Ok(true) + } + + let is_devops = sqlx::query_scalar!("SELECT devops FROM password WHERE email = $1", email) + .fetch_optional(db) + .await + .map_err(|e| Error::InternalErr(format!("fetching super admin: {e:#}")))? + .unwrap_or(false); + + Ok(is_devops) +} + pub fn permissioned_as_to_username(permissioned_as: &str) -> String { if let Some((prefix, name)) = permissioned_as.split_once('/') { if prefix == "u" { diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index d8a3daf50e..08a0c32e3b 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -6,6 +6,7 @@ * LICENSE-AGPL for a copy of the license. */ +use crate::auth::is_devops_email; use crate::ee::LICENSE_KEY_ID; #[cfg(feature = "enterprise")] use crate::ee::{send_critical_alert, CriticalAlertKind}; @@ -77,6 +78,16 @@ pub fn require_admin(is_admin: bool, username: &str) -> Result<()> { } } +pub async fn require_admin_or_devops(is_admin: bool, username: &str, email: &str, db: &DB) -> Result<()> { + if !is_admin { + if !is_devops_email(db, email).await? { + return Err(Error::RequireAdmin(username.to_string())); + } + } + Ok(()) + +} + pub fn hostname() -> String { std::env::var("FORCE_HOSTNAME").unwrap_or_else(|_| { gethostname() diff --git a/frontend/src/lib/components/SuperadminSettings.svelte b/frontend/src/lib/components/SuperadminSettings.svelte index f5e88116f6..2bbc2396f0 100644 --- a/frontend/src/lib/components/SuperadminSettings.svelte +++ b/frontend/src/lib/components/SuperadminSettings.svelte @@ -217,7 +217,7 @@ {#if filteredUsers && users} - {#each filteredUsers.slice(0, nbDisplayed) as { email, super_admin, login_type, name, username, operator_only } (email)} + {#each filteredUsers.slice(0, nbDisplayed) as { email, super_admin, devops, login_type, name, username, operator_only } (email)} {email} {login_type} @@ -252,25 +252,51 @@ {/if} { if (email == $userStore?.email) { sendUserToast('You cannot demote yourself', true) listUsers(activeOnly) return } - await UserService.globalUserUpdate({ - email, - requestBody: { - is_super_admin: !super_admin - } - }) + + let role = e.detail; + + if (role === "super_admin") { + await UserService.globalUserUpdate({ + email, + requestBody: { + is_super_admin: true, + is_devops: false + } + }) + } + if (role === "devops") { + await UserService.globalUserUpdate({ + email, + requestBody: { + is_super_admin: false, + is_devops: true + } + }) + + } + if (role === "user") { + await UserService.globalUserUpdate({ + email, + requestBody: { + is_super_admin: false, + is_devops: false + } + }) + } sendUserToast('User updated') listUsers(activeOnly) }} > - - + + + diff --git a/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte b/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte index 249deecccf..d6d84c39bd 100644 --- a/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte +++ b/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte @@ -3,7 +3,7 @@ import CriticalAlertModalInner from './CriticalAlertModalInner.svelte' import { SettingService } from '$lib/gen' import { sendUserToast } from '$lib/toast' - import { superadmin, workspaceStore, isCriticalAlertsUIOpen } from '$lib/stores' + import { workspaceStore, isCriticalAlertsUIOpen, devopsRole } from '$lib/stores' import Modal from '../common/modal/Modal.svelte' export let open: boolean = false @@ -40,7 +40,7 @@ const withSuperadminLogic = (superadminFunction, workspaceFunction) => { return async (params = {}) => { - if (!$superadmin || workspaceContext) { + if (!$devopsRole || workspaceContext) { return workspaceFunction({ ...params, workspace: $workspaceStore @@ -81,7 +81,7 @@ numUnacknowledgedCriticalAlerts === 0 && unacknowledged.length > 0 && sendToast && - (($superadmin && !muteSettings.global) || (!$superadmin && !muteSettings.workspace)) + (($devopsRole && !muteSettings.global) || (!$devopsRole && !muteSettings.workspace)) ) { sendUserToast( 'Critical Alert:', diff --git a/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte b/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte index 5b9a53992c..c935f82944 100644 --- a/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte +++ b/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte @@ -2,18 +2,11 @@ import Button from '../common/button/Button.svelte' import Toggle from '$lib/components/Toggle.svelte' import { SettingService } from '$lib/gen' - import { - CheckCircle2, - AlertCircle, - RefreshCw, - CheckSquare2, - AlertTriangle, - } from 'lucide-svelte' + import { CheckCircle2, AlertCircle, RefreshCw, CheckSquare2, AlertTriangle } from 'lucide-svelte' import type { CriticalAlert } from '$lib/gen' import { onMount } from 'svelte' - import { instanceSettingsSelectedTab } from '$lib/stores' + import { devopsRole, workspaceStore, instanceSettingsSelectedTab, superadmin, userStore } from '$lib/stores' import { goto } from '$app/navigation' - import { superadmin, workspaceStore } from '$lib/stores' import { sendUserToast } from '$lib/toast' import Section from '$lib/components/Section.svelte' @@ -35,7 +28,10 @@ $: muteSettings $: { - if (initialMuteSettings.workspace !== muteSettings.workspace || initialMuteSettings.global !== muteSettings.global) { + if ( + initialMuteSettings.workspace !== muteSettings.workspace || + initialMuteSettings.global !== muteSettings.global + ) { saveMuteSettings() } } @@ -199,7 +195,7 @@ > - {#if $superadmin} + {#if $devopsRole}
{/if} -
- {#if $superadmin} + {#if $superadmin || $userStore?.is_admin} +
+ {#if $superadmin} +
+ +
+ {/if} +
- {/if} - -
- -
-
+
+ {/if}
@@ -261,7 +255,7 @@ Type Message Created At - {#if $superadmin} + {#if $devopsRole} Workspace {/if} Acknowledge @@ -285,7 +279,7 @@ {message} {formatDate(created_at)} - {#if $superadmin} + {#if $devopsRole} {workspace_id ? workspace_id : 'global'} {/if} diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index 3ed359df87..29678f02bd 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -6,7 +6,10 @@ userStore, workspaceStore, isCriticalAlertsUIOpen, - enterpriseLicense + enterpriseLicense, + + devopsRole + } from '$lib/stores' import { SIDEBAR_SHOW_SCHEDULES } from '$lib/consts' import { @@ -187,7 +190,7 @@ ], disabled: $userStore?.operator }, - $superadmin || $userStore?.is_admin + $devopsRole || $userStore?.is_admin ? { label: 'Logs', icon: Logs, @@ -197,7 +200,7 @@ href: `${base}/audit_logs`, icon: Eye }, - ...($superadmin + ...($devopsRole ? [ { label: 'Service Logs', diff --git a/frontend/src/lib/refreshUser.ts b/frontend/src/lib/refreshUser.ts index cb6c2b5a95..1023f65ab7 100644 --- a/frontend/src/lib/refreshUser.ts +++ b/frontend/src/lib/refreshUser.ts @@ -1,6 +1,6 @@ import { get } from 'svelte/store' import { UserService } from '$lib/gen' -import { superadmin } from './stores.js' +import { superadmin, devopsRole } from './stores.js' import { goto } from '$lib/navigation' export async function refreshSuperadmin(): Promise { @@ -17,4 +17,18 @@ export async function refreshSuperadmin(): Promise { goto('/user/logout') } } + + if (get(devopsRole) == undefined) { + try { + const me = await UserService.globalWhoami() + if (me.devops || me.super_admin) { + devopsRole.set(me.email) + } else { + devopsRole.set(false) + } + } catch { + devopsRole.set(false) + goto('/user/logout') + } + } } diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts index b4569d1901..5ec0265d2f 100644 --- a/frontend/src/lib/stores.ts +++ b/frontend/src/lib/stores.ts @@ -46,6 +46,7 @@ export const isPremiumStore = writable(false) export const starStore = writable(1) export const usersWorkspaceStore = writable(undefined) export const superadmin = writable(undefined) +export const devopsRole = writable(undefined) export const lspTokenStore = writable(undefined) export const hubBaseUrlStore = writable('https://hub.windmill.dev') export const userWorkspaces: Readable< @@ -126,4 +127,4 @@ export const dbSchemas = writable({}) export const instanceSettingsSelectedTab = writable('Core') -export const isCriticalAlertsUIOpen = writable(false) \ No newline at end of file +export const isCriticalAlertsUIOpen = writable(false) diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index fa60e2065d..b82ddb35ce 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -28,7 +28,10 @@ type UserExt, defaultScripts, hubBaseUrlStore, - usedTriggerKinds + usedTriggerKinds, + + devopsRole + } from '$lib/stores' import CenteredModal from '$lib/components/CenteredModal.svelte' import { afterNavigate, beforeNavigate } from '$app/navigation' @@ -286,7 +289,7 @@ setContext('openSearchWithPrefilledText', openSearchModal) $: { - if ($enterpriseLicense && $workspaceStore && $userStore && ($superadmin || $userStore.is_admin)) { + if ($enterpriseLicense && $workspaceStore && $userStore && ($devopsRole || $userStore.is_admin)) { mountModal = true loadCriticalAlertsMuted() } diff --git a/frontend/src/routes/(root)/(logged)/service_logs/+page.svelte b/frontend/src/routes/(root)/(logged)/service_logs/+page.svelte index 9989f8c861..d378026430 100644 --- a/frontend/src/routes/(root)/(logged)/service_logs/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/service_logs/+page.svelte @@ -4,7 +4,7 @@ import Popover from '$lib/components/Popover.svelte' import ServiceLogsInner from '$lib/components/ServiceLogsInner.svelte' import Tooltip from '$lib/components/Tooltip.svelte' - import { superadmin } from '$lib/stores' + import { devopsRole } from '$lib/stores' import { Search, AlertTriangle } from 'lucide-svelte' let searchTerm = $page.url.searchParams.get('query') ?? '' @@ -23,9 +23,9 @@
- {#if !$superadmin} + {#if !$devopsRole} - Service logs are only available to superadmins + Service logs are only available to superadmins (or devops) {:else}