From 59f531e967f3862d783f7815cfeeae5594c4fae4 Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Wed, 11 Dec 2024 08:54:00 -0300 Subject: [PATCH 1/3] chore: update public API docs authentication and perms (#5349) Related to https://github.com/grafana/oncall-private/issues/2826 --- docs/sources/oncall-api-reference/_index.md | 15 ++++++++++++++- .../oncall-api-reference/alertgroups.md | 18 ++++++++++++++++++ docs/sources/oncall-api-reference/alerts.md | 2 ++ .../sources/oncall-api-reference/escalation.md | 2 ++ .../oncall-api-reference/escalation_chains.md | 8 ++++++++ .../escalation_policies.md | 10 ++++++++++ .../oncall-api-reference/integrations.md | 10 ++++++++++ .../oncall-api-reference/on_call_shifts.md | 10 ++++++++++ .../oncall-api-reference/organizations.md | 4 ++++ .../oncall-api-reference/outgoing_webhooks.md | 10 ++++++++++ .../personal_notification_rules.md | 8 ++++++++ .../oncall-api-reference/resolution_notes.md | 10 ++++++++++ docs/sources/oncall-api-reference/routes.md | 10 ++++++++++ docs/sources/oncall-api-reference/schedules.md | 12 ++++++++++++ .../oncall-api-reference/shift_swaps.md | 12 ++++++++++++ .../oncall-api-reference/slack_channels.md | 2 ++ docs/sources/oncall-api-reference/teams.md | 4 ++++ .../oncall-api-reference/user_groups.md | 2 ++ docs/sources/oncall-api-reference/users.md | 4 ++++ 19 files changed, 152 insertions(+), 1 deletion(-) diff --git a/docs/sources/oncall-api-reference/_index.md b/docs/sources/oncall-api-reference/_index.md index 59b747b681..62c1ee7364 100644 --- a/docs/sources/oncall-api-reference/_index.md +++ b/docs/sources/oncall-api-reference/_index.md @@ -29,7 +29,7 @@ To authorize, use the **Authorization** header: ```shell # With shell, you can just pass the correct header with each request -curl "api_endpoint_here" --header "Authorization: "api_key_here"" +curl "api_endpoint_here" --header "Authorization: " ``` Grafana OnCall uses API keys to allow access to the API. You can request a new OnCall API key in OnCall -> Settings page. @@ -39,6 +39,19 @@ request a different API key. The endpoint refers to the OnCall Application endpoint and can be found on the OnCall -> Settings page as well. +### Authentication using Service Account tokens + +It is also possible to use a [service account token](https://grafana.com/docs/grafana/latest/administration/service-accounts/#service-account-tokens) +to authenticate instead of an OnCall access token. In this case you will also need to provide a +header (`X-Grafana-URL`) pointing to your Grafana stack: + +```shell +# With shell, you can just pass the correct header with each request +curl "api_endpoint_here" --header "Authorization: " --header "X-Grafana-URL: " +``` + +Service accounts allow you to set explicit permissions for tokens as well as expire and/or disable them if needed. + ## Pagination List endpoints such as List Integrations or List Alert Groups return multiple objects. diff --git a/docs/sources/oncall-api-reference/alertgroups.md b/docs/sources/oncall-api-reference/alertgroups.md index 33c2d46b6b..2a9c701a09 100644 --- a/docs/sources/oncall-api-reference/alertgroups.md +++ b/docs/sources/oncall-api-reference/alertgroups.md @@ -14,6 +14,8 @@ refs: ## List alert groups +**Required permission**: `grafana-oncall-app.alert-groups:read` + ```shell curl "{{API_URL}}/api/v1/alert_groups/" \ --request GET \ @@ -97,6 +99,8 @@ These available filter parameters should be provided as `GET` arguments: ## Alert group details +**Required permission**: `grafana-oncall-app.alert-groups:read` + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1" \ --request GET \ @@ -109,6 +113,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1" \ ## Acknowledge an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/acknowledge" \ --request POST \ @@ -121,6 +127,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/acknowledge" \ ## Unacknowledge an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unacknowledge" \ --request POST \ @@ -133,6 +141,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unacknowledge" \ ## Resolve an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/resolve" \ --request POST \ @@ -145,6 +155,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/resolve" \ ## Unresolve an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unresolve" \ --request POST \ @@ -157,6 +169,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unresolve" \ ## Silence an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/silence" \ --request POST \ @@ -177,6 +191,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/silence" \ ## Unsilence an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unsilence" \ --request POST \ @@ -189,6 +205,8 @@ curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/unsilence" \ ## Delete an alert group +**Required permission**: `grafana-oncall-app.alert-groups:write` + ```shell curl "{{API_URL}}/api/v1/alert_groups/I68T24C13IFW1/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/alerts.md b/docs/sources/oncall-api-reference/alerts.md index 0cbe85e38f..8752ba7d83 100644 --- a/docs/sources/oncall-api-reference/alerts.md +++ b/docs/sources/oncall-api-reference/alerts.md @@ -14,6 +14,8 @@ refs: ## List Alerts +**Required permission**: `grafana-oncall-app.alert-groups:read` + ```shell curl "{{API_URL}}/api/v1/alerts/" \ --request GET \ diff --git a/docs/sources/oncall-api-reference/escalation.md b/docs/sources/oncall-api-reference/escalation.md index 7a5b381dcc..b2b375d6eb 100644 --- a/docs/sources/oncall-api-reference/escalation.md +++ b/docs/sources/oncall-api-reference/escalation.md @@ -22,6 +22,8 @@ refs: # Escalation HTTP API +**Required permission**: `grafana-oncall-app.alert-groups:direct-paging` (user authentication only) + See [Manual paging integration](ref:manual-paging) for more background on how escalating to a team or user(s) works. ## Escalate to a set of users diff --git a/docs/sources/oncall-api-reference/escalation_chains.md b/docs/sources/oncall-api-reference/escalation_chains.md index a596acad56..5d181c02dc 100644 --- a/docs/sources/oncall-api-reference/escalation_chains.md +++ b/docs/sources/oncall-api-reference/escalation_chains.md @@ -14,6 +14,8 @@ refs: ## Create an escalation chain +**Required permission**: `grafana-oncall-app.escalation-chains:write` + ```shell curl "{{API_URL}}/api/v1/escalation_chains/" \ --request POST \ @@ -45,6 +47,8 @@ The above command returns JSON structured in the following way: ## Get an escalation chain +**Required permission**: `grafana-oncall-app.escalation-chains:read` + ```shell curl "{{API_URL}}/api/v1/escalation_chains/F5JU6KJET33FE/" \ --request GET \ @@ -68,6 +72,8 @@ The above command returns JSON structured in the following way: ## List escalation chains +**Required permission**: `grafana-oncall-app.escalation-chains:read` + ```shell curl "{{API_URL}}/api/v1/escalation_chains/" \ --request GET \ @@ -103,6 +109,8 @@ The above command returns JSON structured in the following way: ## Delete an escalation chain +**Required permission**: `grafana-oncall-app.escalation-chains:write` + ```shell curl "{{API_URL}}/api/v1/escalation_chains/F5JU6KJET33FE/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/escalation_policies.md b/docs/sources/oncall-api-reference/escalation_policies.md index 502e835e77..465b7f2540 100644 --- a/docs/sources/oncall-api-reference/escalation_policies.md +++ b/docs/sources/oncall-api-reference/escalation_policies.md @@ -14,6 +14,8 @@ refs: ## Create an escalation policy +**Required permission**: `grafana-oncall-app.escalation-chains:write` + ```shell curl "{{API_URL}}/api/v1/escalation_policies/" \ --request POST \ @@ -61,6 +63,8 @@ The above command returns JSON structured in the following way: ## Get an escalation policy +**Required permission**: `grafana-oncall-app.escalation-chains:read` + ```shell curl "{{API_URL}}/api/v1/escalation_policies/E3GA6SJETWWJS/" \ --request GET \ @@ -82,6 +86,8 @@ The above command returns JSON structured in the following way: ## Update an escalation policy +**Required permission**: `grafana-oncall-app.escalation-chains:write` + ```shell curl "{{API_URL}}/api/v1/escalation_policies/E3GA6SJETWWJS/" \ --request PUT \ @@ -115,6 +121,8 @@ The above command returns JSON structured in the following way: ## List escalation policies +**Required permission**: `grafana-oncall-app.escalation-chains:read` + ```shell curl "{{API_URL}}/api/v1/escalation_policies/" \ --request GET \ @@ -163,6 +171,8 @@ The following available filter parameter should be provided as a `GET` argument: ## Delete an escalation policy +**Required permission**: `grafana-oncall-app.escalation-chains:write` + ```shell curl "{{API_URL}}/api/v1/escalation_policies/E3GA6SJETWWJS/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/integrations.md b/docs/sources/oncall-api-reference/integrations.md index 0d75e4ca5e..e509674f07 100644 --- a/docs/sources/oncall-api-reference/integrations.md +++ b/docs/sources/oncall-api-reference/integrations.md @@ -23,6 +23,8 @@ refs: ## Create an integration +**Required permission**: `grafana-oncall-app.integrations:write` + ```shell curl "{{API_URL}}/api/v1/integrations/" \ --request POST \ @@ -102,6 +104,8 @@ For example, to learn how to integrate Grafana OnCall with Alertmanager refer to ## Get integration +**Required permission**: `grafana-oncall-app.integrations:read` + ```shell curl "{{API_URL}}/api/v1/integrations/CFRPV98RPR1U8/" \ --request GET \ @@ -177,6 +181,8 @@ This endpoint retrieves an integration. Integrations are sources of alerts and a ## List integrations +**Required permission**: `grafana-oncall-app.integrations:read` + ```shell curl "{{API_URL}}/api/v1/integrations/" \ --request GET \ @@ -262,6 +268,8 @@ The above command returns JSON structured in the following way: ## Update integration +**Required permission**: `grafana-oncall-app.integrations:write` + ```shell curl "{{API_URL}}/api/v1/integrations/CFRPV98RPR1U8/" \ --request PUT \ @@ -339,6 +347,8 @@ The above command returns JSON structured in the following way: ## Delete integration +**Required permission**: `grafana-oncall-app.integrations:write` + Deleted integrations will stop recording new alerts from monitoring. Integration removal won't trigger removal of related alert groups or alerts. diff --git a/docs/sources/oncall-api-reference/on_call_shifts.md b/docs/sources/oncall-api-reference/on_call_shifts.md index 203622303b..9a55360e42 100644 --- a/docs/sources/oncall-api-reference/on_call_shifts.md +++ b/docs/sources/oncall-api-reference/on_call_shifts.md @@ -14,6 +14,8 @@ refs: ## Create an OnCall shift +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/on_call_shifts/" \ --request POST \ @@ -77,6 +79,8 @@ For more information about recurrence rules, refer to [RFC 5545](https://tools.i ## Get OnCall shifts +**Required permission**: `grafana-oncall-app.schedules:read` + ```shell curl "{{API_URL}}/api/v1/on_call_shifts/OH3V5FYQEYJ6M/" \ --request GET \ @@ -106,6 +110,8 @@ The above command returns JSON structured in the following way: ## List OnCall shifts +**Required permission**: `grafana-oncall-app.schedules:read` + ```shell curl "{{API_URL}}/api/v1/on_call_shifts/" \ --request GET \ @@ -169,6 +175,8 @@ The following available filter parameters should be provided as `GET` arguments: ## Update OnCall shift +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/on_call_shifts/OH3V5FYQEYJ6M/" \ --request PUT \ @@ -208,6 +216,8 @@ The above command returns JSON structured in the following way: ## Delete OnCall shift +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/on_call_shifts/OH3V5FYQEYJ6M/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/organizations.md b/docs/sources/oncall-api-reference/organizations.md index 4ad35c07a1..484fca30ac 100644 --- a/docs/sources/oncall-api-reference/organizations.md +++ b/docs/sources/oncall-api-reference/organizations.md @@ -14,6 +14,8 @@ refs: ## Get an organization +**Required permission**: `grafana-oncall-app.other-settings:read` + This endpoint retrieves the organization object. ```shell @@ -41,6 +43,8 @@ The above command returns JSON structured in the following way: ## List Organizations +**Required permission**: `grafana-oncall-app.other-settings:read` + ```shell curl "{{API_URL}}/api/v1/organizations/" \ --request GET \ diff --git a/docs/sources/oncall-api-reference/outgoing_webhooks.md b/docs/sources/oncall-api-reference/outgoing_webhooks.md index 70a37d0a03..8b032db0be 100644 --- a/docs/sources/oncall-api-reference/outgoing_webhooks.md +++ b/docs/sources/oncall-api-reference/outgoing_webhooks.md @@ -30,6 +30,8 @@ For more details about specific fields of a webhook, refer to [Outgoing webhooks ## List webhooks +**Required permission**: `grafana-oncall-app.outgoing-webhooks:read` + ```shell curl "{{API_URL}}/api/v1/webhooks/" \ --request GET \ @@ -75,6 +77,8 @@ The above command returns JSON structured in the following way: ## Get webhook +**Required permission**: `grafana-oncall-app.outgoing-webhooks:read` + ```shell curl "{{API_URL}}/api/v1/webhooks/{{WEBHOOK_UID}}/" \ --request GET \ @@ -108,6 +112,8 @@ The above command returns JSON structured in the following way: ## Create webhook +**Required permission**: `grafana-oncall-app.outgoing-webhooks:write` + ```shell curl "{{API_URL}}/api/v1/webhooks/" \ --request POST \ @@ -167,6 +173,8 @@ The above command returns JSON structured in the following way: ## Update webhook +**Required permission**: `grafana-oncall-app.outgoing-webhooks:write` + ```shell curl "{{API_URL}}/api/v1/webhooks/{{WEBHOOK_UID}}/" \ --request PUT \ @@ -201,6 +209,8 @@ The above command returns JSON structured in the following way: ## Delete webhook +**Required permission**: `grafana-oncall-app.outgoing-webhooks:write` + ```shell curl "{{API_URL}}/api/v1/webhooks/{{WEBHOOK_UID}}/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/personal_notification_rules.md b/docs/sources/oncall-api-reference/personal_notification_rules.md index d647f9d92a..c2aa9006c5 100644 --- a/docs/sources/oncall-api-reference/personal_notification_rules.md +++ b/docs/sources/oncall-api-reference/personal_notification_rules.md @@ -14,6 +14,8 @@ refs: ## Post a personal notification rule +**Required permission**: `grafana-oncall-app.user-settings:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/personal_notification_rules/" \ --request POST \ @@ -51,6 +53,8 @@ The above command returns JSON structured in the following way: ## Get personal notification rule +**Required permission**: `grafana-oncall-app.user-settings:read` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/personal_notification_rules/ND9EHN5LN1DUU/" \ --request GET \ @@ -77,6 +81,8 @@ The above command returns JSON structured in the following way: ## List personal notification rules +**Required permission**: `grafana-oncall-app.user-settings:read` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/personal_notification_rules/" \ --request GET \ @@ -141,6 +147,8 @@ The following available filter parameters should be provided as `GET` arguments: ## Delete a personal notification rule +**Required permission**: `grafana-oncall-app.user-settings:write` (user authentication only) + ```shell curl "{{API_URL}}/api/v1/personal_notification_rules/NWAL6WFJNWDD8/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/resolution_notes.md b/docs/sources/oncall-api-reference/resolution_notes.md index 9cf181d0c2..254ba7cce9 100644 --- a/docs/sources/oncall-api-reference/resolution_notes.md +++ b/docs/sources/oncall-api-reference/resolution_notes.md @@ -14,6 +14,8 @@ refs: ## Create a resolution note +**Required permission**: `grafana-oncall-app.alert-groups:write` + ```shell curl "{{API_URL}}/api/v1/resolution_notes/" \ --request POST \ @@ -49,6 +51,8 @@ The above command returns JSON structured in the following way: ## Get a resolution note +**Required permission**: `grafana-oncall-app.alert-groups:read` + ```shell curl "{{API_URL}}/api/v1/resolution_notes/M4BTQUS3PRHYQ/" \ --request GET \ @@ -75,6 +79,8 @@ The above command returns JSON structured in the following way: ## List resolution notes +**Required permission**: `grafana-oncall-app.alert-groups:read` + ```shell curl "{{API_URL}}/api/v1/resolution_notes/" \ --request GET \ @@ -117,6 +123,8 @@ The following available filter parameter should be provided as a `GET` argument: ## Update a resolution note +**Required permission**: `grafana-oncall-app.alert-groups:write` + ```shell curl "{{API_URL}}/api/v1/resolution_notes/M4BTQUS3PRHYQ/" \ --request PUT \ @@ -146,6 +154,8 @@ The above command returns JSON structured in the following way: ## Delete a resolution note +**Required permission**: `grafana-oncall-app.alert-groups:write` + ```shell curl "{{API_URL}}/api/v1/resolution_notes/M4BTQUS3PRHYQ/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/routes.md b/docs/sources/oncall-api-reference/routes.md index 4386492d2e..acf9a9b241 100644 --- a/docs/sources/oncall-api-reference/routes.md +++ b/docs/sources/oncall-api-reference/routes.md @@ -14,6 +14,8 @@ refs: ## Create a route +**Required permission**: `grafana-oncall-app.integrations:write` + ```shell curl "{{API_URL}}/api/v1/routes/" \ --request POST \ @@ -67,6 +69,8 @@ Routes allow you to direct different alerts to different messenger channels and ## Get a route +**Required permission**: `grafana-oncall-app.integrations:read` + ```shell curl "{{API_URL}}/api/v1/routes/RIYGUJXCPFHXY/" \ --request GET \ @@ -96,6 +100,8 @@ The above command returns JSON structured in the following way: ## List routes +**Required permission**: `grafana-oncall-app.integrations:read` + ```shell curl "{{API_URL}}/api/v1/routes/" \ --request GET \ @@ -153,6 +159,8 @@ The following available filter parameters should be provided as `GET` arguments: ## Update route +**Required permission**: `grafana-oncall-app.integrations:write` + ```shell curl "{{API_URL}}/api/v1/routes/RIYGUJXCPFHXY/" \ --request PUT \ @@ -189,6 +197,8 @@ The above command returns JSON structured in the following way: ## Delete a route +**Required permission**: `grafana-oncall-app.integrations:write` + ```shell curl "{{API_URL}}/api/v1/routes/RIYGUJXCPFHXY/" \ --request DELETE \ diff --git a/docs/sources/oncall-api-reference/schedules.md b/docs/sources/oncall-api-reference/schedules.md index 57dfdf1d7e..a73f305177 100644 --- a/docs/sources/oncall-api-reference/schedules.md +++ b/docs/sources/oncall-api-reference/schedules.md @@ -14,6 +14,8 @@ refs: ## Create a schedule +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/schedules/" \ --request POST \ @@ -65,6 +67,8 @@ The above command returns JSON structured in the following way: ## Get a schedule +**Required permission**: `grafana-oncall-app.schedules:read` + ```shell curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \ --request GET \ @@ -96,6 +100,8 @@ The above command returns JSON structured in the following way: ## List schedules +**Required permission**: `grafana-oncall-app.schedules:read` + ```shell curl "{{API_URL}}/api/v1/schedules/" \ --request GET \ @@ -158,6 +164,8 @@ The following available filter parameter should be provided as a `GET` argument: ## Update a schedule +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \ --request PUT \ @@ -196,6 +204,8 @@ The above command returns JSON structured in the following way: ## Delete a schedule +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \ --request DELETE \ @@ -209,6 +219,8 @@ curl "{{API_URL}}/api/v1/schedules/SBM7DV7BKFUYU/" \ ## Export a schedule's final shifts +**Required permission**: `grafana-oncall-app.schedules:read` + **HTTP request** ```shell diff --git a/docs/sources/oncall-api-reference/shift_swaps.md b/docs/sources/oncall-api-reference/shift_swaps.md index d2b9caef48..7e1d232766 100644 --- a/docs/sources/oncall-api-reference/shift_swaps.md +++ b/docs/sources/oncall-api-reference/shift_swaps.md @@ -14,6 +14,8 @@ refs: ## Create a shift swap request +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/shift_swaps/" \ --request POST \ @@ -87,6 +89,8 @@ The above command returns JSON structured in the following way: ## Get a shift swap request +**Required permission**: `grafana-oncall-app.schedules:read` + ```shell curl "{{API_URL}}/api/v1/shift_swaps/SSRG1TDNBMJQ1NC/" \ --request GET \ @@ -145,6 +149,8 @@ The above command returns JSON structured in the following way: ## List shift swap requests +**Required permission**: `grafana-oncall-app.schedules:read` + ```shell curl "{{API_URL}}/api/v1/shift_swaps/" \ --request GET \ @@ -207,6 +213,8 @@ The following available filter parameters may be provided as a `GET` arguments: ## Update a shift swap request +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/shift_swaps/SSRG1TDNBMJQ1NC/" \ --request PUT \ @@ -271,6 +279,8 @@ The above command returns JSON structured in the following way: ## Delete a shift swap request +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/shift_swaps/SSRG1TDNBMJQ1NC/" \ --request DELETE \ @@ -284,6 +294,8 @@ curl "{{API_URL}}/api/v1/shift_swaps/SSRG1TDNBMJQ1NC/" \ ## Take a shift swap request +**Required permission**: `grafana-oncall-app.schedules:write` + ```shell curl "{{API_URL}}/api/v1/shift_swaps/SSRG1TDNBMJQ1NC/take" \ --request POST \ diff --git a/docs/sources/oncall-api-reference/slack_channels.md b/docs/sources/oncall-api-reference/slack_channels.md index e8e0d89cbd..c1484b6077 100644 --- a/docs/sources/oncall-api-reference/slack_channels.md +++ b/docs/sources/oncall-api-reference/slack_channels.md @@ -14,6 +14,8 @@ refs: ## List Slack Channels +**Required permission**: `grafana-oncall-app.chatops:read` + ```shell curl "{{API_URL}}/api/v1/slack_channels/" \ --request GET \ diff --git a/docs/sources/oncall-api-reference/teams.md b/docs/sources/oncall-api-reference/teams.md index 04e001ae2d..32e39509d8 100644 --- a/docs/sources/oncall-api-reference/teams.md +++ b/docs/sources/oncall-api-reference/teams.md @@ -14,6 +14,8 @@ refs: ## Get a team +**Required permission**: `grafana-oncall-app.user-settings:read` + This endpoint retrieves the team object. ```shell @@ -49,6 +51,8 @@ The above command returns JSON structured in the following way: ## List Teams +**Required permission**: `grafana-oncall-app.user-settings:read` + ```shell curl "{{API_URL}}/api/v1/teams/" \ --request GET \ diff --git a/docs/sources/oncall-api-reference/user_groups.md b/docs/sources/oncall-api-reference/user_groups.md index c1d561eee2..3e4de1323f 100644 --- a/docs/sources/oncall-api-reference/user_groups.md +++ b/docs/sources/oncall-api-reference/user_groups.md @@ -16,6 +16,8 @@ refs: ## List user groups +**Required permission**: `grafana-oncall-app.chatops:read` + ```shell curl "{{API_URL}}/api/v1/user_groups/" \ --request GET \ diff --git a/docs/sources/oncall-api-reference/users.md b/docs/sources/oncall-api-reference/users.md index f7421a7781..e8a38e74a1 100644 --- a/docs/sources/oncall-api-reference/users.md +++ b/docs/sources/oncall-api-reference/users.md @@ -14,6 +14,8 @@ refs: ## Get a user +**Required permission**: `grafana-oncall-app.user-settings:read` + This endpoint retrieves the user object. ```shell @@ -62,6 +64,8 @@ Use `{{API_URL}}/api/v1/users/current` to retrieve the current user. ## List Users +**Required permission**: `grafana-oncall-app.user-settings:read` + ```shell curl "{{API_URL}}/api/v1/users/" \ --request GET \ From ec874440ba43087dd604c10a976d75c1131a9f36 Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Wed, 11 Dec 2024 11:50:49 -0300 Subject: [PATCH 2/3] chore: update service account token auth organization setup check (#5354) Ignore setup organization response (for now, since it can return a 400 when a sync is/was recently in progress) and base response on organization being available or not instead. --- engine/apps/auth_token/auth.py | 9 +++++---- engine/apps/auth_token/tests/test_grafana_auth.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/engine/apps/auth_token/auth.py b/engine/apps/auth_token/auth.py index af1fc2c6b4..85126ef64d 100644 --- a/engine/apps/auth_token/auth.py +++ b/engine/apps/auth_token/auth.py @@ -361,7 +361,7 @@ def authenticate(self, request): organization = self.get_organization(request, auth) if not organization: - raise exceptions.AuthenticationFailed("Invalid organization.") + raise exceptions.AuthenticationFailed("Organization not found.") if organization.is_moved: raise OrganizationMovedException(organization) if organization.deleted_at: @@ -374,9 +374,10 @@ def get_organization(self, request, auth): if grafana_url: organization = Organization.objects.filter(grafana_url=grafana_url).first() if not organization: - success = setup_organization(grafana_url, auth) - if not success: - raise exceptions.AuthenticationFailed("Invalid Grafana URL.") + # trigger a request to sync the organization + # (ignore response since we can get a 400 if sync was already triggered; + # if organization exists, we are good) + setup_organization(grafana_url, auth) organization = Organization.objects.filter(grafana_url=grafana_url).first() return organization diff --git a/engine/apps/auth_token/tests/test_grafana_auth.py b/engine/apps/auth_token/tests/test_grafana_auth.py index 3cb01727f5..950e63e1fa 100644 --- a/engine/apps/auth_token/tests/test_grafana_auth.py +++ b/engine/apps/auth_token/tests/test_grafana_auth.py @@ -93,7 +93,7 @@ def test_grafana_authentication_missing_org(): with pytest.raises(exceptions.AuthenticationFailed) as exc: GrafanaServiceAccountAuthentication().authenticate(request) - assert exc.value.detail == "Invalid organization." + assert exc.value.detail == "Organization not found." @pytest.mark.django_db @@ -112,7 +112,7 @@ def test_grafana_authentication_invalid_grafana_url(): with pytest.raises(exceptions.AuthenticationFailed) as exc: GrafanaServiceAccountAuthentication().authenticate(request) - assert exc.value.detail == "Invalid Grafana URL." + assert exc.value.detail == "Organization not found." @pytest.mark.django_db From b8dc7af14a529cd80582e8127a8d62a46d26aec3 Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Wed, 11 Dec 2024 16:08:10 -0300 Subject: [PATCH 3/3] fix: remove service account role check on sync requests (#5355) External service accounts do not have a role set so async triggered periodic org syncs were rejected. Improving role/perm check in a later PR instead (restriction wasn't originally there). --- engine/apps/auth_token/auth.py | 6 ++-- .../apps/auth_token/tests/test_plugin_auth.py | 32 +------------------ 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/engine/apps/auth_token/auth.py b/engine/apps/auth_token/auth.py index 85126ef64d..cac5e27432 100644 --- a/engine/apps/auth_token/auth.py +++ b/engine/apps/auth_token/auth.py @@ -135,11 +135,9 @@ def _get_user(request: Request, organization: Organization) -> User: user_id = context["UserID"] if context.get("IsServiceAccount", False): - # no user involved in service account requests - logger.info(f"serviceaccount request - id={user_id}") service_account_role = context.get("Role", "None") - if service_account_role.lower() != "admin": - raise exceptions.AuthenticationFailed("Service account requests must have Admin or Editor role.") + # no user involved in service account requests + logger.info(f"serviceaccount request - id={user_id} - role={service_account_role}") return None try: diff --git a/engine/apps/auth_token/tests/test_plugin_auth.py b/engine/apps/auth_token/tests/test_plugin_auth.py index 44440e73f7..6e892da792 100644 --- a/engine/apps/auth_token/tests/test_plugin_auth.py +++ b/engine/apps/auth_token/tests/test_plugin_auth.py @@ -6,7 +6,7 @@ from rest_framework.exceptions import AuthenticationFailed from rest_framework.test import APIRequestFactory -from apps.auth_token.auth import BasePluginAuthentication, PluginAuthentication +from apps.auth_token.auth import PluginAuthentication INSTANCE_CONTEXT = '{"stack_id": 42, "org_id": 24, "grafana_token": "abc"}' @@ -176,33 +176,3 @@ def test_plugin_authentication_self_hosted_setup_new_user(make_organization, mak assert ret_user.user_id == 12 assert ret_token.organization == organization assert organization.users.count() == 1 - - -@pytest.mark.django_db -@pytest.mark.parametrize( - "role,expected_raises", [("Admin", False), ("Editor", True), ("Viewer", True), ("Other", True)] -) -def test_plugin_authentication_service_account(make_organization, role, expected_raises): - # Setting gcom_token_org_last_time_synced to now, so it doesn't try to sync with gcom - organization = make_organization( - stack_id=42, org_id=24, gcom_token="123", api_token="abc", gcom_token_org_last_time_synced=timezone.now() - ) - - headers = { - "HTTP_AUTHORIZATION": "gcom:123", - "HTTP_X-Instance-Context": INSTANCE_CONTEXT, - "HTTP_X-Grafana-Context": json.dumps({"UserId": 12, "Role": role, "IsServiceAccount": True}), - } - request = APIRequestFactory().get("/", **headers) - - if expected_raises: - with pytest.raises(AuthenticationFailed): - BasePluginAuthentication().authenticate(request) - else: - ret_user, ret_token = BasePluginAuthentication().authenticate(request) - assert ret_user is None - assert ret_token.organization == organization - - # PluginAuthentication should always raise an exception if the request comes from a service account - with pytest.raises(AuthenticationFailed): - PluginAuthentication().authenticate(request)