From e6b9ded949bb11316f36efcd1981ea508e1845ca Mon Sep 17 00:00:00 2001 From: Sofia Papagiannaki <1632407+papagian@users.noreply.github.com> Date: Tue, 19 Jul 2022 12:52:51 +0300 Subject: [PATCH] API: Add service account routes to the swagger (#52398) * API: Add service account routes to the swagger --- .../developers/http_api/serviceaccount.md | 34 +- .../definitions/service_account_tokens.go | 90 +++ pkg/api/docs/definitions/service_accounts.go | 154 ++++ pkg/api/docs/tags.json | 4 + pkg/api/dtos/apikey.go | 8 +- pkg/services/serviceaccounts/api/token.go | 22 +- pkg/services/serviceaccounts/models.go | 69 +- public/api-merged.json | 732 +++++++++++++++++- public/api-spec.json | 728 ++++++++++++++++- 9 files changed, 1772 insertions(+), 69 deletions(-) create mode 100644 pkg/api/docs/definitions/service_account_tokens.go create mode 100644 pkg/api/docs/definitions/service_accounts.go diff --git a/docs/sources/developers/http_api/serviceaccount.md b/docs/sources/developers/http_api/serviceaccount.md index a2c5d6fbfae..41ad0787b0f 100644 --- a/docs/sources/developers/http_api/serviceaccount.md +++ b/docs/sources/developers/http_api/serviceaccount.md @@ -115,7 +115,7 @@ Requires basic authentication and that the authenticated user is a Grafana Admin **Example Response**: ```http -HTTP/1.1 200 +HTTP/1.1 201 Content-Type: application/json { @@ -140,9 +140,9 @@ Content-Type: application/json See note in the [introduction]({{< ref "#service-account-api" >}}) for an explanation. -| Action | Scope | -| -------------------- | ------------------ | -| serviceaccounts:read | serviceaccounts:\* | +| Action | Scope | +| -------------------- | -------------------- | +| serviceaccounts:read | serviceaccounts:id:1 | **Example Request**: @@ -183,14 +183,14 @@ Content-Type: application/json See note in the [introduction]({{< ref "#service-account-api" >}}) for an explanation. -| Action | Scope | -| --------------------- | ------------------ | -| serviceaccounts:write | serviceaccounts:\* | +| Action | Scope | +| --------------------- | -------------------- | +| serviceaccounts:write | serviceaccounts:id:1 | **Example Request**: ```http -PUT /api/serviceaccounts/2 HTTP/1.1 +PATCH /api/serviceaccounts/2 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Basic YWRtaW46YWRtaW4= @@ -235,9 +235,9 @@ Content-Type: application/json See note in the [introduction]({{< ref "#service-account-api" >}}) for an explanation. -| Action | Scope | -| -------------------- | ------------------ | -| serviceaccounts:read | serviceaccounts:\* | +| Action | Scope | +| -------------------- | -------------------- | +| serviceaccounts:read | serviceaccounts:id:1 | **Example Request**: @@ -277,9 +277,9 @@ Content-Type: application/json See note in the [introduction]({{< ref "#service-account-api" >}}) for an explanation. -| Action | Scope | -| --------------------- | ------------------ | -| serviceaccounts:write | serviceaccounts:\* | +| Action | Scope | +| --------------------- | -------------------- | +| serviceaccounts:write | serviceaccounts:id:1 | **Example Request**: @@ -318,9 +318,9 @@ Content-Type: application/json See note in the [introduction]({{< ref "#service-account-api" >}}) for an explanation. -| Action | Scope | -| --------------------- | ------------------ | -| serviceaccounts:write | serviceaccounts:\* | +| Action | Scope | +| --------------------- | -------------------- | +| serviceaccounts:write | serviceaccounts:id:1 | **Example Request**: diff --git a/pkg/api/docs/definitions/service_account_tokens.go b/pkg/api/docs/definitions/service_account_tokens.go new file mode 100644 index 00000000000..a7fc74e0134 --- /dev/null +++ b/pkg/api/docs/definitions/service_account_tokens.go @@ -0,0 +1,90 @@ +package definitions + +import ( + "github.com/grafana/grafana/pkg/api/dtos" + "github.com/grafana/grafana/pkg/services/serviceaccounts" + "github.com/grafana/grafana/pkg/services/serviceaccounts/api" +) + +// swagger:route GET /serviceaccounts/{serviceAccountId}/tokens service_accounts listTokens +// +// Get service account tokens +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:read` scope: `global:serviceaccounts:id:1` (single service account) +// +// Requires basic authentication and that the authenticated user is a Grafana Admin. +// +// Responses: +// 200: listTokensResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 500: internalServerError + +// swagger:route POST /serviceaccounts/{serviceAccountId}/tokens service_accounts createToken +// +// Create service account tokens +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account) +// +// Responses: +// 200: createTokenResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 404: notFoundError +// 409: conflictError +// 500: internalServerError + +// swagger:route DELETE /serviceaccounts/{serviceAccountId}/tokens/{tokenId} service_accounts deleteToken +// +// Delete service account tokens +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account) +// +// Requires basic authentication and that the authenticated user is a Grafana Admin. +// +// Responses: +// 200: okResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 404: notFoundError +// 500: internalServerError + +// swagger:parameters listTokens +type ListTokensParams struct { + // in:path + ServiceAccountId int64 `json:"serviceAccountId"` +} + +// swagger:parameters createToken +type CreateTokenParams struct { + // in:path + ServiceAccountId int64 `json:"serviceAccountId"` + // in:body + Body serviceaccounts.AddServiceAccountTokenCommand +} + +// swagger:parameters deleteToken +type DeleteTokenParams struct { + // in:path + TokenId int64 `json:"tokenId"` + // in:path + ServiceAccountId int64 `json:"serviceAccountId"` +} + +// swagger:response listTokensResponse +type ListTokensResponse struct { + // in:body + Body *api.TokenDTO +} + +// swagger:response createTokenResponse +type CreateTokenResponse struct { + // in:body + Body *dtos.NewApiKeyResult +} diff --git a/pkg/api/docs/definitions/service_accounts.go b/pkg/api/docs/definitions/service_accounts.go new file mode 100644 index 00000000000..52455fe3569 --- /dev/null +++ b/pkg/api/docs/definitions/service_accounts.go @@ -0,0 +1,154 @@ +package definitions + +import "github.com/grafana/grafana/pkg/services/serviceaccounts" + +// swagger:route GET /serviceaccounts/search service_accounts searchOrgServiceAccountsWithPaging +// +// Search service accounts with Paging +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:read` scope: `serviceaccounts:*` +// +// Responses: +// 200: searchOrgServiceAccountsWithPagingResponse +// 401: unauthorisedError +// 403: forbiddenError +// 500: internalServerError + +// swagger:route POST /serviceaccounts service_accounts createServiceAccount +// +// Create service account +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:write` scope: `serviceaccounts:*` +// +// Requires basic authentication and that the authenticated user is a Grafana Admin. +// +// Responses: +// 201: createServiceAccountResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 500: internalServerError + +// swagger:route GET /serviceaccounts/{serviceAccountId} service_accounts retrieveServiceAccount +// +// Get single serviceaccount by Id +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:read` scope: `serviceaccounts:id:1` (single service account) +// +// Responses: +// 200: retrieveServiceAccountResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 404: notFoundError +// 500: internalServerError + +// swagger:route PATCH /serviceaccounts/{serviceAccountId} service_accounts updateServiceAccount +// +// Update service account +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account) +// +// Responses: +// 200: updateServiceAccountResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 404: notFoundError +// 500: internalServerError + +// swagger:route DELETE /serviceaccounts/{serviceAccountId} service_accounts deleteServiceAccount +// +// Delete service account +// +// Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation): +// action: `serviceaccounts:delete` scope: `serviceaccounts:id:1` (single service account) +// +// Responses: +// 200: okResponse +// 400: badRequestError +// 401: unauthorisedError +// 403: forbiddenError +// 500: internalServerError + +// swagger:parameters searchOrgServiceAccountsWithPaging +type SearchOrgServiceAccountsWithPagingParams struct { + // in:query + // required:false + Disabled bool `jsson:"disabled"` + // in:query + // required:false + ExpiredTokens bool `json:"expiredTokens"` + // It will return results where the query value is contained in one of the name. + // Query values with spaces need to be URL encoded. + // in:query + // required:false + Query string `json:"query"` + // The default value is 1000. + // in:query + // required:false + PerPage int `json:"perpage"` + // The default value is 1. + // in:query + // required:false + Page int `json:"page"` +} + +// swagger:parameters createServiceAccount +type CreateServiceAccountParams struct { + //in:body + Body serviceaccounts.CreateServiceAccountForm +} + +// swagger:parameters retrieveServiceAccount +type RetrieveServiceAccountParams struct { + // in:path + ServiceAccountId int64 `json:"serviceAccountId"` +} + +// swagger:parameters updateServiceAccount +type UpdateServiceAccountParams struct { + // in:path + ServiceAccountId int64 `json:"serviceAccountId"` + // in:body + Body serviceaccounts.UpdateServiceAccountForm +} + +// swagger:parameters deleteServiceAccount +type DeleteServiceAccountParams struct { + // in:path + ServiceAccountId int64 `json:"serviceAccountId"` +} + +// swagger:response searchOrgServiceAccountsWithPagingResponse +type SearchOrgServiceAccountsWithPagingResponse struct { + // in:body + Body *serviceaccounts.SearchServiceAccountsResult +} + +// swagger:response createServiceAccountResponse +type CreateServiceAccountResponse struct { + // in:body + Body *serviceaccounts.ServiceAccountDTO +} + +// swagger:response retrieveServiceAccountResponse +type RetrieveServiceAccountResponse struct { + // in:body + Body *serviceaccounts.ServiceAccountDTO +} + +// swagger:response updateServiceAccountResponse +type UpdateServiceAccountResponse struct { + // in:body + Body struct { + Message string `json:"message"` + ID int64 `json:"id"` + Name string `json:"name"` + ServiceAccount *serviceaccounts.ServiceAccountProfileDTO `json:"serviceaccount"` + } +} diff --git a/pkg/api/docs/tags.json b/pkg/api/docs/tags.json index b46177db930..38a36cb79bc 100644 --- a/pkg/api/docs/tags.json +++ b/pkg/api/docs/tags.json @@ -83,6 +83,10 @@ { "name": "prometheus", "description": "Grafana Alerting Prometheus-compatible endpoints" + }, + { + "name": "service_accounts", + "description": "If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information." } ] } diff --git a/pkg/api/dtos/apikey.go b/pkg/api/dtos/apikey.go index d96a5201679..6353deccd60 100644 --- a/pkg/api/dtos/apikey.go +++ b/pkg/api/dtos/apikey.go @@ -7,10 +7,14 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol" ) +// swagger:model type NewApiKeyResult struct { - ID int64 `json:"id"` + // example: 1 + ID int64 `json:"id"` + // example: grafana Name string `json:"name"` - Key string `json:"key"` + // example: glsa_yscW25imSKJIuav8zF37RZmnbiDvB05G_fcaaf58a + Key string `json:"key"` } type ApiKeyDTO struct { diff --git a/pkg/services/serviceaccounts/api/token.go b/pkg/services/serviceaccounts/api/token.go index 263e16b6e5f..334b874ffe5 100644 --- a/pkg/services/serviceaccounts/api/token.go +++ b/pkg/services/serviceaccounts/api/token.go @@ -20,14 +20,22 @@ const ( ServiceID = "sa" ) +// swagger:model type TokenDTO struct { - Id int64 `json:"id"` - Name string `json:"name"` - Created *time.Time `json:"created"` - LastUsedAt *time.Time `json:"lastUsedAt"` - Expiration *time.Time `json:"expiration"` - SecondsUntilExpiration *float64 `json:"secondsUntilExpiration"` - HasExpired bool `json:"hasExpired"` + // example: 1 + Id int64 `json:"id"` + // example: grafana + Name string `json:"name"` + // example: 2022-03-23T10:31:02Z + Created *time.Time `json:"created"` + // example: 2022-03-23T10:31:02Z + LastUsedAt *time.Time `json:"lastUsedAt"` + // example: 2022-03-23T10:31:02Z + Expiration *time.Time `json:"expiration"` + // example: 0 + SecondsUntilExpiration *float64 `json:"secondsUntilExpiration"` + // example: false + HasExpired bool `json:"hasExpired"` } func hasExpired(expiration *int64) bool { diff --git a/pkg/services/serviceaccounts/models.go b/pkg/services/serviceaccounts/models.go index 1eab5a23b68..3a93348e1b8 100644 --- a/pkg/services/serviceaccounts/models.go +++ b/pkg/services/serviceaccounts/models.go @@ -25,27 +25,41 @@ type ServiceAccount struct { Id int64 } +// swagger:model type CreateServiceAccountForm struct { - Name string `json:"name" binding:"Required"` - Role *models.RoleType `json:"role"` - IsDisabled *bool `json:"isDisabled"` + // example: grafana + Name string `json:"name" binding:"Required"` + // example: Admin + Role *models.RoleType `json:"role"` + // example: false + IsDisabled *bool `json:"isDisabled"` } +// swagger:model type UpdateServiceAccountForm struct { Name *string `json:"name"` Role *models.RoleType `json:"role"` IsDisabled *bool `json:"isDisabled"` } +// swagger: model type ServiceAccountDTO struct { - Id int64 `json:"id" xorm:"user_id"` - Name string `json:"name" xorm:"name"` - Login string `json:"login" xorm:"login"` - OrgId int64 `json:"orgId" xorm:"org_id"` - IsDisabled bool `json:"isDisabled" xorm:"is_disabled"` - Role string `json:"role" xorm:"role"` - Tokens int64 `json:"tokens"` - AvatarUrl string `json:"avatarUrl"` + Id int64 `json:"id" xorm:"user_id"` + // example: grafana + Name string `json:"name" xorm:"name"` + // example: sa-grafana + Login string `json:"login" xorm:"login"` + // example: 1 + OrgId int64 `json:"orgId" xorm:"org_id"` + // example: false + IsDisabled bool `json:"isDisabled" xorm:"is_disabled"` + // example: Viewer + Role string `json:"role" xorm:"role"` + // example: 0 + Tokens int64 `json:"tokens"` + // example: /avatar/85ec38023d90823d3e5b43ef35646af9 + AvatarUrl string `json:"avatarUrl"` + // example: {"serviceaccounts:delete": true, "serviceaccounts:read": true, "serviceaccounts:write": true} AccessControl map[string]bool `json:"accessControl,omitempty"` } @@ -57,23 +71,38 @@ type AddServiceAccountTokenCommand struct { Result *models.ApiKey `json:"-"` } +// swagger: model type SearchServiceAccountsResult struct { + // It can be used for pagination of the user list + // E.g. if totalCount is equal to 100 users and + // the perpage parameter is set to 10 then there are 10 pages of users. TotalCount int64 `json:"totalCount"` ServiceAccounts []*ServiceAccountDTO `json:"serviceAccounts"` Page int `json:"page"` PerPage int `json:"perPage"` } +// swagger:model type ServiceAccountProfileDTO struct { - Id int64 `json:"id" xorm:"user_id"` - Name string `json:"name" xorm:"name"` - Login string `json:"login" xorm:"login"` - OrgId int64 `json:"orgId" xorm:"org_id"` - IsDisabled bool `json:"isDisabled" xorm:"is_disabled"` - Created time.Time `json:"createdAt" xorm:"created"` - Updated time.Time `json:"updatedAt" xorm:"updated"` - AvatarUrl string `json:"avatarUrl" xorm:"-"` - Role string `json:"role" xorm:"role"` + // example: 2 + Id int64 `json:"id" xorm:"user_id"` + // example: test + Name string `json:"name" xorm:"name"` + // example: sa-grafana + Login string `json:"login" xorm:"login"` + // example: 1 + OrgId int64 `json:"orgId" xorm:"org_id"` + // example: false + IsDisabled bool `json:"isDisabled" xorm:"is_disabled"` + // example: 2022-03-21T14:35:33Z + Created time.Time `json:"createdAt" xorm:"created"` + // example: 2022-03-21T14:35:33Z + Updated time.Time `json:"updatedAt" xorm:"updated"` + // example: /avatar/8ea890a677d6a223c591a1beea6ea9d2 + AvatarUrl string `json:"avatarUrl" xorm:"-"` + // example: Editor + Role string `json:"role" xorm:"role"` + // example: [] Teams []string `json:"teams" xorm:"-"` Tokens int64 `json:"tokens,omitempty"` AccessControl map[string]bool `json:"accessControl,omitempty" xorm:"-"` diff --git a/public/api-merged.json b/public/api-merged.json index 98ab31b0a08..246dc26eb01 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -119,6 +119,13 @@ "tags": ["access_control", "enterprise"], "summary": "Get all roles.", "operationId": "getAllRoles", + "parameters": [ + { + "type": "boolean", + "name": "delegatable", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/getAllRolesResponse" @@ -142,7 +149,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/CreateRoleWithPermissionsCommand" + "$ref": "#/definitions/CreateRoleForm" } } ], @@ -233,6 +240,16 @@ "summary": "Delete a custom role.", "operationId": "deleteCustomRole", "parameters": [ + { + "type": "boolean", + "name": "force", + "in": "query" + }, + { + "type": "boolean", + "name": "global", + "in": "query" + }, { "type": "string", "name": "roleUID", @@ -7073,6 +7090,328 @@ } } }, + "/serviceaccounts": { + "post": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:*`\n\nRequires basic authentication and that the authenticated user is a Grafana Admin.", + "tags": ["service_accounts"], + "summary": "Create service account", + "operationId": "createServiceAccount", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/CreateServiceAccountForm" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/createServiceAccountResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/search": { + "get": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:read` scope: `serviceaccounts:*`", + "tags": ["service_accounts"], + "summary": "Search service accounts with Paging", + "operationId": "searchOrgServiceAccountsWithPaging", + "parameters": [ + { + "type": "boolean", + "name": "Disabled", + "in": "query" + }, + { + "type": "boolean", + "name": "expiredTokens", + "in": "query" + }, + { + "type": "string", + "description": "It will return results where the query value is contained in one of the name.\nQuery values with spaces need to be URL encoded.", + "name": "query", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "description": "The default value is 1000.", + "name": "perpage", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "description": "The default value is 1.", + "name": "page", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/searchOrgServiceAccountsWithPagingResponse" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/{serviceAccountId}": { + "get": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:read` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Get single serviceaccount by Id", + "operationId": "retrieveServiceAccount", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/retrieveServiceAccountResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + }, + "delete": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:delete` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Delete service account", + "operationId": "deleteServiceAccount", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/okResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + }, + "patch": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Update service account", + "operationId": "updateServiceAccount", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/UpdateServiceAccountForm" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/updateServiceAccountResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/{serviceAccountId}/tokens": { + "get": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:read` scope: `global:serviceaccounts:id:1` (single service account)\n\nRequires basic authentication and that the authenticated user is a Grafana Admin.", + "tags": ["service_accounts"], + "summary": "Get service account tokens", + "operationId": "listTokens", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/listTokensResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + }, + "post": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Create service account tokens", + "operationId": "createToken", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/AddServiceAccountTokenCommand" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/createTokenResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "409": { + "$ref": "#/responses/conflictError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/{serviceAccountId}/tokens/{tokenId}": { + "delete": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account)\n\nRequires basic authentication and that the authenticated user is a Grafana Admin.", + "tags": ["service_accounts"], + "summary": "Delete service account tokens", + "operationId": "deleteToken", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "tokenId", + "in": "path", + "required": true + }, + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/okResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, "/snapshot/shared-options": { "get": { "tags": ["snapshots"], @@ -7374,16 +7713,15 @@ "operationId": "removeTeamGroupApi", "parameters": [ { - "type": "integer", - "format": "int64", - "name": "teamId", + "type": "string", + "name": "groupId", "in": "path", "required": true }, { "type": "integer", "format": "int64", - "name": "groupId", + "name": "teamId", "in": "path", "required": true } @@ -9169,6 +9507,18 @@ } } }, + "AddServiceAccountTokenCommand": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "secondsToLive": { + "type": "integer", + "format": "int64" + } + } + }, "AddTeamMemberCommand": { "type": "object", "properties": { @@ -10319,7 +10669,7 @@ } } }, - "CreateRoleWithPermissionsCommand": { + "CreateRoleForm": { "type": "object", "properties": { "description": { @@ -10328,6 +10678,9 @@ "displayName": { "type": "string" }, + "global": { + "type": "boolean" + }, "group": { "type": "string" }, @@ -10352,6 +10705,24 @@ } } }, + "CreateServiceAccountForm": { + "type": "object", + "properties": { + "isDisabled": { + "type": "boolean", + "example": false + }, + "name": { + "type": "string", + "example": "grafana" + }, + "role": { + "type": "string", + "enum": ["Viewer", "Editor", "Admin"], + "example": "Admin" + } + } + }, "CreateTeamCommand": { "type": "object", "properties": { @@ -10991,6 +11362,83 @@ } } }, + "DataSourcePermissionRuleDTO": { + "type": "object", + "properties": { + "builtInRole": { + "type": "string" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "datasourceId": { + "type": "integer", + "format": "int64" + }, + "id": { + "type": "integer", + "format": "int64" + }, + "isManaged": { + "type": "boolean" + }, + "permission": { + "$ref": "#/definitions/DsPermissionType" + }, + "permissionName": { + "type": "string" + }, + "team": { + "type": "string" + }, + "teamAvatarUrl": { + "type": "string" + }, + "teamEmail": { + "type": "string" + }, + "teamId": { + "type": "integer", + "format": "int64" + }, + "updated": { + "type": "string", + "format": "date-time" + }, + "userAvatarUrl": { + "type": "string" + }, + "userEmail": { + "type": "string" + }, + "userId": { + "type": "integer", + "format": "int64" + }, + "userLogin": { + "type": "string" + } + } + }, + "DataSourcePermissionsDTO": { + "type": "object", + "properties": { + "datasourceId": { + "type": "integer", + "format": "int64" + }, + "enabled": { + "type": "boolean" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/DataSourcePermissionRuleDTO" + } + } + } + }, "DateTime": { "description": "DateTime is a time but it serializes to ISO8601 format with millis\nIt knows how to read 3 different variations of a RFC3339 date time.\nMost APIs we encounter want either millisecond or second precision times.\nThis just tries to make it worry-free.", "type": "string", @@ -12763,13 +13211,16 @@ "properties": { "id": { "type": "integer", - "format": "int64" + "format": "int64", + "example": 1 }, "key": { - "type": "string" + "type": "string", + "example": "glsa_yscW25imSKJIuav8zF37RZmnbiDvB05G_fcaaf58a" }, "name": { - "type": "string" + "type": "string", + "example": "grafana" } } }, @@ -14684,6 +15135,31 @@ } } }, + "SearchServiceAccountsResult": { + "description": "swagger: model", + "type": "object", + "properties": { + "page": { + "type": "integer", + "format": "int64" + }, + "perPage": { + "type": "integer", + "format": "int64" + }, + "serviceAccounts": { + "type": "array", + "items": { + "$ref": "#/definitions/ServiceAccountDTO" + } + }, + "totalCount": { + "description": "It can be used for pagination of the user list\nE.g. if totalCount is equal to 100 users and\nthe perpage parameter is set to 10 then there are 10 pages of users.", + "type": "integer", + "format": "int64" + } + } + }, "SearchTeamQueryResult": { "type": "object", "properties": { @@ -14738,6 +15214,119 @@ "title": "SecretURL is a URL that must not be revealed on marshaling.", "$ref": "#/definitions/URL" }, + "ServiceAccountDTO": { + "description": "swagger: model", + "type": "object", + "properties": { + "accessControl": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, + "example": { + "serviceaccounts:delete": true, + "serviceaccounts:read": true, + "serviceaccounts:write": true + } + }, + "avatarUrl": { + "type": "string", + "example": "/avatar/85ec38023d90823d3e5b43ef35646af9" + }, + "id": { + "type": "integer", + "format": "int64" + }, + "isDisabled": { + "type": "boolean", + "example": false + }, + "login": { + "type": "string", + "example": "sa-grafana" + }, + "name": { + "type": "string", + "example": "grafana" + }, + "orgId": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "role": { + "type": "string", + "example": "Viewer" + }, + "tokens": { + "type": "integer", + "format": "int64", + "example": 0 + } + } + }, + "ServiceAccountProfileDTO": { + "type": "object", + "properties": { + "accessControl": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "avatarUrl": { + "type": "string", + "example": "/avatar/8ea890a677d6a223c591a1beea6ea9d2" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "example": "2022-03-21T14:35:33Z" + }, + "id": { + "type": "integer", + "format": "int64", + "example": 2 + }, + "isDisabled": { + "type": "boolean", + "example": false + }, + "login": { + "type": "string", + "example": "sa-grafana" + }, + "name": { + "type": "string", + "example": "test" + }, + "orgId": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "role": { + "type": "string", + "example": "Editor" + }, + "teams": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "tokens": { + "type": "integer", + "format": "int64" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "example": "2022-03-21T14:35:33Z" + } + } + }, "SetUserRolesCommand": { "type": "object", "properties": { @@ -15485,6 +16074,44 @@ } } }, + "TokenDTO": { + "type": "object", + "properties": { + "created": { + "type": "string", + "format": "date-time", + "example": "2022-03-23T10:31:02Z" + }, + "expiration": { + "type": "string", + "format": "date-time", + "example": "2022-03-23T10:31:02Z" + }, + "hasExpired": { + "type": "boolean", + "example": false + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "lastUsedAt": { + "type": "string", + "format": "date-time", + "example": "2022-03-23T10:31:02Z" + }, + "name": { + "type": "string", + "example": "grafana" + }, + "secondsUntilExpiration": { + "type": "number", + "format": "double", + "example": 0 + } + } + }, "TokenStatus": { "type": "integer", "format": "int64" @@ -15515,8 +16142,9 @@ "type": "string" }, "URL": { + "description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.", "type": "object", - "title": "URL is a custom URL type that allows validation at configuration load time.", + "title": "A URL represents a parsed URL (technically, a URI reference).", "properties": { "ForceQuery": { "type": "boolean" @@ -15870,6 +16498,21 @@ } } }, + "UpdateServiceAccountForm": { + "type": "object", + "properties": { + "isDisabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "role": { + "type": "string", + "enum": ["Viewer", "Editor", "Admin"] + } + } + }, "UpdateTeamCommand": { "type": "object", "properties": { @@ -16650,6 +17293,7 @@ } }, "receiver": { + "description": "Receiver receiver", "type": "object", "required": ["name"], "properties": { @@ -16859,7 +17503,22 @@ "createReportResponse": { "description": "", "schema": { - "type": "object" + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + } + } + } + }, + "createServiceAccountResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/ServiceAccountDTO" } }, "createSnapshotResponse": { @@ -16904,6 +17563,12 @@ } } }, + "createTokenResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/NewApiKeyResult" + } + }, "createUserResponse": { "description": "", "schema": { @@ -17273,7 +17938,7 @@ "getPermissionseResponse": { "description": "", "schema": { - "$ref": "#/definitions/AddPermissionDTO" + "$ref": "#/definitions/DataSourcePermissionsDTO" } }, "getPlaylistDashboardsResponse": { @@ -17491,6 +18156,12 @@ } } }, + "listTokensResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/TokenDTO" + } + }, "lookupAlertNotificationChannelsResponse": { "description": "", "schema": { @@ -17641,6 +18312,12 @@ "$ref": "#/definitions/ActiveUserStats" } }, + "retrieveServiceAccountResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/ServiceAccountDTO" + } + }, "searchOrgResponse": { "description": "", "schema": { @@ -17650,6 +18327,12 @@ } } }, + "searchOrgServiceAccountsWithPagingResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchServiceAccountsResult" + } + }, "searchPlaylistsResponse": { "description": "", "schema": { @@ -17727,6 +18410,27 @@ "$ref": "#/definitions/PlaylistDTO" } }, + "updateServiceAccountResponse": { + "description": "", + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + }, + "name": { + "type": "string" + }, + "serviceaccount": { + "$ref": "#/definitions/ServiceAccountProfileDTO" + } + } + } + }, "userResponse": { "description": "", "schema": { @@ -17836,6 +18540,10 @@ { "description": "Grafana Alerting Prometheus-compatible endpoints", "name": "prometheus" + }, + { + "description": "If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information.", + "name": "service_accounts" } ] } diff --git a/public/api-spec.json b/public/api-spec.json index e9275cccdff..1a3a181b650 100644 --- a/public/api-spec.json +++ b/public/api-spec.json @@ -119,6 +119,13 @@ "tags": ["access_control", "enterprise"], "summary": "Get all roles.", "operationId": "getAllRoles", + "parameters": [ + { + "type": "boolean", + "name": "delegatable", + "in": "query" + } + ], "responses": { "200": { "$ref": "#/responses/getAllRolesResponse" @@ -142,7 +149,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/CreateRoleWithPermissionsCommand" + "$ref": "#/definitions/CreateRoleForm" } } ], @@ -233,6 +240,16 @@ "summary": "Delete a custom role.", "operationId": "deleteCustomRole", "parameters": [ + { + "type": "boolean", + "name": "force", + "in": "query" + }, + { + "type": "boolean", + "name": "global", + "in": "query" + }, { "type": "string", "name": "roleUID", @@ -7073,6 +7090,328 @@ } } }, + "/serviceaccounts": { + "post": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:*`\n\nRequires basic authentication and that the authenticated user is a Grafana Admin.", + "tags": ["service_accounts"], + "summary": "Create service account", + "operationId": "createServiceAccount", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/CreateServiceAccountForm" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/createServiceAccountResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/search": { + "get": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:read` scope: `serviceaccounts:*`", + "tags": ["service_accounts"], + "summary": "Search service accounts with Paging", + "operationId": "searchOrgServiceAccountsWithPaging", + "parameters": [ + { + "type": "boolean", + "name": "Disabled", + "in": "query" + }, + { + "type": "boolean", + "name": "expiredTokens", + "in": "query" + }, + { + "type": "string", + "description": "It will return results where the query value is contained in one of the name.\nQuery values with spaces need to be URL encoded.", + "name": "query", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "description": "The default value is 1000.", + "name": "perpage", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "description": "The default value is 1.", + "name": "page", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/searchOrgServiceAccountsWithPagingResponse" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/{serviceAccountId}": { + "get": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:read` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Get single serviceaccount by Id", + "operationId": "retrieveServiceAccount", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/retrieveServiceAccountResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + }, + "delete": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:delete` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Delete service account", + "operationId": "deleteServiceAccount", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/okResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + }, + "patch": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Update service account", + "operationId": "updateServiceAccount", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/UpdateServiceAccountForm" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/updateServiceAccountResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/{serviceAccountId}/tokens": { + "get": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:read` scope: `global:serviceaccounts:id:1` (single service account)\n\nRequires basic authentication and that the authenticated user is a Grafana Admin.", + "tags": ["service_accounts"], + "summary": "Get service account tokens", + "operationId": "listTokens", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/listTokensResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + }, + "post": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account)", + "tags": ["service_accounts"], + "summary": "Create service account tokens", + "operationId": "createToken", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/AddServiceAccountTokenCommand" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/createTokenResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "409": { + "$ref": "#/responses/conflictError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, + "/serviceaccounts/{serviceAccountId}/tokens/{tokenId}": { + "delete": { + "description": "Required permissions (See note in the [introduction](https://grafana.com/docs/grafana/latest/developers/http_api/serviceaccount/#service-account-api) for an explanation):\naction: `serviceaccounts:write` scope: `serviceaccounts:id:1` (single service account)\n\nRequires basic authentication and that the authenticated user is a Grafana Admin.", + "tags": ["service_accounts"], + "summary": "Delete service account tokens", + "operationId": "deleteToken", + "parameters": [ + { + "type": "integer", + "format": "int64", + "name": "tokenId", + "in": "path", + "required": true + }, + { + "type": "integer", + "format": "int64", + "name": "serviceAccountId", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/okResponse" + }, + "400": { + "$ref": "#/responses/badRequestError" + }, + "401": { + "$ref": "#/responses/unauthorisedError" + }, + "403": { + "$ref": "#/responses/forbiddenError" + }, + "404": { + "$ref": "#/responses/notFoundError" + }, + "500": { + "$ref": "#/responses/internalServerError" + } + } + } + }, "/snapshot/shared-options": { "get": { "tags": ["snapshots"], @@ -7374,16 +7713,15 @@ "operationId": "removeTeamGroupApi", "parameters": [ { - "type": "integer", - "format": "int64", - "name": "teamId", + "type": "string", + "name": "groupId", "in": "path", "required": true }, { "type": "integer", "format": "int64", - "name": "groupId", + "name": "teamId", "in": "path", "required": true } @@ -8585,6 +8923,18 @@ } } }, + "AddServiceAccountTokenCommand": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "secondsToLive": { + "type": "integer", + "format": "int64" + } + } + }, "AddTeamMemberCommand": { "type": "object", "properties": { @@ -9427,7 +9777,7 @@ } } }, - "CreateRoleWithPermissionsCommand": { + "CreateRoleForm": { "type": "object", "properties": { "description": { @@ -9436,6 +9786,9 @@ "displayName": { "type": "string" }, + "global": { + "type": "boolean" + }, "group": { "type": "string" }, @@ -9460,6 +9813,24 @@ } } }, + "CreateServiceAccountForm": { + "type": "object", + "properties": { + "isDisabled": { + "type": "boolean", + "example": false + }, + "name": { + "type": "string", + "example": "grafana" + }, + "role": { + "type": "string", + "enum": ["Viewer", "Editor", "Admin"], + "example": "Admin" + } + } + }, "CreateTeamCommand": { "type": "object", "properties": { @@ -10099,6 +10470,83 @@ } } }, + "DataSourcePermissionRuleDTO": { + "type": "object", + "properties": { + "builtInRole": { + "type": "string" + }, + "created": { + "type": "string", + "format": "date-time" + }, + "datasourceId": { + "type": "integer", + "format": "int64" + }, + "id": { + "type": "integer", + "format": "int64" + }, + "isManaged": { + "type": "boolean" + }, + "permission": { + "$ref": "#/definitions/DsPermissionType" + }, + "permissionName": { + "type": "string" + }, + "team": { + "type": "string" + }, + "teamAvatarUrl": { + "type": "string" + }, + "teamEmail": { + "type": "string" + }, + "teamId": { + "type": "integer", + "format": "int64" + }, + "updated": { + "type": "string", + "format": "date-time" + }, + "userAvatarUrl": { + "type": "string" + }, + "userEmail": { + "type": "string" + }, + "userId": { + "type": "integer", + "format": "int64" + }, + "userLogin": { + "type": "string" + } + } + }, + "DataSourcePermissionsDTO": { + "type": "object", + "properties": { + "datasourceId": { + "type": "integer", + "format": "int64" + }, + "enabled": { + "type": "boolean" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/DataSourcePermissionRuleDTO" + } + } + } + }, "DeleteTokenCommand": { "type": "object", "properties": { @@ -11024,13 +11472,16 @@ "properties": { "id": { "type": "integer", - "format": "int64" + "format": "int64", + "example": 1 }, "key": { - "type": "string" + "type": "string", + "example": "glsa_yscW25imSKJIuav8zF37RZmnbiDvB05G_fcaaf58a" }, "name": { - "type": "string" + "type": "string", + "example": "grafana" } } }, @@ -11963,6 +12414,31 @@ } } }, + "SearchServiceAccountsResult": { + "description": "swagger: model", + "type": "object", + "properties": { + "page": { + "type": "integer", + "format": "int64" + }, + "perPage": { + "type": "integer", + "format": "int64" + }, + "serviceAccounts": { + "type": "array", + "items": { + "$ref": "#/definitions/ServiceAccountDTO" + } + }, + "totalCount": { + "description": "It can be used for pagination of the user list\nE.g. if totalCount is equal to 100 users and\nthe perpage parameter is set to 10 then there are 10 pages of users.", + "type": "integer", + "format": "int64" + } + } + }, "SearchTeamQueryResult": { "type": "object", "properties": { @@ -12009,6 +12485,119 @@ } } }, + "ServiceAccountDTO": { + "description": "swagger: model", + "type": "object", + "properties": { + "accessControl": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, + "example": { + "serviceaccounts:delete": true, + "serviceaccounts:read": true, + "serviceaccounts:write": true + } + }, + "avatarUrl": { + "type": "string", + "example": "/avatar/85ec38023d90823d3e5b43ef35646af9" + }, + "id": { + "type": "integer", + "format": "int64" + }, + "isDisabled": { + "type": "boolean", + "example": false + }, + "login": { + "type": "string", + "example": "sa-grafana" + }, + "name": { + "type": "string", + "example": "grafana" + }, + "orgId": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "role": { + "type": "string", + "example": "Viewer" + }, + "tokens": { + "type": "integer", + "format": "int64", + "example": 0 + } + } + }, + "ServiceAccountProfileDTO": { + "type": "object", + "properties": { + "accessControl": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "avatarUrl": { + "type": "string", + "example": "/avatar/8ea890a677d6a223c591a1beea6ea9d2" + }, + "createdAt": { + "type": "string", + "format": "date-time", + "example": "2022-03-21T14:35:33Z" + }, + "id": { + "type": "integer", + "format": "int64", + "example": 2 + }, + "isDisabled": { + "type": "boolean", + "example": false + }, + "login": { + "type": "string", + "example": "sa-grafana" + }, + "name": { + "type": "string", + "example": "test" + }, + "orgId": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "role": { + "type": "string", + "example": "Editor" + }, + "teams": { + "type": "array", + "items": { + "type": "string" + }, + "example": [] + }, + "tokens": { + "type": "integer", + "format": "int64" + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "example": "2022-03-21T14:35:33Z" + } + } + }, "SetUserRolesCommand": { "type": "object", "properties": { @@ -12408,6 +12997,44 @@ } } }, + "TokenDTO": { + "type": "object", + "properties": { + "created": { + "type": "string", + "format": "date-time", + "example": "2022-03-23T10:31:02Z" + }, + "expiration": { + "type": "string", + "format": "date-time", + "example": "2022-03-23T10:31:02Z" + }, + "hasExpired": { + "type": "boolean", + "example": false + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "lastUsedAt": { + "type": "string", + "format": "date-time", + "example": "2022-03-23T10:31:02Z" + }, + "name": { + "type": "string", + "example": "grafana" + }, + "secondsUntilExpiration": { + "type": "number", + "format": "double", + "example": 0 + } + } + }, "TokenStatus": { "type": "integer", "format": "int64" @@ -12757,6 +13384,21 @@ } } }, + "UpdateServiceAccountForm": { + "type": "object", + "properties": { + "isDisabled": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "role": { + "type": "string", + "enum": ["Viewer", "Editor", "Admin"] + } + } + }, "UpdateTeamCommand": { "type": "object", "properties": { @@ -13164,7 +13806,22 @@ "createReportResponse": { "description": "", "schema": { - "type": "object" + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + } + } + } + }, + "createServiceAccountResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/ServiceAccountDTO" } }, "createSnapshotResponse": { @@ -13209,6 +13866,12 @@ } } }, + "createTokenResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/NewApiKeyResult" + } + }, "createUserResponse": { "description": "", "schema": { @@ -13578,7 +14241,7 @@ "getPermissionseResponse": { "description": "", "schema": { - "$ref": "#/definitions/AddPermissionDTO" + "$ref": "#/definitions/DataSourcePermissionsDTO" } }, "getPlaylistDashboardsResponse": { @@ -13796,6 +14459,12 @@ } } }, + "listTokensResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/TokenDTO" + } + }, "lookupAlertNotificationChannelsResponse": { "description": "", "schema": { @@ -13946,6 +14615,12 @@ "$ref": "#/definitions/ActiveUserStats" } }, + "retrieveServiceAccountResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/ServiceAccountDTO" + } + }, "searchOrgResponse": { "description": "", "schema": { @@ -13955,6 +14630,12 @@ } } }, + "searchOrgServiceAccountsWithPagingResponse": { + "description": "", + "schema": { + "$ref": "#/definitions/SearchServiceAccountsResult" + } + }, "searchPlaylistsResponse": { "description": "", "schema": { @@ -14032,6 +14713,27 @@ "$ref": "#/definitions/PlaylistDTO" } }, + "updateServiceAccountResponse": { + "description": "", + "schema": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "message": { + "type": "string" + }, + "name": { + "type": "string" + }, + "serviceaccount": { + "$ref": "#/definitions/ServiceAccountProfileDTO" + } + } + } + }, "userResponse": { "description": "", "schema": { @@ -14141,6 +14843,10 @@ { "description": "Grafana Alerting Prometheus-compatible endpoints", "name": "prometheus" + }, + { + "description": "If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information.", + "name": "service_accounts" } ] }