mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-27 08:02:39 +00:00
feat: slack agents through a workspace's own slack app (#11912)
* feat: let Slack agents answer through a workspace's own Slack app, with signing secrets set from the UI The instance Slack app's signing secret can be set in its OAuth setting as well as SLACK_SIGNING_SECRET, and a workspace's own Slack app takes its own in the workspace settings. Each secret only vouches for the workspaces connected through its app, so /windmill keeps working for a workspace whose app has none set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep Slack signing secrets out of logs and verify an own app without a secret with the instance's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: report a workspace app's own signing secret in the Slack agents status Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: check Slack approval callbacks against the instance signing secret only An approval can be sent through any Slack resource, so the team's workspace connections do not tell which app signed its callback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: share the Slack app setup instructions and require the signing secret for agents Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: tidy the Slack signing secret copy and show a set secret as masked Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: check a workspace app only against its own signing secret, and approvals against their workspace's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: accept a Slack approval signed by the workspace's app or the instance's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: check Slack's signature on approvals only when sent through the workspace's Slack connection Any other Slack resource may belong to any Slack app, so its callbacks rest on the payload HMAC alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: address review findings on Slack signature checks and the signing secret field - reject Slack signatures older than five minutes, so a captured request cannot be replayed - let a reachable workspace's command script take a message before another workspace's help reply - remove a saved workspace signing secret from its field: the button reads Remove when it is empty - set the signing secret on connect only when one was entered - tell non-admins who can set the signing secret Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: keep SLACK_SIGNING_SECRET for workspace apps without a secret, and route a verified request only to its workspaces - a workspace on its own app with no secret of its own is checked against SLACK_SIGNING_SECRET, which may hold that app's secret from before workspaces had one; the instance setting's secret still never applies to it - once a secret verifies a request, only the workspaces it verified take it, command scripts included: it proves which app sent the request - compute the replay window with abs_diff, which cannot overflow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: update ee-repo-ref to c8d562aa9d75a7e99bf0ecd1ac224f532452a07e This commit updates the EE repository reference after PR #907 was merged in windmill-ee-private. Previous ee-repo-ref: 0864b0ab943defa8b8d55838ef09ab7dacfac17f New ee-repo-ref: c8d562aa9d75a7e99bf0ecd1ac224f532452a07e Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
windmill-internal-app[bot]
parent
9202a74071
commit
34de8bc06d
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT c.workspace_id, c.agent_path, c.is_default FROM slack_agent_channel c\n JOIN workspace_settings ws ON ws.workspace_id = c.workspace_id\n AND ws.slack_team_id = c.slack_team_id\n WHERE c.slack_team_id = $1 AND c.channel_id = $2 ORDER BY c.agent_path",
|
||||
"query": "SELECT c.workspace_id, c.agent_path, c.is_default FROM slack_agent_channel c\n JOIN workspace_settings ws ON ws.workspace_id = c.workspace_id\n AND ws.slack_team_id = c.slack_team_id\n WHERE c.slack_team_id = $1 AND c.channel_id = $2\n AND ws.workspace_id = ANY($3)\n ORDER BY c.agent_path",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -22,7 +22,8 @@
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
"Text",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
@@ -31,5 +32,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "44866e040f382b5e12aa21595bd1452aae8ca746348a6ac6b5bba26f5249166f"
|
||||
"hash": "19b9173fad68c2bde71a70d1ce947c20773dd4c44f19b47e73e24438f5c78ab3"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, slack_oauth_client_id IS NOT NULL AS \"own_app!\",\n slack_signing_secret\n FROM workspace_settings WHERE slack_team_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "own_app!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "slack_signing_secret",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "35238a186adbe0583010dbe8e298a907f16d9e88f648705f2372dfe7c0523879"
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT slack_oauth_client_id IS NOT NULL AS \"own_app!\",\n slack_signing_secret IS NOT NULL AS \"own_secret!\"\n FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "own_app!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "own_secret!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "68d95ce32653dece237bbf21459b571b8b67d26ffb60dd21e558dfa10612a0ed"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings\n SET slack_oauth_client_id = NULL, slack_oauth_client_secret = NULL\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "72076c1c210c57ef5db26620063c750c4a7eef6dcb2f4a91c7a3ddc2d367e3ba"
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT slack_oauth_client_id IS NOT NULL AS \"own_app!\", slack_signing_secret\n FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "own_app!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "slack_signing_secret",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "74e6d61f31897d8102fe75bbe44781a71aeba34da94b9b1aa9893c2837d5cb05"
|
||||
}
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, slack_command_script, slack_email FROM workspace_settings WHERE slack_team_id = $1 AND slack_command_script IS NOT NULL",
|
||||
"query": "SELECT workspace_id, slack_command_script, slack_email FROM workspace_settings\n WHERE slack_team_id = $1 AND slack_command_script IS NOT NULL\n AND workspace_id = ANY($2)\n ORDER BY workspace_id = ANY($3) DESC LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -21,7 +21,9 @@
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
"Text",
|
||||
"TextArray",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
@@ -30,5 +32,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b731208c3e98f8fb15bf0c72e2ab1d4c61566c3cedf31596efe084ef7dc9985a"
|
||||
"hash": "8b8b605d656d88e1fe77f936500cec9cb3388b7232a96bfc287f751d1e0092da"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT c.agent_path FROM slack_agent_channel c\n JOIN workspace_settings ws ON ws.workspace_id = c.workspace_id\n AND ws.slack_team_id = c.slack_team_id\n WHERE c.slack_team_id = $1 AND c.channel_id = $2 ORDER BY c.agent_path LIMIT 11",
|
||||
"query": "SELECT c.agent_path FROM slack_agent_channel c\n JOIN workspace_settings ws ON ws.workspace_id = c.workspace_id\n AND ws.slack_team_id = c.slack_team_id\n WHERE c.slack_team_id = $1 AND c.channel_id = $2\n AND ws.workspace_id = ANY($3)\n ORDER BY c.agent_path LIMIT 11",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -12,12 +12,13 @@
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
"Text",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "af9c259d71af873d81ad788ab05cd0b52f561bfaac0d4e8d17b1aa0900dcc3bc"
|
||||
"hash": "acda97a0d2ebb10942b1049cafe3fc9c997dd9427659ff15f6850be861000bdf"
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT slack_oauth_client_id, slack_oauth_client_secret, slack_signing_secret\n FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "slack_oauth_client_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "slack_oauth_client_secret",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "slack_signing_secret",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c01d2853ab35a404e0cce34c273c7ad58cd9add49f45fd50f4fcf909703d01a2"
|
||||
}
|
||||
+2
-4
@@ -1,16 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings\n SET slack_oauth_client_id = $1, slack_oauth_client_secret = $2\n WHERE workspace_id = $3",
|
||||
"query": "UPDATE workspace_settings\n SET slack_oauth_client_id = NULL, slack_oauth_client_secret = NULL,\n slack_signing_secret = NULL\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f830e934b43a83f857f682383f36ed56df6518e3035778e4e3b95182d2504e00"
|
||||
"hash": "cd829b96e6d8f4ed9a0283d9141ce8588ad36379991e71b10e49211891219a92"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings\n SET slack_oauth_client_id = $1, slack_oauth_client_secret = $2,\n slack_signing_secret = CASE WHEN slack_oauth_client_id = $1::varchar\n THEN slack_signing_secret END\n WHERE workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "dda346e3e9d2cab64532948de09cf6dea2537923a987be5584fbcb1c0dc69b7f"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, slack_command_script IS NOT NULL AS \"has_script!\"\n FROM workspace_settings WHERE slack_team_id = $1\n ORDER BY slack_command_script IS NOT NULL DESC LIMIT 2",
|
||||
"query": "SELECT workspace_id, slack_command_script IS NOT NULL AS \"has_script!\"\n FROM workspace_settings ws WHERE slack_team_id = $1\n AND ws.workspace_id = ANY($2)\n ORDER BY slack_command_script IS NOT NULL DESC LIMIT 2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -16,7 +16,8 @@
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
"Text",
|
||||
"TextArray"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
@@ -24,5 +25,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "f91c3e12549a3e037e8e784ad6ed439d762d17acb70793e9b26454572a156397"
|
||||
"hash": "e497205c8654cc0ba1b9c88aa1c253f5823cb10016330b317257c8c668d951c2"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET slack_signing_secret = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "eaed64e9c1b4b13ba97b394a9b17adac931366debe890df5b25e4110e47e4f87"
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_settings (workspace_id, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, teams_command_script, teams_team_id, teams_team_name, git_app_installations, git_credentials, ducklake, dbt_warehouses, slack_oauth_client_id, slack_oauth_client_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts, guest_access_enabled, guest_jwt_public_key, guest_jwt_jwks_url, add_admins_and_developers_to_forks, remote_deploy_target, remote_deploy_target_changed_at) SELECT $1, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, teams_command_script, teams_team_id, teams_team_name, git_app_installations, git_credentials, ducklake, dbt_warehouses, slack_oauth_client_id, slack_oauth_client_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts, guest_access_enabled, guest_jwt_public_key, guest_jwt_jwks_url, add_admins_and_developers_to_forks, remote_deploy_target, remote_deploy_target_changed_at FROM workspace_settings WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ee3ed82783b5b7d2f41f16a1d94f006687bd9de4611203b5dd0c28eacee02842"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO workspace_settings (workspace_id, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, role_overrides, teams_command_script, teams_team_id, teams_team_name, git_app_installations, git_credentials, ducklake, dbt_warehouses, slack_oauth_client_id, slack_oauth_client_secret, slack_signing_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts, guest_access_enabled, guest_jwt_public_key, guest_jwt_jwks_url, add_admins_and_developers_to_forks, remote_deploy_target, remote_deploy_target_changed_at) SELECT $1, slack_team_id, slack_name, slack_command_script, slack_email, customer_id, plan, webhook, ai_config, large_file_storage, git_sync, default_app, default_scripts, deploy_ui, mute_critical_alerts, color, operator_settings, role_overrides, teams_command_script, teams_team_id, teams_team_name, git_app_installations, git_credentials, ducklake, dbt_warehouses, slack_oauth_client_id, slack_oauth_client_secret, slack_signing_secret, datatable, teams_team_guid, auto_invite, error_handler, success_handler, public_app_execution_limit_per_minute, error_handler_fallback_to_instance_alerts, guest_access_enabled, guest_jwt_public_key, guest_jwt_jwks_url, add_admins_and_developers_to_forks, remote_deploy_target, remote_deploy_target_changed_at FROM workspace_settings WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f5022831ab610ab22ef1af1441753aba57d5e8f19a316f76e01922559425d81b"
|
||||
}
|
||||
Reference in New Issue
Block a user