mirror of https://github.com/grafana/grafana.git
				
				
				
			
							parent
							
								
									1ec68b6917
								
							
						
					
					
						commit
						a82d01214d
					
				
							
								
								
									
										4
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										4
									
								
								go.mod
								
								
								
								
							|  | @ -73,8 +73,8 @@ require ( | |||
| 	github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group | ||||
| 	github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad | ||||
| 	github.com/grafana/alerting v0.0.0-20241010165806-807ddf183724 // @grafana/alerting-backend | ||||
| 	github.com/grafana/authlib v0.0.0-20241014135010-3e1f37f75699 // @grafana/identity-access-team | ||||
| 	github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0 // @grafana/identity-access-team | ||||
| 	github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240 // @grafana/identity-access-team | ||||
| 	github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e // @grafana/identity-access-team | ||||
| 	github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad | ||||
| 	github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code | ||||
| 	github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics | ||||
|  |  | |||
							
								
								
									
										8
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										8
									
								
								go.sum
								
								
								
								
							|  | @ -2244,10 +2244,10 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm | |||
| github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/grafana/alerting v0.0.0-20241010165806-807ddf183724 h1:u+ZM5TLkdeEoSWXgYWxc4XRfPHhXpR63MyHXJxbBLrc= | ||||
| github.com/grafana/alerting v0.0.0-20241010165806-807ddf183724/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= | ||||
| github.com/grafana/authlib v0.0.0-20241014135010-3e1f37f75699 h1:+xSpRpQPhMXAE9z68u0zMzzIa78jy1UqFb4tMJczFNc= | ||||
| github.com/grafana/authlib v0.0.0-20241014135010-3e1f37f75699/go.mod h1:fhuI+ulquEIVcLsbwPml9JapWQzg8EYBp29HteO62DM= | ||||
| github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0 h1:XT/WvQCWVVOvXRJy0SCQHkhxXFHNRJ3+jzhW5PutEk8= | ||||
| github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= | ||||
| github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240 h1:bBn6sCbBjxjYlvs5JAIGHQSOs8xbDEBWbezxarA/DDo= | ||||
| github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240/go.mod h1:RKqhn8E5PY2k5Xo6X8FHFgP45/qt9qqfAY7YYJ2mtB8= | ||||
| github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e h1:I0sSXcqdt/ttiOJ/BVhpfa2q/xAyWSweQwaypGmvLss= | ||||
| github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= | ||||
| github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= | ||||
| github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= | ||||
| github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= | ||||
|  |  | |||
|  | @ -3,8 +3,8 @@ module github.com/grafana/grafana/pkg/apimachinery | |||
| go 1.23.1 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/grafana/authlib v0.0.0-20241014135010-3e1f37f75699 // @grafana/identity-access-team | ||||
| 	github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0 // @grafana/identity-access-team | ||||
| 	github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240 // @grafana/identity-access-team | ||||
| 	github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e // @grafana/identity-access-team | ||||
| 	github.com/stretchr/testify v1.9.0 | ||||
| 	k8s.io/apimachinery v0.31.1 | ||||
| 	k8s.io/apiserver v0.31.1 | ||||
|  |  | |||
|  | @ -28,10 +28,10 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN | |||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | ||||
| github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/grafana/authlib v0.0.0-20241014135010-3e1f37f75699 h1:+xSpRpQPhMXAE9z68u0zMzzIa78jy1UqFb4tMJczFNc= | ||||
| github.com/grafana/authlib v0.0.0-20241014135010-3e1f37f75699/go.mod h1:fhuI+ulquEIVcLsbwPml9JapWQzg8EYBp29HteO62DM= | ||||
| github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0 h1:XT/WvQCWVVOvXRJy0SCQHkhxXFHNRJ3+jzhW5PutEk8= | ||||
| github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= | ||||
| github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240 h1:bBn6sCbBjxjYlvs5JAIGHQSOs8xbDEBWbezxarA/DDo= | ||||
| github.com/grafana/authlib v0.0.0-20241018103850-afc1195d8240/go.mod h1:RKqhn8E5PY2k5Xo6X8FHFgP45/qt9qqfAY7YYJ2mtB8= | ||||
| github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e h1:I0sSXcqdt/ttiOJ/BVhpfa2q/xAyWSweQwaypGmvLss= | ||||
| github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= | ||||
| github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= | ||||
| github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ go 1.23.1 | |||
| 
 | ||||
| require ( | ||||
| 	github.com/google/go-cmp v0.6.0 | ||||
| 	github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0 | ||||
| 	github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e | ||||
| 	github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 | ||||
| 	github.com/prometheus/client_golang v1.20.4 | ||||
| 	github.com/stretchr/testify v1.9.0 | ||||
|  |  | |||
|  | @ -78,8 +78,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | |||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= | ||||
| github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0 h1:XT/WvQCWVVOvXRJy0SCQHkhxXFHNRJ3+jzhW5PutEk8= | ||||
| github.com/grafana/authlib/claims v0.0.0-20240926100702-4aee62663da0/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= | ||||
| github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e h1:I0sSXcqdt/ttiOJ/BVhpfa2q/xAyWSweQwaypGmvLss= | ||||
| github.com/grafana/authlib/claims v0.0.0-20241018085709-130ad686d80e/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= | ||||
| github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 h1:ItDcDxUjVLPKja+hogpqgW/kj8LxUL2qscelXIsN1Bs= | ||||
| github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1/go.mod h1:DkxMin+qOh1Fgkxfbt+CUfBqqsCQJMG9op8Os/irBPA= | ||||
| github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"context" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"k8s.io/apiserver/pkg/authorization/authorizer" | ||||
| 
 | ||||
|  | @ -14,7 +15,7 @@ import ( | |||
| 	gfauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" | ||||
| ) | ||||
| 
 | ||||
| func newLegacyAuthorizer(ac accesscontrol.AccessControl, store legacy.LegacyIdentityStore) (authorizer.Authorizer, claims.AccessClient) { | ||||
| func newLegacyAuthorizer(ac accesscontrol.AccessControl, store legacy.LegacyIdentityStore) (authorizer.Authorizer, authz.AccessClient) { | ||||
| 	client := accesscontrol.NewLegacyAccessClient( | ||||
| 		ac, | ||||
| 		accesscontrol.ResourceAuthorizerOptions{ | ||||
|  |  | |||
|  | @ -4,9 +4,9 @@ import ( | |||
| 	"context" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/utils" | ||||
| 	iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" | ||||
| 	"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" | ||||
| 	"github.com/grafana/grafana/pkg/services/team" | ||||
|  | @ -48,7 +48,7 @@ type ListFunc[T Resource] func(ctx context.Context, ns claims.NamespaceInfo, p P | |||
| func List[T Resource]( | ||||
| 	ctx context.Context, | ||||
| 	resourceName string, | ||||
| 	ac claims.AccessClient, | ||||
| 	ac authz.AccessClient, | ||||
| 	p Pagination, | ||||
| 	fn ListFunc[T], | ||||
| ) (*ListResponse[T], error) { | ||||
|  | @ -62,11 +62,10 @@ func List[T Resource]( | |||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	check := func(_ string, _ string) bool { return true } | ||||
| 	check := func(_, _, _ string) bool { return true } | ||||
| 	if ac != nil { | ||||
| 		var err error | ||||
| 		check, err = ac.Compile(ctx, ident, claims.AccessRequest{ | ||||
| 			Verb:      utils.VerbList, | ||||
| 		check, err = ac.Compile(ctx, ident, authz.ListRequest{ | ||||
| 			Resource:  resourceName, | ||||
| 			Namespace: ns.Value, | ||||
| 		}) | ||||
|  | @ -84,7 +83,7 @@ func List[T Resource]( | |||
| 	} | ||||
| 
 | ||||
| 	for _, item := range first.Items { | ||||
| 		if !check(ns.Value, item.AuthID()) { | ||||
| 		if !check(ns.Value, item.AuthID(), "") { | ||||
| 			continue | ||||
| 		} | ||||
| 		res.Items = append(res.Items, item) | ||||
|  | @ -107,7 +106,7 @@ outer: | |||
| 				break outer | ||||
| 			} | ||||
| 
 | ||||
| 			if !check(ns.Value, item.AuthID()) { | ||||
| 			if !check(ns.Value, item.AuthID(), "") { | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
|  |  | |||
|  | @ -40,11 +40,6 @@ func NewLegacySQLStores(sql legacysql.LegacyDatabaseProvider) LegacyIdentityStor | |||
| 
 | ||||
| type legacySQLStore struct { | ||||
| 	sql legacysql.LegacyDatabaseProvider | ||||
| 	ac  claims.AccessClient | ||||
| } | ||||
| 
 | ||||
| func (s *legacySQLStore) WithAccessClient(ac claims.AccessClient) { | ||||
| 	s.ac = ac | ||||
| } | ||||
| 
 | ||||
| // Templates setup.
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ import ( | |||
| 	genericapiserver "k8s.io/apiserver/pkg/server" | ||||
| 	common "k8s.io/kube-openapi/pkg/common" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
| 	iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" | ||||
| 	"github.com/grafana/grafana/pkg/infra/db" | ||||
|  | @ -32,7 +32,7 @@ var _ builder.APIGroupBuilder = (*IdentityAccessManagementAPIBuilder)(nil) | |||
| type IdentityAccessManagementAPIBuilder struct { | ||||
| 	store        legacy.LegacyIdentityStore | ||||
| 	authorizer   authorizer.Authorizer | ||||
| 	accessClient claims.AccessClient | ||||
| 	accessClient authz.AccessClient | ||||
| 
 | ||||
| 	// Not set for multi-tenant deployment for now
 | ||||
| 	sso ssosettings.Service | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import ( | |||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apiserver/pkg/registry/rest" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/utils" | ||||
| 	iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" | ||||
|  | @ -28,13 +29,13 @@ var ( | |||
| 
 | ||||
| var resource = iamv0.ServiceAccountResourceInfo | ||||
| 
 | ||||
| func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient) *LegacyStore { | ||||
| func NewLegacyStore(store legacy.LegacyIdentityStore, ac authz.AccessClient) *LegacyStore { | ||||
| 	return &LegacyStore{store, ac} | ||||
| } | ||||
| 
 | ||||
| type LegacyStore struct { | ||||
| 	store legacy.LegacyIdentityStore | ||||
| 	ac    claims.AccessClient | ||||
| 	ac    authz.AccessClient | ||||
| } | ||||
| 
 | ||||
| func (s *LegacyStore) New() runtime.Object { | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import ( | |||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apiserver/pkg/registry/rest" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/utils" | ||||
| 	iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" | ||||
|  | @ -29,13 +30,13 @@ var ( | |||
| 
 | ||||
| var resource = iamv0.TeamResourceInfo | ||||
| 
 | ||||
| func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient) *LegacyStore { | ||||
| func NewLegacyStore(store legacy.LegacyIdentityStore, ac authz.AccessClient) *LegacyStore { | ||||
| 	return &LegacyStore{store, ac} | ||||
| } | ||||
| 
 | ||||
| type LegacyStore struct { | ||||
| 	store legacy.LegacyIdentityStore | ||||
| 	ac    claims.AccessClient | ||||
| 	ac    authz.AccessClient | ||||
| } | ||||
| 
 | ||||
| func (s *LegacyStore) New() runtime.Object { | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ import ( | |||
| 	"k8s.io/apimachinery/pkg/runtime" | ||||
| 	"k8s.io/apiserver/pkg/registry/rest" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/utils" | ||||
| 	iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" | ||||
|  | @ -29,13 +30,13 @@ var ( | |||
| 
 | ||||
| var resource = iamv0.UserResourceInfo | ||||
| 
 | ||||
| func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient) *LegacyStore { | ||||
| func NewLegacyStore(store legacy.LegacyIdentityStore, ac authz.AccessClient) *LegacyStore { | ||||
| 	return &LegacyStore{store, ac} | ||||
| } | ||||
| 
 | ||||
| type LegacyStore struct { | ||||
| 	store legacy.LegacyIdentityStore | ||||
| 	ac    claims.AccessClient | ||||
| 	ac    authz.AccessClient | ||||
| } | ||||
| 
 | ||||
| func (s *LegacyStore) New() runtime.Object { | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import ( | |||
| 	"errors" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/utils" | ||||
|  | @ -44,7 +45,7 @@ type ResourceAuthorizerOptions struct { | |||
| 	Resolver ResourceResolver | ||||
| } | ||||
| 
 | ||||
| var _ claims.AccessClient = (*LegacyAccessClient)(nil) | ||||
| var _ authz.AccessClient = (*LegacyAccessClient)(nil) | ||||
| 
 | ||||
| func NewLegacyAccessClient(ac AccessControl, opts ...ResourceAuthorizerOptions) *LegacyAccessClient { | ||||
| 	stored := map[string]ResourceAuthorizerOptions{} | ||||
|  | @ -82,35 +83,34 @@ type LegacyAccessClient struct { | |||
| 	opts map[string]ResourceAuthorizerOptions | ||||
| } | ||||
| 
 | ||||
| // HasAccess implements claims.AccessClient.
 | ||||
| func (c *LegacyAccessClient) HasAccess(ctx context.Context, id claims.AuthInfo, req claims.AccessRequest) (bool, error) { | ||||
| func (c *LegacyAccessClient) Check(ctx context.Context, id claims.AuthInfo, req authz.CheckRequest) (authz.CheckResponse, error) { | ||||
| 	ident, ok := id.(identity.Requester) | ||||
| 	if !ok { | ||||
| 		return false, errors.New("expected identity.Requester for legacy access control") | ||||
| 		return authz.CheckResponse{}, errors.New("expected identity.Requester for legacy access control") | ||||
| 	} | ||||
| 
 | ||||
| 	opts, ok := c.opts[req.Resource] | ||||
| 	if !ok { | ||||
| 		// For now we fallback to grafana admin if no options are found for resource.
 | ||||
| 		if ident.GetIsGrafanaAdmin() { | ||||
| 			return true, nil | ||||
| 			return authz.CheckResponse{Allowed: true}, nil | ||||
| 		} | ||||
| 		return false, nil | ||||
| 		return authz.CheckResponse{}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	skip := opts.Unchecked[req.Verb] | ||||
| 	if skip { | ||||
| 		return true, nil | ||||
| 		return authz.CheckResponse{Allowed: true}, nil | ||||
| 	} | ||||
| 
 | ||||
| 	action, ok := opts.Mapping[req.Verb] | ||||
| 	if !ok { | ||||
| 		return false, fmt.Errorf("missing action for %s %s", req.Verb, req.Resource) | ||||
| 		return authz.CheckResponse{}, fmt.Errorf("missing action for %s %s", req.Verb, req.Resource) | ||||
| 	} | ||||
| 
 | ||||
| 	ns, err := claims.ParseNamespace(req.Namespace) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 		return authz.CheckResponse{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	var eval Evaluator | ||||
|  | @ -118,7 +118,7 @@ func (c *LegacyAccessClient) HasAccess(ctx context.Context, id claims.AuthInfo, | |||
| 		if opts.Resolver != nil { | ||||
| 			scopes, err := opts.Resolver.Resolve(ctx, ns, req.Name) | ||||
| 			if err != nil { | ||||
| 				return false, err | ||||
| 				return authz.CheckResponse{}, err | ||||
| 			} | ||||
| 			eval = EvalPermission(action, scopes...) | ||||
| 		} else { | ||||
|  | @ -129,14 +129,18 @@ func (c *LegacyAccessClient) HasAccess(ctx context.Context, id claims.AuthInfo, | |||
| 		eval = EvalPermission(action) | ||||
| 	} else { | ||||
| 		// Assuming that all non list request should have a valid name
 | ||||
| 		return false, fmt.Errorf("unhandled authorization: %s %s", req.Group, req.Verb) | ||||
| 		return authz.CheckResponse{}, fmt.Errorf("unhandled authorization: %s %s", req.Group, req.Verb) | ||||
| 	} | ||||
| 
 | ||||
| 	return c.ac.Evaluate(ctx, ident, eval) | ||||
| 	allowed, err := c.ac.Evaluate(ctx, ident, eval) | ||||
| 	if err != nil { | ||||
| 		return authz.CheckResponse{}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return authz.CheckResponse{Allowed: allowed}, nil | ||||
| } | ||||
| 
 | ||||
| // Compile implements claims.AccessClient.
 | ||||
| func (c *LegacyAccessClient) Compile(ctx context.Context, id claims.AuthInfo, req claims.AccessRequest) (claims.AccessChecker, error) { | ||||
| func (c *LegacyAccessClient) Compile(ctx context.Context, id claims.AuthInfo, req authz.ListRequest) (authz.ItemChecker, error) { | ||||
| 	ident, ok := id.(identity.Requester) | ||||
| 	if !ok { | ||||
| 		return nil, errors.New("expected identity.Requester for legacy access control") | ||||
|  | @ -147,13 +151,13 @@ func (c *LegacyAccessClient) Compile(ctx context.Context, id claims.AuthInfo, re | |||
| 		return nil, fmt.Errorf("unsupported resource: %s", req.Resource) | ||||
| 	} | ||||
| 
 | ||||
| 	action, ok := opts.Mapping[req.Verb] | ||||
| 	action, ok := opts.Mapping[utils.VerbList] | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("missing action for %s %s", req.Verb, req.Resource) | ||||
| 		return nil, fmt.Errorf("missing action for %s %s", utils.VerbList, req.Resource) | ||||
| 	} | ||||
| 
 | ||||
| 	check := Checker(ident, action) | ||||
| 	return func(_, name string) bool { | ||||
| 	return func(_, name, _ string) bool { | ||||
| 		return check(fmt.Sprintf("%s:%s:%s", opts.Resource, opts.Attr, name)) | ||||
| 	}, nil | ||||
| } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ import ( | |||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
| 	"github.com/grafana/grafana/pkg/services/accesscontrol" | ||||
| 	"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" | ||||
|  | @ -14,20 +14,20 @@ import ( | |||
| 	"github.com/grafana/grafana/pkg/services/featuremgmt" | ||||
| ) | ||||
| 
 | ||||
| func TestLegacyAccessClient_HasAccess(t *testing.T) { | ||||
| func TestLegacyAccessClient_Check(t *testing.T) { | ||||
| 	ac := acimpl.ProvideAccessControl(featuremgmt.WithFeatures(), zanzana.NewNoopClient()) | ||||
| 
 | ||||
| 	t.Run("should reject when when no configuration for resource exist", func(t *testing.T) { | ||||
| 		a := accesscontrol.NewLegacyAccessClient(ac) | ||||
| 
 | ||||
| 		ok, err := a.HasAccess(context.Background(), &identity.StaticRequester{}, claims.AccessRequest{ | ||||
| 		res, err := a.Check(context.Background(), &identity.StaticRequester{}, authz.CheckRequest{ | ||||
| 			Verb:      "get", | ||||
| 			Resource:  "dashboards", | ||||
| 			Namespace: "default", | ||||
| 			Name:      "1", | ||||
| 		}) | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, false, ok) | ||||
| 		assert.Equal(t, false, res.Allowed) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("should reject when user don't have correct scope", func(t *testing.T) { | ||||
|  | @ -43,7 +43,7 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 			accesscontrol.Permission{Action: "dashboards:read", Scope: "dashboards:uid:2"}, | ||||
| 		) | ||||
| 
 | ||||
| 		ok, err := a.HasAccess(context.Background(), ident, claims.AccessRequest{ | ||||
| 		res, err := a.Check(context.Background(), ident, authz.CheckRequest{ | ||||
| 			Verb:      "get", | ||||
| 			Namespace: "default", | ||||
| 			Resource:  "dashboards", | ||||
|  | @ -51,7 +51,7 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 		}) | ||||
| 
 | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, false, ok) | ||||
| 		assert.Equal(t, false, res.Allowed) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("should just check action for list requests", func(t *testing.T) { | ||||
|  | @ -67,14 +67,14 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 			accesscontrol.Permission{Action: "dashboards:read"}, | ||||
| 		) | ||||
| 
 | ||||
| 		ok, err := a.HasAccess(context.Background(), ident, claims.AccessRequest{ | ||||
| 		res, err := a.Check(context.Background(), ident, authz.CheckRequest{ | ||||
| 			Verb:      "list", | ||||
| 			Namespace: "default", | ||||
| 			Resource:  "dashboards", | ||||
| 		}) | ||||
| 
 | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, true, ok) | ||||
| 		assert.Equal(t, true, res.Allowed) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("should allow when user have correct scope", func(t *testing.T) { | ||||
|  | @ -90,7 +90,7 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 			accesscontrol.Permission{Action: "dashboards:read", Scope: "dashboards:uid:1"}, | ||||
| 		) | ||||
| 
 | ||||
| 		ok, err := a.HasAccess(context.Background(), ident, claims.AccessRequest{ | ||||
| 		res, err := a.Check(context.Background(), ident, authz.CheckRequest{ | ||||
| 			Verb:      "get", | ||||
| 			Namespace: "default", | ||||
| 			Resource:  "dashboards", | ||||
|  | @ -98,7 +98,7 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 		}) | ||||
| 
 | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, true, ok) | ||||
| 		assert.Equal(t, true, res.Allowed) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("should skip authorization for configured verb", func(t *testing.T) { | ||||
|  | @ -115,7 +115,7 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 
 | ||||
| 		ident := newIdent(accesscontrol.Permission{}) | ||||
| 
 | ||||
| 		ok, err := a.HasAccess(context.Background(), ident, claims.AccessRequest{ | ||||
| 		res, err := a.Check(context.Background(), ident, authz.CheckRequest{ | ||||
| 			Verb:      "get", | ||||
| 			Namespace: "default", | ||||
| 			Resource:  "dashboards", | ||||
|  | @ -123,9 +123,9 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 		}) | ||||
| 
 | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, true, ok) | ||||
| 		assert.Equal(t, true, res.Allowed) | ||||
| 
 | ||||
| 		ok, err = a.HasAccess(context.Background(), ident, claims.AccessRequest{ | ||||
| 		res, err = a.Check(context.Background(), ident, authz.CheckRequest{ | ||||
| 			Verb:      "create", | ||||
| 			Namespace: "default", | ||||
| 			Resource:  "dashboards", | ||||
|  | @ -133,7 +133,7 @@ func TestLegacyAccessClient_HasAccess(t *testing.T) { | |||
| 		}) | ||||
| 
 | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, false, ok) | ||||
| 		assert.Equal(t, false, res.Allowed) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,17 +4,18 @@ import ( | |||
| 	"context" | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authz" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 	"k8s.io/apiserver/pkg/authorization/authorizer" | ||||
| ) | ||||
| 
 | ||||
| func NewResourceAuthorizer(c claims.AccessClient) authorizer.Authorizer { | ||||
| func NewResourceAuthorizer(c authz.AccessClient) authorizer.Authorizer { | ||||
| 	return ResourceAuthorizer{c} | ||||
| } | ||||
| 
 | ||||
| // ResourceAuthorizer is used to translate authorizer.Authorizer calls to claims.AccessClient calls
 | ||||
| type ResourceAuthorizer struct { | ||||
| 	c claims.AccessClient | ||||
| 	c authz.AccessClient | ||||
| } | ||||
| 
 | ||||
| func (r ResourceAuthorizer) Authorize(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) { | ||||
|  | @ -27,7 +28,7 @@ func (r ResourceAuthorizer) Authorize(ctx context.Context, attr authorizer.Attri | |||
| 		return authorizer.DecisionDeny, "", errors.New("no identity found for request") | ||||
| 	} | ||||
| 
 | ||||
| 	ok, err := r.c.HasAccess(ctx, ident, claims.AccessRequest{ | ||||
| 	res, err := r.c.Check(ctx, ident, authz.CheckRequest{ | ||||
| 		Verb:        attr.GetVerb(), | ||||
| 		Group:       attr.GetAPIGroup(), | ||||
| 		Resource:    attr.GetResource(), | ||||
|  | @ -41,7 +42,7 @@ func (r ResourceAuthorizer) Authorize(ctx context.Context, attr authorizer.Attri | |||
| 		return authorizer.DecisionDeny, "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if !ok { | ||||
| 	if !res.Allowed { | ||||
| 		return authorizer.DecisionDeny, "unauthorized request", nil | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ import ( | |||
| const authzServiceAudience = "authzService" | ||||
| 
 | ||||
| type Client interface { | ||||
| 	authzlib.Client | ||||
| 	authzlib.AccessChecker | ||||
| } | ||||
| 
 | ||||
| // ProvideAuthZClient provides an AuthZ client and creates the AuthZ service.
 | ||||
|  | @ -40,7 +40,7 @@ func ProvideAuthZClient( | |||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var client authzlib.Client | ||||
| 	var client Client | ||||
| 
 | ||||
| 	// Register the server
 | ||||
| 	server, err := newLegacyServer(acSvc, features, grpcServer, tracer, authCfg) | ||||
|  | @ -86,7 +86,7 @@ func ProvideStandaloneAuthZClient( | |||
| 	return newGrpcLegacyClient(authCfg.remoteAddress) | ||||
| } | ||||
| 
 | ||||
| func newInProcLegacyClient(server *legacyServer) (authzlib.Client, error) { | ||||
| func newInProcLegacyClient(server *legacyServer) (authzlib.AccessChecker, error) { | ||||
| 	noAuth := func(ctx context.Context) (context.Context, error) { | ||||
| 		return ctx, nil | ||||
| 	} | ||||
|  | @ -101,14 +101,14 @@ func newInProcLegacyClient(server *legacyServer) (authzlib.Client, error) { | |||
| 		server, | ||||
| 	) | ||||
| 
 | ||||
| 	return authzlib.NewLegacyClient( | ||||
| 	return authzlib.NewClient( | ||||
| 		&authzlib.ClientConfig{}, | ||||
| 		authzlib.WithGrpcConnectionLCOption(channel), | ||||
| 		authzlib.WithDisableAccessTokenLCOption(), | ||||
| 		authzlib.WithGrpcConnectionClientOption(channel), | ||||
| 		authzlib.WithDisableAccessTokenClientOption(), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func newGrpcLegacyClient(address string) (authzlib.Client, error) { | ||||
| func newGrpcLegacyClient(address string) (authzlib.AccessChecker, error) { | ||||
| 	// This client interceptor is a noop, as we don't send an access token
 | ||||
| 	grpcClientConfig := authnlib.GrpcClientConfig{} | ||||
| 	clientInterceptor, err := authnlib.NewGrpcClientInterceptor(&grpcClientConfig, | ||||
|  | @ -119,15 +119,15 @@ func newGrpcLegacyClient(address string) (authzlib.Client, error) { | |||
| 	} | ||||
| 
 | ||||
| 	cfg := authzlib.ClientConfig{RemoteAddress: address} | ||||
| 	client, err := authzlib.NewLegacyClient(&cfg, | ||||
| 	client, err := authzlib.NewClient(&cfg, | ||||
| 		// TODO(drclau): make this configurable (e.g. allow to use insecure connections)
 | ||||
| 		authzlib.WithGrpcDialOptionsLCOption( | ||||
| 		authzlib.WithGrpcDialOptionsClientOption( | ||||
| 			grpc.WithTransportCredentials(insecure.NewCredentials()), | ||||
| 			grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor), | ||||
| 			grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor), | ||||
| 		), | ||||
| 		// TODO(drclau): remove this once we have access token support on-prem
 | ||||
| 		authzlib.WithDisableAccessTokenLCOption(), | ||||
| 		authzlib.WithDisableAccessTokenClientOption(), | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|  | @ -136,7 +136,7 @@ func newGrpcLegacyClient(address string) (authzlib.Client, error) { | |||
| 	return client, nil | ||||
| } | ||||
| 
 | ||||
| func newCloudLegacyClient(authCfg *Cfg) (authzlib.Client, error) { | ||||
| func newCloudLegacyClient(authCfg *Cfg) (authzlib.AccessChecker, error) { | ||||
| 	grpcClientConfig := authnlib.GrpcClientConfig{ | ||||
| 		TokenClientConfig: &authnlib.TokenExchangeConfig{ | ||||
| 			Token:            authCfg.token, | ||||
|  | @ -154,9 +154,9 @@ func newCloudLegacyClient(authCfg *Cfg) (authzlib.Client, error) { | |||
| 	} | ||||
| 
 | ||||
| 	clientCfg := authzlib.ClientConfig{RemoteAddress: authCfg.remoteAddress} | ||||
| 	client, err := authzlib.NewLegacyClient(&clientCfg, | ||||
| 	client, err := authzlib.NewClient(&clientCfg, | ||||
| 		// TODO(drclau): make this configurable (e.g. allow to use insecure connections)
 | ||||
| 		authzlib.WithGrpcDialOptionsLCOption( | ||||
| 		authzlib.WithGrpcDialOptionsClientOption( | ||||
| 			grpc.WithTransportCredentials(insecure.NewCredentials()), | ||||
| 			grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor), | ||||
| 			grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor), | ||||
|  |  | |||
|  | @ -2,10 +2,9 @@ package authz | |||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"errors" | ||||
| 
 | ||||
| 	authzv1 "github.com/grafana/authlib/authz/proto/v1" | ||||
| 	"github.com/grafana/authlib/claims" | ||||
| 
 | ||||
| 	"github.com/grafana/grafana/pkg/infra/log" | ||||
| 	"github.com/grafana/grafana/pkg/infra/tracing" | ||||
|  | @ -16,14 +15,6 @@ import ( | |||
| 
 | ||||
| var _ authzv1.AuthzServiceServer = (*legacyServer)(nil) | ||||
| 
 | ||||
| type legacyServer struct { | ||||
| 	authzv1.UnimplementedAuthzServiceServer | ||||
| 
 | ||||
| 	acSvc  accesscontrol.Service | ||||
| 	logger log.Logger | ||||
| 	tracer tracing.Tracer | ||||
| } | ||||
| 
 | ||||
| func newLegacyServer( | ||||
| 	acSvc accesscontrol.Service, features featuremgmt.FeatureToggles, | ||||
| 	grpcServer grpcserver.Provider, tracer tracing.Tracer, cfg *Cfg, | ||||
|  | @ -45,40 +36,15 @@ func newLegacyServer( | |||
| 	return s, nil | ||||
| } | ||||
| 
 | ||||
| func (s *legacyServer) Read(ctx context.Context, req *authzv1.ReadRequest) (*authzv1.ReadResponse, error) { | ||||
| 	ctx, span := s.tracer.Start(ctx, "authz.grpc.Read") | ||||
| 	defer span.End() | ||||
| type legacyServer struct { | ||||
| 	authzv1.UnimplementedAuthzServiceServer | ||||
| 
 | ||||
| 	// FIXME: once we have access tokens, we need to do namespace validation here
 | ||||
| 
 | ||||
| 	action := req.GetAction() | ||||
| 	subject := req.GetSubject() | ||||
| 	namespace := req.GetNamespace() // TODO can we consider the stackID as the orgID?
 | ||||
| 
 | ||||
| 	info, err := claims.ParseNamespace(namespace) | ||||
| 	if err != nil || info.OrgID == 0 { | ||||
| 		return nil, fmt.Errorf("invalid namespace: %s", namespace) | ||||
| 	} | ||||
| 
 | ||||
| 	ctxLogger := s.logger.FromContext(ctx) | ||||
| 	ctxLogger.Debug("Read", "action", action, "subject", subject, "namespace", namespace) | ||||
| 
 | ||||
| 	permissions, err := s.acSvc.SearchUserPermissions( | ||||
| 		ctx, | ||||
| 		info.OrgID, | ||||
| 		accesscontrol.SearchOptions{Action: action, TypedID: subject}, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		ctxLogger.Error("failed to search user permissions", "error", err) | ||||
| 		return nil, tracing.Errorf(span, "failed to search user permissions: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	data := make([]*authzv1.ReadResponse_Data, 0, len(permissions)) | ||||
| 	for _, perm := range permissions { | ||||
| 		data = append(data, &authzv1.ReadResponse_Data{Scope: perm.Scope}) | ||||
| 	} | ||||
| 	return &authzv1.ReadResponse{ | ||||
| 		Data:  data, | ||||
| 		Found: len(data) > 0, | ||||
| 	}, nil | ||||
| 	acSvc  accesscontrol.Service | ||||
| 	logger log.Logger | ||||
| 	tracer tracing.Tracer | ||||
| } | ||||
| 
 | ||||
| func (l *legacyServer) Check(context.Context, *authzv1.CheckRequest) (*authzv1.CheckResponse, error) { | ||||
| 	// FIXME: implement for legacy access control
 | ||||
| 	return nil, errors.New("unimplemented") | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue