Plugins: Rename externalServiceAuthentication to iam (#78686)

Plugins: Rename externalServiceAuthentication to iam
This commit is contained in:
Gabriel MABILLE 2023-12-04 13:14:21 +01:00 committed by GitHub
parent 0825b63b79
commit 5b70130e6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 53 additions and 66 deletions

View File

@ -478,51 +478,37 @@
"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": {
"iam": {
"type": "object",
"description": "Oauth App Service Registration.",
"description": "Identity and Access Management.",
"properties": {
"permissions": {
"type": "array",
"description": "Permissions are the permissions that the plugin needs its associated service account to have",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"action": {
"type": "string"
},
"scope": {
"type": "string"
}
}
}
},
"impersonation": {
"type": "object",
"description": "Impersonation describes the permissions that the external service will have on behalf of the user.",
"description": "Impersonation describes the permissions that the plugin will be restricted to when acting 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",
"description": "Permissions are the permissions that the plugin 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,

View File

@ -519,13 +519,13 @@ func (hs *HTTPServer) hasPluginRequestedPermissions(c *contextmodel.ReqContext,
}
// No registration => Early return
if plugin.JSONData.ExternalServiceRegistration == nil || len(plugin.JSONData.ExternalServiceRegistration.Permissions) == 0 {
if plugin.JSONData.IAM == nil || len(plugin.JSONData.IAM.Permissions) == 0 {
hs.log.Debug("plugin did not request permissions on Grafana", "pluginID", pluginID)
return
}
hs.log.Debug("check installer's permissions, plugin wants to register an external service")
evaluator := evalAllPermissions(plugin.JSONData.ExternalServiceRegistration.Permissions)
evaluator := evalAllPermissions(plugin.JSONData.IAM.Permissions)
hasAccess := ac.HasGlobalAccess(hs.AccessControl, hs.accesscontrolService, c)
if hs.Cfg.RBACSingleOrganization {
// In a single organization setup, no need for a global check

View File

@ -650,7 +650,7 @@ func TestHTTPServer_hasPluginRequestedPermissions(t *testing.T) {
pluginReg := pluginstore.Plugin{
JSONData: plugins.JSONData{
ID: "grafana-test-app",
ExternalServiceRegistration: &plugindef.ExternalServiceRegistration{
IAM: &plugindef.IAM{
Permissions: []plugindef.Permission{{Action: ac.ActionUsersRead, Scope: newStr(ac.ScopeUsersAll)}, {Action: ac.ActionUsersCreate}},
},
},

View File

@ -14,6 +14,6 @@ type ExternalService struct {
type ExternalServiceRegistry interface {
HasExternalService(ctx context.Context, pluginID string) (bool, error)
RegisterExternalService(ctx context.Context, pluginID string, pType plugindef.Type, svc *plugindef.ExternalServiceRegistration) (*ExternalService, error)
RegisterExternalService(ctx context.Context, pluginID string, pType plugindef.Type, svc *plugindef.IAM) (*ExternalService, error)
RemoveExternalService(ctx context.Context, pluginID string) error
}

View File

@ -529,8 +529,8 @@ func TestInitializer_authEnvVars(t *testing.T) {
t.Run("backend datasource with auth registration", func(t *testing.T) {
p := &plugins.Plugin{
JSONData: plugins.JSONData{
ID: "test",
ExternalServiceRegistration: &plugindef.ExternalServiceRegistration{},
ID: "test",
IAM: &plugindef.IAM{},
},
ExternalService: &auth.ExternalService{
ClientID: "clientID",

View File

@ -441,7 +441,7 @@ func (f *FakeAuthService) HasExternalService(ctx context.Context, pluginID strin
return f.Result != nil, nil
}
func (f *FakeAuthService) RegisterExternalService(ctx context.Context, pluginID string, pType plugindef.Type, svc *plugindef.ExternalServiceRegistration) (*auth.ExternalService, error) {
func (f *FakeAuthService) RegisterExternalService(ctx context.Context, pluginID string, pType plugindef.Type, svc *plugindef.IAM) (*auth.ExternalService, error) {
return f.Result, nil
}

View File

@ -17,7 +17,7 @@
"updated": "2023-08-03",
"version": "1.0.0"
},
"externalServiceRegistration": {
"iam": {
"permissions" : [
{
"action": "read",

View File

@ -17,7 +17,7 @@
"updated": "2023-08-03",
"version": "1.0.0"
},
"externalServiceRegistration": {
"iam": {
"impersonation": {
"groups" : true,
"permissions" : [

View File

@ -410,12 +410,13 @@ schemas: [{
params: [string]: string
}
// External service registration information
externalServiceRegistration: #ExternalServiceRegistration
// Identity and Access Management information.
// Allows the plugin to define the permissions it requires to have on Grafana.
iam: #IAM
// ExternalServiceRegistration allows the service to get a service account token
// IAM allows the plugin to get a service account with tailored permissions and a token
// (or to use the client_credentials grant if the token provider is the OAuth2 Server)
#ExternalServiceRegistration: {
#IAM: {
// Permissions are the permissions that the external service needs its associated service account to have.
permissions?: [...#Permission]

View File

@ -122,15 +122,6 @@ type Dependency struct {
// DependencyType defines model for Dependency.Type.
type DependencyType string
// ExternalServiceRegistration allows the service to get a service account token
// (or to use the client_credentials grant if the token provider is the OAuth2 Server)
type ExternalServiceRegistration struct {
Impersonation *Impersonation `json:"impersonation,omitempty"`
// Permissions are the permissions that the external service needs its associated service account to have.
Permissions []Permission `json:"permissions,omitempty"`
}
// Header describes an HTTP header that is forwarded with a proxied request for
// a plugin route.
type Header struct {
@ -138,6 +129,15 @@ type Header struct {
Name string `json:"name"`
}
// IAM allows the plugin to get a service account with tailored permissions and a token
// (or to use the client_credentials grant if the token provider is the OAuth2 Server)
type IAM struct {
Impersonation *Impersonation `json:"impersonation,omitempty"`
// Permissions are the permissions that the external service needs its associated service account to have.
Permissions []Permission `json:"permissions,omitempty"`
}
// Impersonation defines model for Impersonation.
type Impersonation struct {
// Groups allows the service to list the impersonated user's teams.
@ -315,14 +315,14 @@ type PluginDef struct {
// https://golang.org/doc/install/source#environment.
Executable *string `json:"executable,omitempty"`
// ExternalServiceRegistration allows the service to get a service account token
// (or to use the client_credentials grant if the token provider is the OAuth2 Server)
ExternalServiceRegistration ExternalServiceRegistration `json:"externalServiceRegistration"`
// [internal only] Excludes the plugin from listings in Grafana's UI. Only
// allowed for `builtIn` plugins.
HideFromList bool `json:"hideFromList"`
// IAM allows the plugin to get a service account with tailored permissions and a token
// (or to use the client_credentials grant if the token provider is the OAuth2 Server)
Iam IAM `json:"iam"`
// Unique name of the plugin. If the plugin is published on
// grafana.com, then the plugin `id` has to follow the naming
// conventions.

View File

@ -118,7 +118,7 @@ type JSONData struct {
Executable string `json:"executable,omitempty"`
// App Service Auth Registration
ExternalServiceRegistration *plugindef.ExternalServiceRegistration `json:"externalServiceRegistration,omitempty"`
IAM *plugindef.IAM `json:"iam,omitempty"`
}
func ReadPluginJSON(reader io.Reader) (JSONData, error) {

View File

@ -535,7 +535,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
GrafanaVersion: "*",
Plugins: []plugins.Dependency{},
},
ExternalServiceRegistration: &plugindef.ExternalServiceRegistration{
IAM: &plugindef.IAM{
Impersonation: &plugindef.Impersonation{
Groups: boolPtr(true),
Permissions: []plugindef.Permission{
@ -636,7 +636,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
GrafanaVersion: "*",
Plugins: []plugins.Dependency{},
},
ExternalServiceRegistration: &plugindef.ExternalServiceRegistration{
IAM: &plugindef.IAM{
Permissions: []plugindef.Permission{
{
Action: "read",

View File

@ -39,8 +39,8 @@ func newExternalServiceRegistration(cfg *config.Cfg, serviceRegistry auth.Extern
// Register registers the external service with the external service registry, if the feature is enabled.
func (r *ExternalServiceRegistration) Register(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
if p.ExternalServiceRegistration != nil {
s, err := r.externalServiceRegistry.RegisterExternalService(ctx, p.ID, plugindef.Type(p.Type), p.ExternalServiceRegistration)
if p.IAM != nil {
s, err := r.externalServiceRegistry.RegisterExternalService(ctx, p.ID, plugindef.Type(p.Type), p.IAM)
if err != nil {
r.log.Error("Could not register an external service. Initialization skipped", "pluginId", p.ID, "error", err)
return nil, err

View File

@ -41,7 +41,7 @@ func (s *Service) HasExternalService(ctx context.Context, pluginID string) (bool
}
// RegisterExternalService is a simplified wrapper around SaveExternalService for the plugin use case.
func (s *Service) RegisterExternalService(ctx context.Context, pluginID string, pType plugindef.Type, svc *plugindef.ExternalServiceRegistration) (*auth.ExternalService, error) {
func (s *Service) RegisterExternalService(ctx context.Context, pluginID string, pType plugindef.Type, svc *plugindef.IAM) (*auth.ExternalService, error) {
if !s.featureEnabled {
s.log.Warn("Skipping External Service Registration. The feature is behind a feature toggle and needs to be enabled.")
return nil, nil