Skip to content

Commit 5340a6e

Browse files
Jguergamab
andauthored
Auth: Extended JWT client for OBO and Service Authentication (#83814)
* reenable ext-jwt-client * fixup settings struct * add user and service auth * lint up * add user auth to grafana ext * fixes * Populate token permissions Co-authored-by: jguer <[email protected]> * fix tests * fix lint * small prealloc * small prealloc * use special namespace for access policies * fix access policy auth * fix tests * fix uncalled settings expander * add feature toggle * small feedback fixes * rename entitlements to permissions * add authlibn * allow viewing the signed in user info for non user namespace * fix invalid namespacedID * use authlib as verifier for tokens * Update pkg/services/authn/clients/ext_jwt.go Co-authored-by: Gabriel MABILLE <[email protected]> * Update pkg/services/authn/clients/ext_jwt_test.go Co-authored-by: Gabriel MABILLE <[email protected]> * fix parameter names * change asserts to normal package * add rule for assert * fix ownerships * Local diff * test and lint * Fix test * Fix ac test * Fix pluginproxy test * Revert testdata changes * Force revert on test data --------- Co-authored-by: gamab <[email protected]> Co-authored-by: Gabriel MABILLE <[email protected]>
1 parent ac6e51c commit 5340a6e

28 files changed

+433
-316
lines changed

.golangci.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ deny = [
1717
{ pkg = "github.com/pkg/errors", desc = "Deprecated: Go 1.13 supports the functionality provided by pkg/errors in the standard library." },
1818
{ pkg = "github.com/xorcare/pointer", desc = "Use pkg/util.Pointer instead, which is a generic one-liner alternative" },
1919
{ pkg = "github.com/gofrs/uuid", desc = "Use github.com/google/uuid instead, which we already depend on." },
20+
{ pkg = "github.com/bmizerany/assert", desc = "Use github.com/stretchr/testify/assert instead, which we already depend on." },
2021
]
2122

2223
[linters-settings.depguard.rules.coreplugins]

go.mod

+8-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ require (
2020
cuelang.org/go v0.6.0-0.dev // @grafana/grafana-as-code
2121
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // @grafana/partner-datasources
2222
github.com/Azure/go-autorest/autorest v0.11.29 // @grafana/backend-platform
23-
github.com/BurntSushi/toml v1.3.2 // @grafana/grafana-authnz-team
23+
github.com/BurntSushi/toml v1.3.2 // @grafana/identity-access-team
2424
github.com/Masterminds/semver v1.5.0 // @grafana/backend-platform
2525
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // @grafana/backend-platform
2626
github.com/aws/aws-sdk-go v1.50.8 // @grafana/aws-datasources
@@ -29,10 +29,10 @@ require (
2929
github.com/blang/semver/v4 v4.0.0 // @grafana/grafana-release-guild
3030
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // @grafana/backend-platform
3131
github.com/centrifugal/centrifuge v0.30.2 // @grafana/grafana-app-platform-squad
32-
github.com/crewjam/saml v0.4.13 // @grafana/grafana-authnz-team
32+
github.com/crewjam/saml v0.4.13 // @grafana/identity-access-team
3333
github.com/fatih/color v1.15.0 // @grafana/backend-platform
3434
github.com/gchaincl/sqlhooks v1.3.0 // @grafana/backend-platform
35-
github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/grafana-authnz-team
35+
github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/identity-access-team
3636
github.com/go-openapi/strfmt v0.22.0 // @grafana/alerting-squad-backend
3737
github.com/go-redis/redis/v8 v8.11.5 // @grafana/backend-platform
3838
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // @grafana/backend-platform
@@ -96,7 +96,7 @@ require (
9696
golang.org/x/crypto v0.21.0 // @grafana/backend-platform
9797
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // @grafana/alerting-squad-backend
9898
golang.org/x/net v0.22.0 // @grafana/oss-big-tent @grafana/partner-datasources
99-
golang.org/x/oauth2 v0.18.0 // @grafana/grafana-authnz-team
99+
golang.org/x/oauth2 v0.18.0 // @grafana/identity-access-team
100100
golang.org/x/sync v0.6.0 // @grafana/alerting-squad-backend
101101
golang.org/x/time v0.5.0 // @grafana/backend-platform
102102
golang.org/x/tools v0.17.0 // @grafana/grafana-as-code
@@ -241,7 +241,7 @@ require (
241241
github.com/Masterminds/semver/v3 v3.1.1 // @grafana/grafana-release-guild
242242
github.com/alicebob/miniredis/v2 v2.30.1 // @grafana/alerting-squad-backend
243243
github.com/dave/dst v0.27.2 // @grafana/grafana-as-code
244-
github.com/go-jose/go-jose/v3 v3.0.3 // @grafana/grafana-authnz-team
244+
github.com/go-jose/go-jose/v3 v3.0.3 // @grafana/identity-access-team
245245
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
246246
github.com/grafana/dataplane/sdata v0.0.7 // @grafana/observability-metrics
247247
github.com/grafana/tempo v1.5.1-0.20230524121406-1dc1bfe7085b // @grafana/observability-traces-and-profiling
@@ -326,7 +326,7 @@ require (
326326
github.com/mattn/go-colorable v0.1.13 // indirect
327327
github.com/mattn/go-ieproxy v0.0.3 // indirect
328328
github.com/mitchellh/copystructure v1.2.0 // indirect
329-
github.com/mitchellh/mapstructure v1.5.0 //@grafana/grafana-authnz-team
329+
github.com/mitchellh/mapstructure v1.5.0 //@grafana/identity-access-team
330330
github.com/mitchellh/reflectwalk v1.0.2 // indirect
331331
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // @grafana/alerting-squad-backend
332332
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@@ -384,7 +384,7 @@ require (
384384
require (
385385
cloud.google.com/go/compute v1.23.3 // indirect
386386
cloud.google.com/go/iam v1.1.5 // indirect
387-
filippo.io/age v1.1.1 // @grafana/grafana-authnz-team
387+
filippo.io/age v1.1.1 // @grafana/identity-access-team
388388
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect
389389
github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.0 // indirect
390390
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
@@ -473,7 +473,7 @@ require github.com/jackc/pgx/v5 v5.5.5 // @grafana/oss-big-tent
473473

474474
require github.com/getkin/kin-openapi v0.120.0 // @grafana/grafana-as-code
475475

476-
require github.com/grafana/authlib v0.0.0-20240319083410-9d4a6e3861e5 // @grafana/grafana-app-platform-squad
476+
require github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 // @grafana/identity-access-team
477477

478478
require (
479479
github.com/bahlo/generic-list-go v0.2.0 // indirect

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -2161,8 +2161,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
21612161
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
21622162
github.com/grafana/alerting v0.0.0-20240322221449-89ae4e299bf8 h1:ndBSFAHmJRWqln2uNys7lV0+9U8tlW6ZuNz8ETW60Us=
21632163
github.com/grafana/alerting v0.0.0-20240322221449-89ae4e299bf8/go.mod h1:0nHKO0w8OTemvZ3eh7+s1EqGGhgbs0kvkTeLU1FrbTw=
2164-
github.com/grafana/authlib v0.0.0-20240319083410-9d4a6e3861e5 h1:A13Z8Hy60BfIduM819kpk0njrRKjbAVbVRhE+R+AF/8=
2165-
github.com/grafana/authlib v0.0.0-20240319083410-9d4a6e3861e5/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4=
2164+
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 h1:lGEuhD/KhhN1OiPrvwQejl9Lg8MvaHdj3lHZNref4is=
2165+
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4=
21662166
github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw=
21672167
github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
21682168
github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ=

go.work.sum

+6
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg=
351351
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
352352
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
353353
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
354+
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
354355
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
355356
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ=
356357
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk=
@@ -405,6 +406,9 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8
405406
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
406407
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
407408
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
409+
github.com/grafana/authlib v0.0.0-20240319083410-9d4a6e3861e5/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4=
410+
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 h1:lGEuhD/KhhN1OiPrvwQejl9Lg8MvaHdj3lHZNref4is=
411+
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4=
408412
github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b h1:Ha+kSIoTutf4ytlVw/SaEclDUloYx0+FXDKJWKhNbE4=
409413
github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b/go.mod h1:3UsooRp7yW5/NJQBlXcTsAHOoykEhNUYXkQ3r6ehEEY=
410414
github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ=
@@ -661,6 +665,7 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJ
661665
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
662666
github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
663667
github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e h1:mOtuXaRAbVZsxAHVdPR3IjfmN8T1h2iczJLynhLybf8=
668+
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
664669
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
665670
github.com/substrait-io/substrait-go v0.4.2 h1:buDnjsb3qAqTaNbOR7VKmNgXf4lYQxWEcnSGUWBtmN8=
666671
github.com/substrait-io/substrait-go v0.4.2/go.mod h1:qhpnLmrcvAnlZsUyPXZRqldiHapPTXC3t7xFgDi3aQg=
@@ -771,6 +776,7 @@ go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM=
771776
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
772777
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
773778
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
779+
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
774780
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
775781
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
776782
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=

packages/grafana-data/src/types/featureToggles.gen.ts

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ export interface FeatureToggles {
173173
expressionParser?: boolean;
174174
groupByVariable?: boolean;
175175
betterPageScrolling?: boolean;
176+
authAPIAccessTokenAuth?: boolean;
176177
scopeFilters?: boolean;
177178
ssoSettingsSAML?: boolean;
178179
usePrometheusFrontendPackage?: boolean;

pkg/api/pluginproxy/ds_proxy_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,8 @@ func TestDataSourceProxy_routeRule(t *testing.T) {
513513
t,
514514
&contextmodel.ReqContext{
515515
SignedInUser: &user.SignedInUser{
516-
Login: "test_user",
516+
Login: "test_user",
517+
NamespacedID: "user:1",
517518
},
518519
},
519520
&setting.Cfg{SendUserHeader: true},

pkg/api/pluginproxy/pluginproxy_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ func TestPluginProxy(t *testing.T) {
7676
secretsService,
7777
&contextmodel.ReqContext{
7878
SignedInUser: &user.SignedInUser{
79-
Login: "test_user",
79+
Login: "test_user",
80+
NamespacedID: "user:1",
8081
},
8182
Context: &web.Context{
8283
Req: httpReq,

pkg/api/user.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,23 @@ import (
3131
// 404: notFoundError
3232
// 500: internalServerError
3333
func (hs *HTTPServer) GetSignedInUser(c *contextmodel.ReqContext) response.Response {
34-
userID, errResponse := getUserID(c)
35-
if errResponse != nil {
36-
return errResponse
34+
namespace, identifier := c.SignedInUser.GetNamespacedID()
35+
if namespace != identity.NamespaceUser {
36+
return response.JSON(http.StatusOK, user.UserProfileDTO{
37+
IsGrafanaAdmin: c.SignedInUser.GetIsGrafanaAdmin(),
38+
OrgID: c.SignedInUser.GetOrgID(),
39+
UID: strings.Join([]string{namespace, identifier}, ":"),
40+
Name: c.SignedInUser.NameOrFallback(),
41+
Email: c.SignedInUser.GetEmail(),
42+
Login: c.SignedInUser.GetLogin(),
43+
})
3744
}
45+
46+
userID, err := identity.IntIdentifier(namespace, identifier)
47+
if err != nil {
48+
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
49+
}
50+
3851
return hs.getUserUserProfile(c, userID)
3952
}
4053

pkg/services/accesscontrol/accesscontrol.go

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type AccessControl interface {
2424

2525
type Service interface {
2626
registry.ProvidesUsageStats
27+
// GetRoleByName returns a role by name
28+
GetRoleByName(ctx context.Context, orgID int64, roleName string) (*RoleDTO, error)
2729
// GetUserPermissions returns user permissions with only action and scope fields set.
2830
GetUserPermissions(ctx context.Context, user identity.Requester, options Options) ([]Permission, error)
2931
// GetUserPermissionsInOrg return user permission in a specific organization

pkg/services/accesscontrol/acimpl/service.go

+23
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,26 @@ func (s *Service) DeleteExternalServiceRole(ctx context.Context, externalService
504504
func (*Service) SyncUserRoles(ctx context.Context, orgID int64, cmd accesscontrol.SyncUserRolesCommand) error {
505505
return nil
506506
}
507+
508+
func (s *Service) GetRoleByName(ctx context.Context, orgID int64, roleName string) (*accesscontrol.RoleDTO, error) {
509+
err := accesscontrol.ErrRoleNotFound
510+
if _, ok := s.roles[roleName]; ok {
511+
return nil, err
512+
}
513+
514+
var role *accesscontrol.RoleDTO
515+
s.registrations.Range(func(registration accesscontrol.RoleRegistration) bool {
516+
if registration.Role.Name == roleName {
517+
role = &accesscontrol.RoleDTO{
518+
Name: registration.Role.Name,
519+
Permissions: registration.Role.Permissions,
520+
DisplayName: registration.Role.DisplayName,
521+
Description: registration.Role.Description,
522+
}
523+
err = nil
524+
return false
525+
}
526+
return true
527+
})
528+
return role, err
529+
}

pkg/services/accesscontrol/acimpl/service_test.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -754,8 +754,9 @@ func TestPermissionCacheKey(t *testing.T) {
754754
{
755755
name: "should return correct key for user",
756756
signedInUser: &user.SignedInUser{
757-
OrgID: 1,
758-
UserID: 1,
757+
OrgID: 1,
758+
UserID: 1,
759+
NamespacedID: "user:1",
759760
},
760761
expected: "rbac-permissions-1-user-1",
761762
},
@@ -765,6 +766,7 @@ func TestPermissionCacheKey(t *testing.T) {
765766
OrgID: 1,
766767
ApiKeyID: 1,
767768
IsServiceAccount: false,
769+
NamespacedID: "user:1",
768770
},
769771
expected: "rbac-permissions-1-api-key-1",
770772
},
@@ -774,6 +776,7 @@ func TestPermissionCacheKey(t *testing.T) {
774776
OrgID: 1,
775777
UserID: 1,
776778
IsServiceAccount: true,
779+
NamespacedID: "serviceaccount:1",
777780
},
778781
expected: "rbac-permissions-1-service-account-1",
779782
},
@@ -783,14 +786,16 @@ func TestPermissionCacheKey(t *testing.T) {
783786
OrgID: 1,
784787
UserID: -1,
785788
IsServiceAccount: true,
789+
NamespacedID: "serviceaccount:-1",
786790
},
787791
expected: "rbac-permissions-1-service-account--1",
788792
},
789793
{
790794
name: "should use org role if no unique id",
791795
signedInUser: &user.SignedInUser{
792-
OrgID: 1,
793-
OrgRole: org.RoleNone,
796+
OrgID: 1,
797+
OrgRole: org.RoleNone,
798+
NamespacedID: "user:1",
794799
},
795800
expected: "rbac-permissions-1-user-None",
796801
},

pkg/services/accesscontrol/mock/mock.go

+10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type fullAccessControl interface {
2020

2121
type Calls struct {
2222
Evaluate []interface{}
23+
GetRoleByName []interface{}
2324
GetUserPermissions []interface{}
2425
GetUserPermissionsInOrg []interface{}
2526
ClearUserPermissionCache []interface{}
@@ -47,6 +48,7 @@ type Mock struct {
4748

4849
// Override functions
4950
EvaluateFunc func(context.Context, identity.Requester, accesscontrol.Evaluator) (bool, error)
51+
GetRoleByNameFunc func(context.Context, int64, string) (*accesscontrol.RoleDTO, error)
5052
GetUserPermissionsFunc func(context.Context, identity.Requester, accesscontrol.Options) ([]accesscontrol.Permission, error)
5153
GetUserPermissionsInOrgFunc func(context.Context, identity.Requester, int64) ([]accesscontrol.Permission, error)
5254
ClearUserPermissionCacheFunc func(identity.Requester)
@@ -81,6 +83,14 @@ func New() *Mock {
8183
return mock
8284
}
8385

86+
func (m *Mock) GetRoleByName(ctx context.Context, orgID int64, roleName string) (*accesscontrol.RoleDTO, error) {
87+
m.Calls.GetRoleByName = append(m.Calls.GetRoleByName, []interface{}{ctx, orgID, roleName})
88+
if m.GetRoleByNameFunc != nil {
89+
return m.GetRoleByNameFunc(ctx, orgID, roleName)
90+
}
91+
return nil, nil
92+
}
93+
8494
func (m *Mock) GetUsageStats(ctx context.Context) map[string]interface{} {
8595
return make(map[string]interface{})
8696
}

pkg/services/auth/identity/requester.go

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const (
1414
NamespaceServiceAccount = "service-account"
1515
NamespaceAnonymous = "anonymous"
1616
NamespaceRenderService = "render"
17+
NamespaceAccessPolicy = "access-policy"
1718
)
1819

1920
var ErrNotIntIdentifier = errors.New("identifier is not an int64")

pkg/services/authn/authn.go

+9
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ type ClientParams struct {
5757
LookUpParams login.UserLookupParams
5858
// SyncPermissions ensure that permissions are loaded from DB and added to the identity
5959
SyncPermissions bool
60+
// FetchPermissionsParams are the arguments used to fetch permissions from the DB
61+
FetchPermissionsParams FetchPermissionsParams
62+
}
63+
64+
type FetchPermissionsParams struct {
65+
// ActionsLookup will restrict the permissions to only these actions
66+
ActionsLookup []string
67+
// Roles permissions will be directly added to the identity permissions
68+
Roles []string
6069
}
6170

6271
type PostAuthHookFn func(ctx context.Context, identity *Identity, r *Request) error

pkg/services/authn/authnimpl/service.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,9 @@ func ProvideService(
135135
s.RegisterClient(clients.ProvideJWT(jwtService, cfg))
136136
}
137137

138-
// FIXME (gamab): Commenting that out for now as we want to re-use the client for external service auth
139-
// if s.cfg.ExtendedJWTAuthEnabled && features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth) {
140-
// s.RegisterClient(clients.ProvideExtendedJWT(userService, cfg, signingKeysService, oauthServer))
141-
// }
138+
if s.cfg.ExtJWTAuth.Enabled && features.IsEnabledGlobally(featuremgmt.FlagAuthAPIAccessTokenAuth) {
139+
s.RegisterClient(clients.ProvideExtendedJWT(userService, cfg, signingKeysService))
140+
}
142141

143142
for name := range socialService.GetOAuthProviders() {
144143
clientName := authn.ClientWithPrefix(name)

pkg/services/authn/authnimpl/sync/rbac_sync.go

+44-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sync
22

33
import (
44
"context"
5+
"errors"
56

67
"github.com/grafana/grafana/pkg/infra/log"
78
"github.com/grafana/grafana/pkg/services/accesscontrol"
@@ -34,19 +35,57 @@ func (s *RBACSync) SyncPermissionsHook(ctx context.Context, ident *authn.Identit
3435
return nil
3536
}
3637

37-
permissions, err := s.ac.GetUserPermissions(ctx, ident, accesscontrol.Options{ReloadCache: false})
38+
// Populate permissions from roles
39+
permissions, err := s.fetchPermissions(ctx, ident)
3840
if err != nil {
39-
s.log.FromContext(ctx).Error("Failed to fetch permissions from db", "error", err, "id", ident.ID)
40-
return errSyncPermissionsForbidden
41+
return err
4142
}
4243

4344
if ident.Permissions == nil {
44-
ident.Permissions = make(map[int64]map[string][]string)
45+
ident.Permissions = make(map[int64]map[string][]string, 1)
46+
}
47+
grouped := accesscontrol.GroupScopesByAction(permissions)
48+
49+
// Restrict access to the list of actions
50+
actionsLookup := ident.ClientParams.FetchPermissionsParams.ActionsLookup
51+
if len(actionsLookup) > 0 {
52+
filtered := make(map[string][]string, len(actionsLookup))
53+
for _, action := range actionsLookup {
54+
if scopes, ok := grouped[action]; ok {
55+
filtered[action] = scopes
56+
}
57+
}
58+
grouped = filtered
4559
}
46-
ident.Permissions[ident.OrgID] = accesscontrol.GroupScopesByAction(permissions)
60+
61+
ident.Permissions[ident.OrgID] = grouped
4762
return nil
4863
}
4964

65+
func (s *RBACSync) fetchPermissions(ctx context.Context, ident *authn.Identity) ([]accesscontrol.Permission, error) {
66+
permissions := make([]accesscontrol.Permission, 0, 8)
67+
roles := ident.ClientParams.FetchPermissionsParams.Roles
68+
if len(roles) > 0 {
69+
for _, role := range roles {
70+
roleDTO, err := s.ac.GetRoleByName(ctx, ident.GetOrgID(), role)
71+
if err != nil && !errors.Is(err, accesscontrol.ErrRoleNotFound) {
72+
s.log.FromContext(ctx).Error("Failed to fetch role from db", "error", err, "role", role)
73+
return nil, errSyncPermissionsForbidden
74+
}
75+
permissions = append(permissions, roleDTO.Permissions...)
76+
}
77+
78+
return permissions, nil
79+
}
80+
81+
permissions, err := s.ac.GetUserPermissions(ctx, ident, accesscontrol.Options{ReloadCache: false})
82+
if err != nil {
83+
s.log.FromContext(ctx).Error("Failed to fetch permissions from db", "error", err, "id", ident.ID)
84+
return nil, errSyncPermissionsForbidden
85+
}
86+
return permissions, nil
87+
}
88+
5089
var fixedCloudRoles = map[org.RoleType]string{
5190
org.RoleViewer: accesscontrol.FixedCloudViewerRole,
5291
org.RoleEditor: accesscontrol.FixedCloudEditorRole,

0 commit comments

Comments
 (0)