Chore: Update plugin schema with service registration info (#70692)

This commit is contained in:
Andres Martinez Gotor 2023-06-27 08:47:25 +02:00 committed by GitHub
parent e03f61fe26
commit 025465e611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 155 additions and 29 deletions

View File

@ -477,6 +477,68 @@
"tracing": {
"type": "boolean",
"description": "For data source plugins, if the plugin supports tracing. Used for example to link logs (e.g. Loki logs) with tracing plugins."
},
"externalServiceRegistration": {
"type": "object",
"description": "Oauth App Service Registration.",
"properties": {
"impersonation": {
"type": "object",
"description": "Impersonation describes the permissions that the external service will have on behalf of the user.",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enabled allows the service to request access tokens to impersonate users using the jwtbearer grant"
},
"groups": {
"type": "boolean",
"description": "Groups allows the service to list the impersonated user's teams."
},
"permissions": {
"type": "array",
"description": "Permissions are the permissions that the external service needs when impersonating a user. The intersection of this set with the impersonated user's permission guarantees that the client will not gain more privileges than the impersonated user has.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"action": {
"type": "string"
},
"scope": {
"type": "string"
}
}
}
}
}
},
"self": {
"type": "object",
"description": "Self describes the permissions that the external service will have on behalf of itself",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enabled allows the service to request access tokens for itself using the client_credentials grant"
},
"permissions": {
"type": "array",
"description": "Permissions are the permissions that the external service needs its associated service account to have",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"action": {
"type": "string"
},
"scope": {
"type": "string"
}
}
}
}
}
}
}
}
}
}

View File

@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
"github.com/grafana/grafana/pkg/plugins/oauth"
"github.com/grafana/grafana/pkg/plugins/plugindef"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
)
@ -312,7 +313,7 @@ func TestInitializer_oauthEnvVars(t *testing.T) {
p := &plugins.Plugin{
JSONData: plugins.JSONData{
ID: "test",
ExternalServiceRegistration: &oauth.ExternalServiceRegistration{},
ExternalServiceRegistration: &plugindef.ExternalServiceRegistration{},
},
ExternalService: &oauth.ExternalService{
ClientID: "clientID",

View File

@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/oauth"
"github.com/grafana/grafana/pkg/plugins/plugindef"
"github.com/grafana/grafana/pkg/plugins/repo"
"github.com/grafana/grafana/pkg/plugins/storage"
)
@ -428,6 +429,6 @@ type FakeOauthService struct {
Result *oauth.ExternalService
}
func (f *FakeOauthService) RegisterExternalService(ctx context.Context, name string, svc *oauth.ExternalServiceRegistration) (*oauth.ExternalService, error) {
func (f *FakeOauthService) RegisterExternalService(ctx context.Context, name string, svc *plugindef.ExternalServiceRegistration) (*oauth.ExternalService, error) {
return f.Result, nil
}

View File

@ -3,29 +3,9 @@ package oauth
import (
"context"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/plugins/plugindef"
)
// SelfCfg is a subset of oauthserver.SelfCfg making some fields optional
type SelfCfg struct {
Enabled *bool `json:"enabled,omitempty"`
Permissions []accesscontrol.Permission `json:"permissions,omitempty"`
}
// ImpersonationCfg is a subset of oauthserver.ImpersonationCfg making some fields optional
type ImpersonationCfg struct {
Enabled *bool `json:"enabled,omitempty"`
Groups *bool `json:"groups,omitempty"`
Permissions []accesscontrol.Permission `json:"permissions,omitempty"`
}
// PluginExternalServiceRegistration is a subset of oauthserver.ExternalServiceRegistration
// simplified for the plugin use case.
type ExternalServiceRegistration struct {
Impersonation *ImpersonationCfg `json:"impersonation,omitempty"`
Self *SelfCfg `json:"self,omitempty"`
}
type ExternalService struct {
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
@ -33,5 +13,5 @@ type ExternalService struct {
}
type ExternalServiceRegistry interface {
RegisterExternalService(ctx context.Context, name string, svc *ExternalServiceRegistration) (*ExternalService, error)
RegisterExternalService(ctx context.Context, name string, svc *plugindef.ExternalServiceRegistration) (*ExternalService, error)
}

View File

@ -405,6 +405,37 @@ schemas: [{
// Parameters for the JWT token authentication request.
params: [string]: string
}
// External service registration information
externalServiceRegistration: #ExternalServiceRegistration
#ExternalServiceRegistration: {
// Impersonation describes the permissions that the external service will have on behalf of the user
impersonation?: #Impersonation
// Self describes the permissions that the external service will have on behalf of itself
self?: #Self
}
#Impersonation: {
// Enabled allows the service to request access tokens to impersonate users using the jwtbearer grant
// Defaults to true.
enabled?: bool
// Groups allows the service to list the impersonated user's teams.
// Defaults to true.
groups?: bool
// Permissions are the permissions that the external service needs when impersonating a user.
// The intersection of this set with the impersonated user's permission guarantees that the client will not
// gain more privileges than the impersonated user has.
permissions?: [...#Permission]
}
#Self: {
// Enabled allows the service to request access tokens for itself using the client_credentials grant
// Defaults to true.
enabled?: bool
// Permissions are the permissions that the external service needs its associated service account to have.
permissions?: [...#Permission]
}
}
}]
lenses: []

View File

@ -122,6 +122,12 @@ type Dependency struct {
// DependencyType defines model for Dependency.Type.
type DependencyType string
// ExternalServiceRegistration defines model for ExternalServiceRegistration.
type ExternalServiceRegistration struct {
Impersonation *Impersonation `json:"impersonation,omitempty"`
Self *Self `json:"self,omitempty"`
}
// Header describes an HTTP header that is forwarded with a proxied request for
// a plugin route.
type Header struct {
@ -129,6 +135,22 @@ type Header struct {
Name string `json:"name"`
}
// Impersonation defines model for Impersonation.
type Impersonation struct {
// Enabled allows the service to request access tokens to impersonate users using the jwtbearer grant
// Defaults to true.
Enabled *bool `json:"enabled,omitempty"`
// Groups allows the service to list the impersonated user's teams.
// Defaults to true.
Groups *bool `json:"groups,omitempty"`
// Permissions are the permissions that the external service needs when impersonating a user.
// The intersection of this set with the impersonated user's permission guarantees that the client will not
// gain more privileges than the impersonated user has.
Permissions []Permission `json:"permissions,omitempty"`
}
// A resource to be included in a plugin.
type Include struct {
// RBAC action the user must have to access the route
@ -288,7 +310,8 @@ type PluginDef struct {
// $GOARCH><.exe for Windows>`, e.g. `plugin_linux_amd64`.
// Combination of $GOOS and $GOARCH can be found here:
// https://golang.org/doc/install/source#environment.
Executable *string `json:"executable,omitempty"`
Executable *string `json:"executable,omitempty"`
ExternalServiceRegistration ExternalServiceRegistration `json:"externalServiceRegistration"`
// [internal only] Excludes the plugin from listings in Grafana's UI. Only
// allowed for `builtIn` plugins.
@ -445,6 +468,16 @@ type Route struct {
UrlParams []URLParam `json:"urlParams,omitempty"`
}
// Self defines model for Self.
type Self struct {
// Enabled allows the service to request access tokens for itself using the client_credentials grant
// Defaults to true.
Enabled *bool `json:"enabled,omitempty"`
// Permissions are the permissions that the external service needs its associated service account to have.
Permissions []Permission `json:"permissions,omitempty"`
}
// TODO docs
type TokenAuth struct {
// Parameters for the token authentication request.

View File

@ -18,6 +18,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/plugins/oauth"
"github.com/grafana/grafana/pkg/plugins/plugindef"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util"
)
@ -155,7 +156,7 @@ type JSONData struct {
Executable string `json:"executable,omitempty"`
// Oauth App Service Registration
ExternalServiceRegistration *oauth.ExternalServiceRegistration `json:"externalServiceRegistration,omitempty"`
ExternalServiceRegistration *plugindef.ExternalServiceRegistration `json:"externalServiceRegistration,omitempty"`
}
func ReadPluginJSON(reader io.Reader) (JSONData, error) {

View File

@ -4,6 +4,8 @@ import (
"context"
"github.com/grafana/grafana/pkg/plugins/oauth"
"github.com/grafana/grafana/pkg/plugins/plugindef"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/oauthserver"
)
@ -19,10 +21,10 @@ func ProvideService(os oauthserver.OAuth2Server) *Service {
}
// RegisterExternalService is a simplified wrapper around SaveExternalService for the plugin use case.
func (s *Service) RegisterExternalService(ctx context.Context, svcName string, svc *oauth.ExternalServiceRegistration) (*oauth.ExternalService, error) {
func (s *Service) RegisterExternalService(ctx context.Context, svcName string, svc *plugindef.ExternalServiceRegistration) (*oauth.ExternalService, error) {
impersonation := oauthserver.ImpersonationCfg{}
if svc.Impersonation != nil {
impersonation.Permissions = svc.Impersonation.Permissions
impersonation.Permissions = toAccessControlPermissions(svc.Impersonation.Permissions)
if svc.Impersonation.Enabled != nil {
impersonation.Enabled = *svc.Impersonation.Enabled
} else {
@ -37,7 +39,7 @@ func (s *Service) RegisterExternalService(ctx context.Context, svcName string, s
self := oauthserver.SelfCfg{}
if svc.Self != nil {
self.Permissions = svc.Self.Permissions
self.Permissions = toAccessControlPermissions(svc.Self.Permissions)
if svc.Self.Enabled != nil {
self.Enabled = *svc.Self.Enabled
} else {
@ -60,3 +62,18 @@ func (s *Service) RegisterExternalService(ctx context.Context, svcName string, s
PrivateKey: extSvc.KeyResult.PrivatePem,
}, nil
}
func toAccessControlPermissions(ps []plugindef.Permission) []accesscontrol.Permission {
res := make([]accesscontrol.Permission, 0, len(ps))
for _, p := range ps {
scope := ""
if p.Scope != nil {
scope = *p.Scope
}
res = append(res, accesscontrol.Permission{
Action: p.Action,
Scope: scope,
})
}
return res
}