From 21d4a4f49ee4d74119089c5c2e2d2d6878f4faa0 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Mon, 12 Aug 2024 09:26:53 +0300 Subject: [PATCH] Auth: use IdentityType from authlib (#91763) --- pkg/api/admin_users.go | 3 +- pkg/api/dashboard.go | 3 +- pkg/api/folder.go | 3 +- pkg/api/index.go | 3 +- pkg/api/org.go | 3 +- pkg/api/pluginproxy/ds_proxy_test.go | 3 +- pkg/api/pluginproxy/pluginproxy_test.go | 4 +- pkg/api/user.go | 9 +-- pkg/api/user_token.go | 5 +- pkg/apimachinery/identity/requester.go | 6 +- pkg/apimachinery/identity/static.go | 4 +- pkg/apimachinery/identity/typed_id.go | 56 ++++--------------- pkg/apimachinery/identity/wrapper.go | 2 +- pkg/apiserver/endpoints/filters/requester.go | 5 +- pkg/apiserver/go.mod | 1 + pkg/apiserver/go.sum | 2 + .../apis/dashboard/legacy/sql_dashboards.go | 5 +- pkg/registry/apis/dashboard/sub_dto.go | 3 +- pkg/services/accesscontrol/accesscontrol.go | 3 +- pkg/services/accesscontrol/acimpl/service.go | 3 +- .../acimpl/service_bench_test.go | 3 +- .../accesscontrol/acimpl/service_test.go | 17 +++--- .../accesscontrol/authorize_in_org_test.go | 3 +- pkg/services/accesscontrol/cacheutils_test.go | 12 ++-- .../accesscontrol/database/database_test.go | 3 +- pkg/services/anonymous/anonimpl/client.go | 5 +- pkg/services/auth/idimpl/service.go | 3 +- pkg/services/auth/idimpl/service_test.go | 5 +- pkg/services/authn/authn.go | 3 +- pkg/services/authn/authnimpl/service.go | 9 +-- pkg/services/authn/authnimpl/service_test.go | 15 ++--- .../authn/authnimpl/sync/oauth_token_sync.go | 4 +- pkg/services/authn/authnimpl/sync/org_sync.go | 6 +- .../authn/authnimpl/sync/rbac_sync.go | 4 +- .../authn/authnimpl/sync/rbac_sync_test.go | 19 ++++--- .../authn/authnimpl/sync/user_sync.go | 19 ++++--- .../authn/authnimpl/sync/user_sync_test.go | 7 ++- pkg/services/authn/authntest/mock.go | 7 ++- pkg/services/authn/clients/api_key.go | 15 ++--- pkg/services/authn/clients/ext_jwt.go | 8 +-- pkg/services/authn/clients/ext_jwt_test.go | 7 +-- pkg/services/authn/clients/grafana.go | 3 +- pkg/services/authn/clients/oauth_test.go | 3 +- pkg/services/authn/clients/proxy.go | 5 +- pkg/services/authn/clients/proxy_test.go | 3 +- pkg/services/authn/clients/render.go | 5 +- pkg/services/authn/clients/session.go | 3 +- pkg/services/authn/identity.go | 14 ++--- pkg/services/contexthandler/contexthandler.go | 7 ++- .../contexthandler/contexthandler_test.go | 3 +- pkg/services/dashboards/database/database.go | 4 +- .../dashboards/service/dashboard_service.go | 5 +- .../dashboardsnapshots/database/database.go | 3 +- pkg/services/folder/folderimpl/sqlstore.go | 6 +- pkg/services/oauthtoken/oauth_token.go | 5 +- .../user_header_middleware.go | 3 +- pkg/services/serviceaccounts/api/api.go | 3 +- pkg/services/team/teamapi/team.go | 4 +- pkg/services/user/identity.go | 26 ++++----- pkg/services/user/userimpl/verifier.go | 3 +- pkg/storage/unified/apistore/store_test.go | 3 +- pkg/storage/unified/resource/go.mod | 1 + pkg/storage/unified/resource/go.sum | 2 + .../unified/resource/grpc/authenticator.go | 3 +- .../resource/grpc/authenticator_test.go | 3 +- pkg/storage/unified/resource/server.go | 3 +- pkg/storage/unified/resource/server_test.go | 3 +- .../unified/sql/test/integration_test.go | 5 +- pkg/util/proxyutil/proxyutil.go | 3 +- pkg/util/proxyutil/proxyutil_test.go | 8 +-- 70 files changed, 230 insertions(+), 212 deletions(-) diff --git a/pkg/api/admin_users.go b/pkg/api/admin_users.go index 5efd9a9446d..83b1ae516af 100644 --- a/pkg/api/admin_users.go +++ b/pkg/api/admin_users.go @@ -8,6 +8,7 @@ import ( "golang.org/x/sync/errgroup" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -366,7 +367,7 @@ func (hs *HTTPServer) AdminLogoutUser(c *contextmodel.ReqContext) response.Respo return response.Error(http.StatusBadRequest, "id is invalid", err) } - if c.SignedInUser.GetID() == identity.NewTypedID(identity.TypeUser, userID) { + if c.SignedInUser.GetID() == identity.NewTypedID(claims.TypeUser, userID) { return response.Error(http.StatusBadRequest, "You cannot logout yourself", nil) } diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index c383d5c0e0a..97a8693fb22 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/apierrors" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" @@ -43,7 +44,7 @@ func (hs *HTTPServer) isDashboardStarredByUser(c *contextmodel.ReqContext, dashI return false, nil } - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return false, nil } diff --git a/pkg/api/folder.go b/pkg/api/folder.go index 1046ad7ed85..3b379cd5291 100644 --- a/pkg/api/folder.go +++ b/pkg/api/folder.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/apierrors" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" @@ -194,7 +195,7 @@ func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int var permissions []accesscontrol.SetResourcePermissionCommand - if identity.IsIdentityType(user.GetID(), identity.TypeUser) { + if identity.IsIdentityType(user.GetID(), claims.TypeUser) { userID, err := identity.UserIdentifier(user.GetID()) if err != nil { return err diff --git a/pkg/api/index.go b/pkg/api/index.go index 14a64f76a09..8f1eebad990 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/webassets" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -170,7 +171,7 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV func (hs *HTTPServer) buildUserAnalyticsSettings(c *contextmodel.ReqContext) dtos.AnalyticsSettings { // Anonymous users do not have an email or auth info - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return dtos.AnalyticsSettings{Identifier: "@" + hs.Cfg.AppURL} } diff --git a/pkg/api/org.go b/pkg/api/org.go index 34b08544b11..88181f7e48a 100644 --- a/pkg/api/org.go +++ b/pkg/api/org.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -132,7 +133,7 @@ func (hs *HTTPServer) CreateOrg(c *contextmodel.ReqContext) response.Response { return response.Error(http.StatusBadRequest, "bad request data", err) } - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return response.Error(http.StatusForbidden, "Only users can create organizations", nil) } diff --git a/pkg/api/pluginproxy/ds_proxy_test.go b/pkg/api/pluginproxy/ds_proxy_test.go index 16203412fce..7ba804a41be 100644 --- a/pkg/api/pluginproxy/ds_proxy_test.go +++ b/pkg/api/pluginproxy/ds_proxy_test.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/oauth2" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/datasource" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/components/simplejson" @@ -586,7 +587,7 @@ func TestDataSourceProxy_routeRule(t *testing.T) { &contextmodel.ReqContext{ SignedInUser: &user.SignedInUser{ Login: "test_user", - FallbackType: identity.TypeUser, + FallbackType: claims.TypeUser, UserID: 1, }, }, diff --git a/pkg/api/pluginproxy/pluginproxy_test.go b/pkg/api/pluginproxy/pluginproxy_test.go index 2a89f18a27d..c1a8523f942 100644 --- a/pkg/api/pluginproxy/pluginproxy_test.go +++ b/pkg/api/pluginproxy/pluginproxy_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -79,7 +79,7 @@ func TestPluginProxy(t *testing.T) { &contextmodel.ReqContext{ SignedInUser: &user.SignedInUser{ Login: "test_user", - FallbackType: identity.TypeUser, + FallbackType: claims.TypeUser, UserID: 1, }, Context: &web.Context{ diff --git a/pkg/api/user.go b/pkg/api/user.go index 42baf886680..6179f71f0b2 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -31,7 +32,7 @@ import ( // 404: notFoundError // 500: internalServerError func (hs *HTTPServer) GetSignedInUser(c *contextmodel.ReqContext) response.Response { - if !identity.IsIdentityType(c.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.GetID(), claims.TypeUser) { return response.JSON(http.StatusOK, user.UserProfileDTO{ IsGrafanaAdmin: c.SignedInUser.GetIsGrafanaAdmin(), OrgID: c.SignedInUser.GetOrgID(), @@ -277,7 +278,7 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC } func (hs *HTTPServer) StartEmailVerificaton(c *contextmodel.ReqContext) response.Response { - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return response.Error(http.StatusBadRequest, "Only users can verify their email", nil) } @@ -504,7 +505,7 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *contextmodel.ReqContex return } - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { c.JsonApiErr(http.StatusForbidden, "Endpoint only available for users", nil) return } @@ -629,7 +630,7 @@ func (hs *HTTPServer) ClearHelpFlags(c *contextmodel.ReqContext) response.Respon } func getUserID(c *contextmodel.ReqContext) (int64, *response.NormalResponse) { - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return 0, response.Error(http.StatusForbidden, "Endpoint only available for users", nil) } diff --git a/pkg/api/user_token.go b/pkg/api/user_token.go index c168c590784..41ba01e7a11 100644 --- a/pkg/api/user_token.go +++ b/pkg/api/user_token.go @@ -8,6 +8,7 @@ import ( "github.com/ua-parser/uap-go/uaparser" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -32,7 +33,7 @@ import ( // 403: forbiddenError // 500: internalServerError func (hs *HTTPServer) GetUserAuthTokens(c *contextmodel.ReqContext) response.Response { - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return response.Error(http.StatusForbidden, "entity not allowed to get tokens", nil) } @@ -62,7 +63,7 @@ func (hs *HTTPServer) RevokeUserAuthToken(c *contextmodel.ReqContext) response.R return response.Error(http.StatusBadRequest, "bad request data", err) } - if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { return response.Error(http.StatusForbidden, "entity not allowed to revoke tokens", nil) } diff --git a/pkg/apimachinery/identity/requester.go b/pkg/apimachinery/identity/requester.go index c8f110a5a2e..ff1e248368d 100644 --- a/pkg/apimachinery/identity/requester.go +++ b/pkg/apimachinery/identity/requester.go @@ -14,7 +14,7 @@ type Requester interface { claims.AuthInfo // GetIdentityType returns the type for the requester - GetIdentityType() IdentityType + GetIdentityType() claims.IdentityType // GetRawIdentifier returns only the identifier part of the UID, excluding the type GetRawIdentifier() string // Deprecated: use GetUID instead @@ -82,7 +82,7 @@ type Requester interface { // Applicable for users, service accounts, api keys and renderer service. // Errors if the identifier is not initialized or if type is not recognized. func IntIdentifier(typedID TypedID) (int64, error) { - if IsIdentityType(typedID, TypeUser, TypeAPIKey, TypeServiceAccount, TypeRenderService) { + if claims.IsIdentityType(typedID.t, claims.TypeUser, claims.TypeAPIKey, claims.TypeServiceAccount, claims.TypeRenderService) { id, err := strconv.ParseInt(typedID.ID(), 10, 64) if err != nil { return 0, fmt.Errorf("unrecognized format for valid type %s: %w", typedID.Type(), err) @@ -107,7 +107,7 @@ func UserIdentifier(typedID TypedID) (int64, error) { return 0, err } - if IsIdentityType(typedID, TypeUser, TypeServiceAccount) { + if claims.IsIdentityType(typedID.t, claims.TypeUser, claims.TypeServiceAccount) { return userID, nil } diff --git a/pkg/apimachinery/identity/static.go b/pkg/apimachinery/identity/static.go index 3fc5aec3430..5d4f25e9a8a 100644 --- a/pkg/apimachinery/identity/static.go +++ b/pkg/apimachinery/identity/static.go @@ -14,7 +14,7 @@ var _ Requester = &StaticRequester{} // This is mostly copied from: // https://github.com/grafana/grafana/blob/v11.0.0/pkg/services/user/identity.go#L16 type StaticRequester struct { - Type IdentityType + Type claims.IdentityType UserID int64 UserUID string OrgID int64 @@ -65,7 +65,7 @@ func (u *StaticRequester) GetInternalID() (int64, error) { } // GetIdentityType implements Requester. -func (u *StaticRequester) GetIdentityType() IdentityType { +func (u *StaticRequester) GetIdentityType() claims.IdentityType { return u.Type } diff --git a/pkg/apimachinery/identity/typed_id.go b/pkg/apimachinery/identity/typed_id.go index 4cecbd2cc93..a2e81179396 100644 --- a/pkg/apimachinery/identity/typed_id.go +++ b/pkg/apimachinery/identity/typed_id.go @@ -4,46 +4,12 @@ import ( "fmt" "strconv" "strings" + + "github.com/grafana/authlib/claims" ) -type IdentityType string - -const ( - TypeUser IdentityType = "user" - TypeAPIKey IdentityType = "api-key" - TypeServiceAccount IdentityType = "service-account" - TypeAnonymous IdentityType = "anonymous" - TypeRenderService IdentityType = "render" - TypeAccessPolicy IdentityType = "access-policy" - TypeProvisioning IdentityType = "provisioning" - TypeEmpty IdentityType = "" -) - -func (n IdentityType) String() string { - return string(n) -} - -func ParseType(str string) (IdentityType, error) { - switch str { - case string(TypeUser): - return TypeUser, nil - case string(TypeAPIKey): - return TypeAPIKey, nil - case string(TypeServiceAccount): - return TypeServiceAccount, nil - case string(TypeAnonymous): - return TypeAnonymous, nil - case string(TypeRenderService): - return TypeRenderService, nil - case string(TypeAccessPolicy): - return TypeAccessPolicy, nil - default: - return "", ErrInvalidTypedID.Errorf("got invalid identity type %s", str) - } -} - // IsIdentityType returns true if typedID matches any expected identity type -func IsIdentityType(typedID TypedID, expected ...IdentityType) bool { +func IsIdentityType(typedID TypedID, expected ...claims.IdentityType) bool { for _, e := range expected { if typedID.Type() == e { return true @@ -53,7 +19,7 @@ func IsIdentityType(typedID TypedID, expected ...IdentityType) bool { return false } -var AnonymousTypedID = NewTypedID(TypeAnonymous, 0) +var AnonymousTypedID = NewTypedID(claims.TypeAnonymous, 0) func ParseTypedID(str string) (TypedID, error) { var typeID TypedID @@ -63,7 +29,7 @@ func ParseTypedID(str string) (TypedID, error) { return typeID, ErrInvalidTypedID.Errorf("expected typed id to have 2 parts") } - t, err := ParseType(parts[0]) + t, err := claims.ParseType(parts[0]) if err != nil { return typeID, err } @@ -84,7 +50,7 @@ func MustParseTypedID(str string) TypedID { return typeID } -func NewTypedID(t IdentityType, id int64) TypedID { +func NewTypedID(t claims.IdentityType, id int64) TypedID { return TypedID{ id: strconv.FormatInt(id, 10), t: t, @@ -92,7 +58,7 @@ func NewTypedID(t IdentityType, id int64) TypedID { } // NewTypedIDString creates a new TypedID with a string id -func NewTypedIDString(t IdentityType, id string) TypedID { +func NewTypedIDString(t claims.IdentityType, id string) TypedID { return TypedID{ id: id, t: t, @@ -102,7 +68,7 @@ func NewTypedIDString(t IdentityType, id string) TypedID { // FIXME: use this instead of encoded string through the codebase type TypedID struct { id string - t IdentityType + t claims.IdentityType } func (ni TypedID) ID() string { @@ -112,7 +78,7 @@ func (ni TypedID) ID() string { // UserID will try to parse and int64 identifier if namespace is either user or service-account. // For all other namespaces '0' will be returned. func (ni TypedID) UserID() (int64, error) { - if ni.IsType(TypeUser, TypeServiceAccount) { + if ni.IsType(claims.TypeUser, claims.TypeServiceAccount) { return ni.ParseInt() } return 0, nil @@ -123,11 +89,11 @@ func (ni TypedID) ParseInt() (int64, error) { return strconv.ParseInt(ni.id, 10, 64) } -func (ni TypedID) Type() IdentityType { +func (ni TypedID) Type() claims.IdentityType { return ni.t } -func (ni TypedID) IsType(expected ...IdentityType) bool { +func (ni TypedID) IsType(expected ...claims.IdentityType) bool { return IsIdentityType(ni, expected...) } diff --git a/pkg/apimachinery/identity/wrapper.go b/pkg/apimachinery/identity/wrapper.go index 07e2b3b95e1..03da34e7aaa 100644 --- a/pkg/apimachinery/identity/wrapper.go +++ b/pkg/apimachinery/identity/wrapper.go @@ -35,7 +35,7 @@ func (i *IDClaimsWrapper) EmailVerified() bool { // GetIdentityType implements claims.IdentityClaims. func (i *IDClaimsWrapper) IdentityType() claims.IdentityType { - return claims.IdentityType(i.Source.GetIdentityType()) + return i.Source.GetIdentityType() } // GetInternalID implements claims.IdentityClaims. diff --git a/pkg/apiserver/endpoints/filters/requester.go b/pkg/apiserver/endpoints/filters/requester.go index 71bb45e19c0..63baf363619 100644 --- a/pkg/apiserver/endpoints/filters/requester.go +++ b/pkg/apiserver/endpoints/filters/requester.go @@ -8,6 +8,7 @@ import ( "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/klog/v2" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" ) @@ -26,7 +27,7 @@ func WithRequester(handler http.Handler) http.Handler { if ok { if info.GetName() == user.Anonymous { requester = &identity.StaticRequester{ - Type: identity.TypeAnonymous, + Type: claims.TypeAnonymous, Name: info.GetName(), Login: info.GetName(), Permissions: map[int64]map[string][]string{}, @@ -37,7 +38,7 @@ func WithRequester(handler http.Handler) http.Handler { slices.Contains(info.GetGroups(), user.SystemPrivilegedGroup) { orgId := int64(1) requester = &identity.StaticRequester{ - Type: identity.TypeServiceAccount, // system:apiserver + Type: claims.TypeServiceAccount, // system:apiserver UserID: 1, OrgID: orgId, Name: info.GetName(), diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 29939ea3ce1..46d483c36a0 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -4,6 +4,7 @@ go 1.22.4 require ( github.com/google/go-cmp v0.6.0 + github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 github.com/prometheus/client_golang v1.19.1 github.com/stretchr/testify v1.9.0 diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index ce3066b8d01..892365f6bea 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -77,6 +77,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-20240809101159-74eaccc31a06 h1:uD1LcKwvEAqzDsgVChBudPqo5BhPxkj9AgylT5QCReo= +github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06/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= diff --git a/pkg/registry/apis/dashboard/legacy/sql_dashboards.go b/pkg/registry/apis/dashboard/legacy/sql_dashboards.go index 23938a00b23..85426711c63 100644 --- a/pkg/registry/apis/dashboard/legacy/sql_dashboards.go +++ b/pkg/registry/apis/dashboard/legacy/sql_dashboards.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" dashboardsV0 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" @@ -338,10 +339,10 @@ func (a *dashboardSqlAccess) scanRow(rows *sql.Rows) (*dashboardRow, error) { func getUserID(v sql.NullString, id sql.NullInt64) string { if v.Valid && v.String != "" { - return identity.NewTypedIDString(identity.TypeUser, v.String).String() + return identity.NewTypedIDString(claims.TypeUser, v.String).String() } if id.Valid && id.Int64 == -1 { - return identity.NewTypedIDString(identity.TypeProvisioning, "").String() + return identity.NewTypedIDString(claims.TypeProvisioning, "").String() } return "" } diff --git a/pkg/registry/apis/dashboard/sub_dto.go b/pkg/registry/apis/dashboard/sub_dto.go index cba01d2724b..7e0cd3a60a1 100644 --- a/pkg/registry/apis/dashboard/sub_dto.go +++ b/pkg/registry/apis/dashboard/sub_dto.go @@ -9,6 +9,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/registry/rest" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" @@ -86,7 +87,7 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob access.CanSave, _ = guardian.CanSave() access.CanAdmin, _ = guardian.CanAdmin() access.CanDelete, _ = guardian.CanDelete() - access.CanStar = user.GetID().Type() == identity.TypeUser // not anon + access.CanStar = user.GetID().Type() == claims.TypeUser // not anon access.AnnotationsPermissions = &dashboard.AnnotationPermission{} r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Dashboard, accesscontrol.ScopeAnnotationsTypeDashboard) diff --git a/pkg/services/accesscontrol/accesscontrol.go b/pkg/services/accesscontrol/accesscontrol.go index 2c89d4e3e0f..a4a48dbb71f 100644 --- a/pkg/services/accesscontrol/accesscontrol.go +++ b/pkg/services/accesscontrol/accesscontrol.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/authn" @@ -110,7 +111,7 @@ func (s *SearchOptions) ComputeUserID() (int64, error) { } // Validate namespace type is user or service account - if s.TypedID.Type() != identity.TypeUser && s.TypedID.Type() != identity.TypeServiceAccount { + if s.TypedID.Type() != claims.TypeUser && s.TypedID.Type() != claims.TypeServiceAccount { return 0, fmt.Errorf("invalid type: %s", s.TypedID.Type()) } diff --git a/pkg/services/accesscontrol/acimpl/service.go b/pkg/services/accesscontrol/acimpl/service.go index c0641bbb118..07e9168bf6e 100644 --- a/pkg/services/accesscontrol/acimpl/service.go +++ b/pkg/services/accesscontrol/acimpl/service.go @@ -11,6 +11,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel/attribute" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" @@ -211,7 +212,7 @@ func (s *Service) getUserDirectPermissions(ctx context.Context, user identity.Re defer span.End() var userID int64 - if identity.IsIdentityType(user.GetID(), identity.TypeUser, identity.TypeServiceAccount) { + if identity.IsIdentityType(user.GetID(), claims.TypeUser, claims.TypeServiceAccount) { var err error userID, err = identity.UserIdentifier(user.GetID()) if err != nil { diff --git a/pkg/services/accesscontrol/acimpl/service_bench_test.go b/pkg/services/accesscontrol/acimpl/service_bench_test.go index 20772c5c8a9..864676ae54b 100644 --- a/pkg/services/accesscontrol/acimpl/service_bench_test.go +++ b/pkg/services/accesscontrol/acimpl/service_bench_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/localcache" @@ -262,7 +263,7 @@ func benchSearchUserWithAction(b *testing.B, usersCount, resourceCount int) { for n := 0; n < b.N; n++ { usersPermissions, err := acService.SearchUsersPermissions(context.Background(), siu, - accesscontrol.SearchOptions{Action: "resources:action2", TypedID: identity.NewTypedID(identity.TypeUser, 14)}) + accesscontrol.SearchOptions{Action: "resources:action2", TypedID: identity.NewTypedID(claims.TypeUser, 14)}) require.NoError(b, err) require.Len(b, usersPermissions, 1) for _, permissions := range usersPermissions { diff --git a/pkg/services/accesscontrol/acimpl/service_test.go b/pkg/services/accesscontrol/acimpl/service_test.go index e5df3ada30c..a9713529f6b 100644 --- a/pkg/services/accesscontrol/acimpl/service_test.go +++ b/pkg/services/accesscontrol/acimpl/service_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/localcache" @@ -546,7 +547,7 @@ func TestService_SearchUsersPermissions(t *testing.T) { // only the user's basic roles and the user's stored permissions name: "check namespacedId filter works correctly", siuPermissions: listAllPerms, - searchOption: accesscontrol.SearchOptions{TypedID: identity.NewTypedID(identity.TypeServiceAccount, 1)}, + searchOption: accesscontrol.SearchOptions{TypedID: identity.NewTypedID(claims.TypeServiceAccount, 1)}, ramRoles: map[string]*accesscontrol.RoleDTO{ string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{ {Action: accesscontrol.ActionTeamsRead, Scope: "teams:*"}, @@ -618,7 +619,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "ram only", searchOption: accesscontrol.SearchOptions{ ActionPrefix: "teams", - TypedID: identity.NewTypedID(identity.TypeUser, 2), + TypedID: identity.NewTypedID(claims.TypeUser, 2), }, ramRoles: map[string]*accesscontrol.RoleDTO{ string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{ @@ -643,7 +644,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "stored only", searchOption: accesscontrol.SearchOptions{ ActionPrefix: "teams", - TypedID: identity.NewTypedID(identity.TypeUser, 2), + TypedID: identity.NewTypedID(claims.TypeUser, 2), }, storedPerms: map[int64][]accesscontrol.Permission{ 1: {{Action: accesscontrol.ActionTeamsRead, Scope: "teams:id:1"}}, @@ -663,7 +664,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "ram and stored", searchOption: accesscontrol.SearchOptions{ ActionPrefix: "teams", - TypedID: identity.NewTypedID(identity.TypeUser, 2), + TypedID: identity.NewTypedID(claims.TypeUser, 2), }, ramRoles: map[string]*accesscontrol.RoleDTO{ string(identity.RoleAdmin): {Permissions: []accesscontrol.Permission{ @@ -693,7 +694,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "check action prefix filter works correctly", searchOption: accesscontrol.SearchOptions{ ActionPrefix: "teams", - TypedID: identity.NewTypedID(identity.TypeUser, 1), + TypedID: identity.NewTypedID(claims.TypeUser, 1), }, ramRoles: map[string]*accesscontrol.RoleDTO{ string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{ @@ -715,7 +716,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "check action filter works correctly", searchOption: accesscontrol.SearchOptions{ Action: accesscontrol.ActionTeamsRead, - TypedID: identity.NewTypedID(identity.TypeUser, 1), + TypedID: identity.NewTypedID(claims.TypeUser, 1), }, ramRoles: map[string]*accesscontrol.RoleDTO{ string(identity.RoleEditor): {Permissions: []accesscontrol.Permission{ @@ -737,7 +738,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "check action sets are correctly included if an action is specified", searchOption: accesscontrol.SearchOptions{ Action: "dashboards:read", - TypedID: identity.NewTypedID(identity.TypeUser, 1), + TypedID: identity.NewTypedID(claims.TypeUser, 1), }, withActionSets: true, actionSets: map[string][]string{ @@ -770,7 +771,7 @@ func TestService_SearchUserPermissions(t *testing.T) { name: "check action sets are correctly included if an action prefix is specified", searchOption: accesscontrol.SearchOptions{ ActionPrefix: "dashboards", - TypedID: identity.NewTypedID(identity.TypeUser, 1), + TypedID: identity.NewTypedID(claims.TypeUser, 1), }, withActionSets: true, actionSets: map[string][]string{ diff --git a/pkg/services/accesscontrol/authorize_in_org_test.go b/pkg/services/accesscontrol/authorize_in_org_test.go index 44f41d37bb0..e85ced6c2bd 100644 --- a/pkg/services/accesscontrol/authorize_in_org_test.go +++ b/pkg/services/accesscontrol/authorize_in_org_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/acimpl" @@ -187,7 +188,7 @@ func TestAuthorizeInOrgMiddleware(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/api/endpoint", nil) expectedIdentity := &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, tc.ctxSignedInUser.UserID), + ID: identity.NewTypedID(claims.TypeUser, tc.ctxSignedInUser.UserID), OrgID: tc.targetOrgId, Permissions: map[int64]map[string][]string{}, } diff --git a/pkg/services/accesscontrol/cacheutils_test.go b/pkg/services/accesscontrol/cacheutils_test.go index b65ec9485d6..7aff7941586 100644 --- a/pkg/services/accesscontrol/cacheutils_test.go +++ b/pkg/services/accesscontrol/cacheutils_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" ) @@ -21,7 +21,7 @@ func TestPermissionCacheKey(t *testing.T) { signedInUser: &user.SignedInUser{ OrgID: 1, UserID: 1, - FallbackType: identity.TypeUser, + FallbackType: claims.TypeUser, }, expected: "rbac-permissions-1-user-1", }, @@ -31,7 +31,7 @@ func TestPermissionCacheKey(t *testing.T) { OrgID: 1, ApiKeyID: 1, IsServiceAccount: false, - FallbackType: identity.TypeUser, + FallbackType: claims.TypeUser, }, expected: "rbac-permissions-1-api-key-1", }, @@ -41,7 +41,7 @@ func TestPermissionCacheKey(t *testing.T) { OrgID: 1, UserID: 1, IsServiceAccount: true, - FallbackType: identity.TypeUser, + FallbackType: claims.TypeUser, }, expected: "rbac-permissions-1-service-account-1", }, @@ -51,7 +51,7 @@ func TestPermissionCacheKey(t *testing.T) { OrgID: 1, UserID: -1, IsServiceAccount: true, - FallbackType: identity.TypeUser, // NOTE, this is still a service account! + FallbackType: claims.TypeUser, // NOTE, this is still a service account! }, expected: "rbac-permissions-1-service-account--1", }, @@ -60,7 +60,7 @@ func TestPermissionCacheKey(t *testing.T) { signedInUser: &user.SignedInUser{ OrgID: 1, OrgRole: org.RoleNone, - FallbackType: identity.TypeUser, + FallbackType: claims.TypeUser, }, expected: "rbac-permissions-1-user-None", }, diff --git a/pkg/services/accesscontrol/database/database_test.go b/pkg/services/accesscontrol/database/database_test.go index 8099a22e910..3ec2950d088 100644 --- a/pkg/services/accesscontrol/database/database_test.go +++ b/pkg/services/accesscontrol/database/database_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/localcache" @@ -626,7 +627,7 @@ func TestIntegrationAccessControlStore_SearchUsersPermissions(t *testing.T) { }, options: accesscontrol.SearchOptions{ ActionPrefix: "teams:", - TypedID: identity.NewTypedID(identity.TypeUser, 1), + TypedID: identity.NewTypedID(claims.TypeUser, 1), }, wantPerm: map[int64][]accesscontrol.Permission{ 1: {{Action: "teams:read", Scope: "teams:id:1"}, {Action: "teams:read", Scope: "teams:id:10"}, diff --git a/pkg/services/anonymous/anonimpl/client.go b/pkg/services/anonymous/anonimpl/client.go index 587f1c2fd78..9af940ead3d 100644 --- a/pkg/services/anonymous/anonimpl/client.go +++ b/pkg/services/anonymous/anonimpl/client.go @@ -6,6 +6,7 @@ import ( "net/http" "strings" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" @@ -69,8 +70,8 @@ func (a *Anonymous) Test(ctx context.Context, r *authn.Request) bool { return true } -func (a *Anonymous) IdentityType() identity.IdentityType { - return identity.TypeAnonymous +func (a *Anonymous) IdentityType() claims.IdentityType { + return claims.TypeAnonymous } func (a *Anonymous) ResolveIdentity(ctx context.Context, orgID int64, namespaceID identity.TypedID) (*authn.Identity, error) { diff --git a/pkg/services/auth/idimpl/service.go b/pkg/services/auth/idimpl/service.go index 4f9e37f2618..1c696255b34 100644 --- a/pkg/services/auth/idimpl/service.go +++ b/pkg/services/auth/idimpl/service.go @@ -8,6 +8,7 @@ import ( "github.com/go-jose/go-jose/v3/jwt" authnlib "github.com/grafana/authlib/authn" + authnlibclaims "github.com/grafana/authlib/claims" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/singleflight" @@ -99,7 +100,7 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri }, } - if identity.IsIdentityType(id.GetID(), identity.TypeUser) { + if identity.IsIdentityType(id.GetID(), authnlibclaims.TypeUser) { claims.Rest.Email = id.GetEmail() claims.Rest.EmailVerified = id.IsEmailVerified() claims.Rest.AuthenticatedBy = id.GetAuthenticatedBy() diff --git a/pkg/services/auth/idimpl/service_test.go b/pkg/services/auth/idimpl/service_test.go index 3cb8b36a7b9..b0407743679 100644 --- a/pkg/services/auth/idimpl/service_test.go +++ b/pkg/services/auth/idimpl/service_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/remotecache" "github.com/grafana/grafana/pkg/services/auth" @@ -85,7 +86,7 @@ func TestService_SignIdentity(t *testing.T) { ID: identity.MustParseTypedID("user:1"), AuthenticatedBy: login.AzureADAuthModule, Login: "U1", - UID: identity.NewTypedIDString(identity.TypeUser, "edpu3nnt61se8e")}) + UID: identity.NewTypedIDString(claims.TypeUser, "edpu3nnt61se8e")}) require.NoError(t, err) parsed, err := jwt.ParseSigned(token) @@ -108,7 +109,7 @@ func TestService_SignIdentity(t *testing.T) { ID: identity.MustParseTypedID("user:1"), AuthenticatedBy: login.AzureADAuthModule, Login: "U1", - UID: identity.NewTypedIDString(identity.TypeUser, "edpu3nnt61se8e")}) + UID: identity.NewTypedIDString(claims.TypeUser, "edpu3nnt61se8e")}) require.NoError(t, err) assert.Equal(t, login.AzureADAuthModule, gotClaims.Rest.AuthenticatedBy) diff --git a/pkg/services/authn/authn.go b/pkg/services/authn/authn.go index af7e20923db..f7f2686a134 100644 --- a/pkg/services/authn/authn.go +++ b/pkg/services/authn/authn.go @@ -8,6 +8,7 @@ import ( "strconv" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/middleware/cookies" @@ -179,7 +180,7 @@ type UsageStatClient interface { // Clients that implements this interface can resolve an full identity from an orgID and namespaceID. type IdentityResolverClient interface { Client - IdentityType() identity.IdentityType + IdentityType() claims.IdentityType ResolveIdentity(ctx context.Context, orgID int64, namespaceID identity.TypedID) (*Identity, error) } diff --git a/pkg/services/authn/authnimpl/service.go b/pkg/services/authn/authnimpl/service.go index 865ad9dca19..63f587e5ca4 100644 --- a/pkg/services/authn/authnimpl/service.go +++ b/pkg/services/authn/authnimpl/service.go @@ -12,6 +12,7 @@ import ( "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" @@ -217,7 +218,7 @@ func (s *Service) Login(ctx context.Context, client string, r *authn.Request) (i } // Login is only supported for users - if !id.ID.IsType(identity.TypeUser) { + if !id.ID.IsType(claims.TypeUser) { s.metrics.failedLogin.WithLabelValues(client).Inc() return nil, authn.ErrUnsupportedIdentity.Errorf("expected identity of type user but got: %s", id.ID.Type()) } @@ -281,7 +282,7 @@ func (s *Service) Logout(ctx context.Context, user identity.Requester, sessionTo redirect.URL = s.cfg.SignoutRedirectUrl } - if !user.GetID().IsType(identity.TypeUser) { + if !user.GetID().IsType(claims.TypeUser) { return redirect, nil } @@ -380,7 +381,7 @@ func (s *Service) resolveIdenity(ctx context.Context, orgID int64, namespaceID i ctx, span := s.tracer.Start(ctx, "authn.resolveIdentity") defer span.End() - if namespaceID.IsType(identity.TypeUser) { + if namespaceID.IsType(claims.TypeUser) { return &authn.Identity{ OrgID: orgID, ID: namespaceID, @@ -391,7 +392,7 @@ func (s *Service) resolveIdenity(ctx context.Context, orgID int64, namespaceID i }}, nil } - if namespaceID.IsType(identity.TypeServiceAccount) { + if namespaceID.IsType(claims.TypeServiceAccount) { return &authn.Identity{ ID: namespaceID, OrgID: orgID, diff --git a/pkg/services/authn/authnimpl/service_test.go b/pkg/services/authn/authnimpl/service_test.go index ca34c134b4d..8eb0f49846c 100644 --- a/pkg/services/authn/authnimpl/service_test.go +++ b/pkg/services/authn/authnimpl/service_test.go @@ -15,6 +15,7 @@ import ( sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" @@ -420,31 +421,31 @@ func TestService_Logout(t *testing.T) { tests := []TestCase{ { desc: "should redirect to default redirect url when identity is not a user", - identity: &authn.Identity{ID: identity.NewTypedID(identity.TypeServiceAccount, 1)}, + identity: &authn.Identity{ID: identity.NewTypedID(claims.TypeServiceAccount, 1)}, expectedRedirect: &authn.Redirect{URL: "http://localhost:3000/login"}, }, { desc: "should redirect to default redirect url when no external provider was used to authenticate", - identity: &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, 1)}, + identity: &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, 1)}, expectedRedirect: &authn.Redirect{URL: "http://localhost:3000/login"}, expectedTokenRevoked: true, }, { desc: "should redirect to default redirect url when client is not found", - identity: &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, 1), AuthenticatedBy: "notfound"}, + identity: &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, 1), AuthenticatedBy: "notfound"}, expectedRedirect: &authn.Redirect{URL: "http://localhost:3000/login"}, expectedTokenRevoked: true, }, { desc: "should redirect to default redirect url when client do not implement logout extension", - identity: &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, 1), AuthenticatedBy: "azuread"}, + identity: &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, 1), AuthenticatedBy: "azuread"}, expectedRedirect: &authn.Redirect{URL: "http://localhost:3000/login"}, client: &authntest.FakeClient{ExpectedName: "auth.client.azuread"}, expectedTokenRevoked: true, }, { desc: "should use signout redirect url if configured", - identity: &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, 1), AuthenticatedBy: "azuread"}, + identity: &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, 1), AuthenticatedBy: "azuread"}, expectedRedirect: &authn.Redirect{URL: "some-url"}, client: &authntest.FakeClient{ExpectedName: "auth.client.azuread"}, signoutRedirectURL: "some-url", @@ -452,7 +453,7 @@ func TestService_Logout(t *testing.T) { }, { desc: "should redirect to client specific url", - identity: &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, 1), AuthenticatedBy: "azuread"}, + identity: &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, 1), AuthenticatedBy: "azuread"}, expectedRedirect: &authn.Redirect{URL: "http://idp.com/logout"}, client: &authntest.MockClient{ NameFunc: func() string { return "auth.client.azuread" }, @@ -527,7 +528,7 @@ func TestService_ResolveIdentity(t *testing.T) { t.Run("should resolve for valid namespace if client is registered", func(t *testing.T) { svc := setupTests(t, func(svc *Service) { svc.RegisterClient(&authntest.MockClient{ - IdentityTypeFunc: func() identity.IdentityType { return identity.TypeAPIKey }, + IdentityTypeFunc: func() claims.IdentityType { return claims.TypeAPIKey }, ResolveIdentityFunc: func(ctx context.Context, orgID int64, namespaceID identity.TypedID) (*authn.Identity, error) { return &authn.Identity{}, nil }, diff --git a/pkg/services/authn/authnimpl/sync/oauth_token_sync.go b/pkg/services/authn/authnimpl/sync/oauth_token_sync.go index 8262d290d93..cecc3cc5218 100644 --- a/pkg/services/authn/authnimpl/sync/oauth_token_sync.go +++ b/pkg/services/authn/authnimpl/sync/oauth_token_sync.go @@ -8,7 +8,7 @@ import ( "golang.org/x/sync/singleflight" - "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/login/social" @@ -42,7 +42,7 @@ func (s *OAuthTokenSync) SyncOauthTokenHook(ctx context.Context, id *authn.Ident defer span.End() // only perform oauth token check if identity is a user - if !id.ID.IsType(identity.TypeUser) { + if !id.ID.IsType(claims.TypeUser) { return nil } diff --git a/pkg/services/authn/authnimpl/sync/org_sync.go b/pkg/services/authn/authnimpl/sync/org_sync.go index 9fc5b1d3e7c..0ef1d5150d2 100644 --- a/pkg/services/authn/authnimpl/sync/org_sync.go +++ b/pkg/services/authn/authnimpl/sync/org_sync.go @@ -6,7 +6,7 @@ import ( "fmt" "sort" - "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -39,7 +39,7 @@ func (s *OrgSync) SyncOrgRolesHook(ctx context.Context, id *authn.Identity, _ *a ctxLogger := s.log.FromContext(ctx).New("id", id.ID, "login", id.Login) - if !id.ID.IsType(identity.TypeUser) { + if !id.ID.IsType(claims.TypeUser) { ctxLogger.Warn("Failed to sync org role, invalid namespace for identity", "type", id.ID.Type()) return nil } @@ -145,7 +145,7 @@ func (s *OrgSync) SetDefaultOrgHook(ctx context.Context, currentIdentity *authn. ctxLogger := s.log.FromContext(ctx) - if !currentIdentity.ID.IsType(identity.TypeUser) { + if !currentIdentity.ID.IsType(claims.TypeUser) { ctxLogger.Debug("Skipping default org sync, not a user", "type", currentIdentity.ID.Type()) return } diff --git a/pkg/services/authn/authnimpl/sync/rbac_sync.go b/pkg/services/authn/authnimpl/sync/rbac_sync.go index 95d7b0b44b5..e9ed94ff0d6 100644 --- a/pkg/services/authn/authnimpl/sync/rbac_sync.go +++ b/pkg/services/authn/authnimpl/sync/rbac_sync.go @@ -6,8 +6,8 @@ import ( "golang.org/x/exp/maps" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/accesscontrol" @@ -148,7 +148,7 @@ func (s *RBACSync) SyncCloudRoles(ctx context.Context, ident *authn.Identity, r return nil } - if !ident.ID.IsType(identity.TypeUser) { + if !ident.ID.IsType(claims.TypeUser) { s.log.FromContext(ctx).Debug("Skip syncing cloud role", "id", ident.ID) return nil } diff --git a/pkg/services/authn/authnimpl/sync/rbac_sync_test.go b/pkg/services/authn/authnimpl/sync/rbac_sync_test.go index 1b0f54ef58e..f38b83679ed 100644 --- a/pkg/services/authn/authnimpl/sync/rbac_sync_test.go +++ b/pkg/services/authn/authnimpl/sync/rbac_sync_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/tracing" @@ -67,7 +68,7 @@ func TestRBACSync_SyncCloudRoles(t *testing.T) { desc: "should call sync when authenticated with grafana com and has viewer role", module: login.GrafanaComAuthModule, identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleViewer}, }, @@ -78,7 +79,7 @@ func TestRBACSync_SyncCloudRoles(t *testing.T) { desc: "should call sync when authenticated with grafana com and has editor role", module: login.GrafanaComAuthModule, identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleEditor}, }, @@ -89,7 +90,7 @@ func TestRBACSync_SyncCloudRoles(t *testing.T) { desc: "should call sync when authenticated with grafana com and has admin role", module: login.GrafanaComAuthModule, identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleAdmin}, }, @@ -100,7 +101,7 @@ func TestRBACSync_SyncCloudRoles(t *testing.T) { desc: "should not call sync when authenticated with grafana com and has invalid role", module: login.GrafanaComAuthModule, identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleType("something else")}, }, @@ -111,7 +112,7 @@ func TestRBACSync_SyncCloudRoles(t *testing.T) { desc: "should not call sync when not authenticated with grafana com", module: login.LDAPAuthModule, identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleAdmin}, }, @@ -157,7 +158,7 @@ func TestRBACSync_cloudRolesToAddAndRemove(t *testing.T) { { desc: "should map Cloud Viewer to Grafana Cloud Viewer and Support ticket reader", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleViewer}, }, @@ -176,7 +177,7 @@ func TestRBACSync_cloudRolesToAddAndRemove(t *testing.T) { { desc: "should map Cloud Editor to Grafana Cloud Editor and Support ticket admin", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleEditor}, }, @@ -194,7 +195,7 @@ func TestRBACSync_cloudRolesToAddAndRemove(t *testing.T) { { desc: "should map Cloud Admin to Grafana Cloud Admin and Support ticket admin", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleAdmin}, }, @@ -212,7 +213,7 @@ func TestRBACSync_cloudRolesToAddAndRemove(t *testing.T) { { desc: "should return an error for not supported role", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1, OrgRoles: map[int64]org.RoleType{1: org.RoleNone}, }, diff --git a/pkg/services/authn/authnimpl/sync/user_sync.go b/pkg/services/authn/authnimpl/sync/user_sync.go index 622846e5ac6..4c00d809b38 100644 --- a/pkg/services/authn/authnimpl/sync/user_sync.go +++ b/pkg/services/authn/authnimpl/sync/user_sync.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" @@ -118,7 +119,7 @@ func (s *UserSync) FetchSyncedUserHook(ctx context.Context, id *authn.Identity, return nil } - if !id.ID.IsType(identity.TypeUser, identity.TypeServiceAccount) { + if !id.ID.IsType(claims.TypeUser, claims.TypeServiceAccount) { return nil } @@ -159,7 +160,7 @@ func (s *UserSync) SyncLastSeenHook(ctx context.Context, id *authn.Identity, r * return nil } - if !id.ID.IsType(identity.TypeUser, identity.TypeServiceAccount) { + if !id.ID.IsType(claims.TypeUser, claims.TypeServiceAccount) { return nil } @@ -195,7 +196,7 @@ func (s *UserSync) EnableUserHook(ctx context.Context, id *authn.Identity, _ *au return nil } - if !id.ID.IsType(identity.TypeUser) { + if !id.ID.IsType(claims.TypeUser) { return nil } @@ -418,8 +419,8 @@ func (s *UserSync) lookupByOneOf(ctx context.Context, params login.UserLookupPar // syncUserToIdentity syncs a user to an identity. // This is used to update the identity with the latest user information. func syncUserToIdentity(usr *user.User, id *authn.Identity) { - id.ID = identity.NewTypedID(identity.TypeUser, usr.ID) - id.UID = identity.NewTypedIDString(identity.TypeUser, usr.UID) + id.ID = identity.NewTypedID(claims.TypeUser, usr.ID) + id.UID = identity.NewTypedIDString(claims.TypeUser, usr.UID) id.Login = usr.Login id.Email = usr.Email id.Name = usr.Name @@ -429,11 +430,11 @@ func syncUserToIdentity(usr *user.User, id *authn.Identity) { // syncSignedInUserToIdentity syncs a user to an identity. func syncSignedInUserToIdentity(usr *user.SignedInUser, id *authn.Identity) { - var ns identity.IdentityType - if id.ID.IsType(identity.TypeServiceAccount) { - ns = identity.TypeServiceAccount + var ns claims.IdentityType + if id.ID.IsType(claims.TypeServiceAccount) { + ns = claims.TypeServiceAccount } else { - ns = identity.TypeUser + ns = claims.TypeUser } id.UID = identity.NewTypedIDString(ns, usr.UserUID) diff --git a/pkg/services/authn/authnimpl/sync/user_sync_test.go b/pkg/services/authn/authnimpl/sync/user_sync_test.go index 3363f421662..82d76e729aa 100644 --- a/pkg/services/authn/authnimpl/sync/user_sync_test.go +++ b/pkg/services/authn/authnimpl/sync/user_sync_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/authn" @@ -484,7 +485,7 @@ func TestUserSync_EnableDisabledUserHook(t *testing.T) { { desc: "should skip if correct flag is not set", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), IsDisabled: true, ClientParams: authn.ClientParams{EnableUser: false}, }, @@ -493,7 +494,7 @@ func TestUserSync_EnableDisabledUserHook(t *testing.T) { { desc: "should skip if identity is not a user", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeAPIKey, 1), + ID: identity.NewTypedID(claims.TypeAPIKey, 1), IsDisabled: true, ClientParams: authn.ClientParams{EnableUser: true}, }, @@ -502,7 +503,7 @@ func TestUserSync_EnableDisabledUserHook(t *testing.T) { { desc: "should enabled disabled user", identity: &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, 1), + ID: identity.NewTypedID(claims.TypeUser, 1), IsDisabled: true, ClientParams: authn.ClientParams{EnableUser: true}, }, diff --git a/pkg/services/authn/authntest/mock.go b/pkg/services/authn/authntest/mock.go index a4934ca640b..81dad579b61 100644 --- a/pkg/services/authn/authntest/mock.go +++ b/pkg/services/authn/authntest/mock.go @@ -3,6 +3,7 @@ package authntest import ( "context" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/models/usertoken" "github.com/grafana/grafana/pkg/services/authn" @@ -77,7 +78,7 @@ type MockClient struct { PriorityFunc func() uint HookFunc func(ctx context.Context, identity *authn.Identity, r *authn.Request) error LogoutFunc func(ctx context.Context, user identity.Requester) (*authn.Redirect, bool) - IdentityTypeFunc func() identity.IdentityType + IdentityTypeFunc func() claims.IdentityType ResolveIdentityFunc func(ctx context.Context, orgID int64, namespaceID identity.TypedID) (*authn.Identity, error) } @@ -127,11 +128,11 @@ func (m *MockClient) Logout(ctx context.Context, user identity.Requester) (*auth return nil, false } -func (m *MockClient) IdentityType() identity.IdentityType { +func (m *MockClient) IdentityType() claims.IdentityType { if m.IdentityTypeFunc != nil { return m.IdentityTypeFunc() } - return identity.TypeEmpty + return claims.TypeEmpty } // ResolveIdentity implements authn.IdentityResolverClient. diff --git a/pkg/services/authn/clients/api_key.go b/pkg/services/authn/clients/api_key.go index bfbe8b59bd1..fdab410c979 100644 --- a/pkg/services/authn/clients/api_key.go +++ b/pkg/services/authn/clients/api_key.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/components/apikeygen" @@ -135,12 +136,12 @@ func (s *APIKey) Priority() uint { return 30 } -func (s *APIKey) IdentityType() identity.IdentityType { - return identity.TypeAPIKey +func (s *APIKey) IdentityType() claims.IdentityType { + return claims.TypeAPIKey } func (s *APIKey) ResolveIdentity(ctx context.Context, orgID int64, namespaceID identity.TypedID) (*authn.Identity, error) { - if !namespaceID.IsType(identity.TypeAPIKey) { + if !namespaceID.IsType(claims.TypeAPIKey) { return nil, identity.ErrInvalidTypedID.Errorf("got unspected namespace: %s", namespaceID.Type()) } @@ -195,11 +196,11 @@ func (s *APIKey) getAPIKeyID(ctx context.Context, id *authn.Identity, r *authn.R return -1, false } - if id.ID.IsType(identity.TypeAPIKey) { + if id.ID.IsType(claims.TypeAPIKey) { return internalId, true } - if id.ID.IsType(identity.TypeServiceAccount) { + if id.ID.IsType(claims.TypeServiceAccount) { // When the identity is service account, the ID in from the namespace is the service account ID. // We need to fetch the API key in this scenario, as we could use it to uniquely identify a service account token. apiKey, err := s.getAPIKey(ctx, getTokenFromRequest(r)) @@ -256,7 +257,7 @@ func validateApiKey(orgID int64, key *apikey.APIKey) error { func newAPIKeyIdentity(key *apikey.APIKey) *authn.Identity { return &authn.Identity{ - ID: identity.NewTypedID(identity.TypeAPIKey, key.ID), + ID: identity.NewTypedID(claims.TypeAPIKey, key.ID), OrgID: key.OrgID, OrgRoles: map[int64]org.RoleType{key.OrgID: key.Role}, ClientParams: authn.ClientParams{SyncPermissions: true}, @@ -266,7 +267,7 @@ func newAPIKeyIdentity(key *apikey.APIKey) *authn.Identity { func newServiceAccountIdentity(key *apikey.APIKey) *authn.Identity { return &authn.Identity{ - ID: identity.NewTypedID(identity.TypeServiceAccount, *key.ServiceAccountId), + ID: identity.NewTypedID(claims.TypeServiceAccount, *key.ServiceAccountId), OrgID: key.OrgID, AuthenticatedBy: login.APIKeyAuthModule, ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true}, diff --git a/pkg/services/authn/clients/ext_jwt.go b/pkg/services/authn/clients/ext_jwt.go index af0682b8bd9..f44d1705d6b 100644 --- a/pkg/services/authn/clients/ext_jwt.go +++ b/pkg/services/authn/clients/ext_jwt.go @@ -8,7 +8,7 @@ import ( "github.com/go-jose/go-jose/v3/jwt" authlib "github.com/grafana/authlib/authn" - + authlibclaims "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" @@ -114,7 +114,7 @@ func (s *ExtendedJWT) authenticateAsUser( return nil, errExtJWTInvalidSubject.Errorf("unexpected identity: %s", accessID.String()) } - if !accessID.IsType(identity.TypeAccessPolicy) { + if !accessID.IsType(authlibclaims.TypeAccessPolicy) { return nil, errExtJWTInvalid.Errorf("unexpected identity: %s", accessID.String()) } @@ -123,7 +123,7 @@ func (s *ExtendedJWT) authenticateAsUser( return nil, errExtJWTInvalid.Errorf("failed to parse id token subject: %w", err) } - if !userID.IsType(identity.TypeUser) { + if !userID.IsType(authlibclaims.TypeUser) { return nil, errExtJWTInvalidSubject.Errorf("unexpected identity: %s", userID.String()) } @@ -160,7 +160,7 @@ func (s *ExtendedJWT) authenticateAsService(claims *authlib.Claims[authlib.Acces return nil, fmt.Errorf("failed to parse access token subject: %w", err) } - if !id.IsType(identity.TypeAccessPolicy) { + if !id.IsType(authlibclaims.TypeAccessPolicy) { return nil, errExtJWTInvalidSubject.Errorf("unexpected identity: %s", id.String()) } diff --git a/pkg/services/authn/clients/ext_jwt_test.go b/pkg/services/authn/clients/ext_jwt_test.go index de612aecf4c..e87feec4c61 100644 --- a/pkg/services/authn/clients/ext_jwt_test.go +++ b/pkg/services/authn/clients/ext_jwt_test.go @@ -11,15 +11,12 @@ import ( "github.com/go-jose/go-jose/v3" "github.com/go-jose/go-jose/v3/jwt" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - authnlib "github.com/grafana/authlib/authn" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type ( diff --git a/pkg/services/authn/clients/grafana.go b/pkg/services/authn/clients/grafana.go index 32ce5725a08..04de6c88151 100644 --- a/pkg/services/authn/clients/grafana.go +++ b/pkg/services/authn/clients/grafana.go @@ -6,6 +6,7 @@ import ( "errors" "net/mail" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/login" @@ -106,7 +107,7 @@ func (c *Grafana) AuthenticatePassword(ctx context.Context, r *authn.Request, us } return &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, usr.ID), + ID: identity.NewTypedID(claims.TypeUser, usr.ID), OrgID: r.OrgID, ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true}, AuthenticatedBy: login.PasswordAuthModule, diff --git a/pkg/services/authn/clients/oauth_test.go b/pkg/services/authn/clients/oauth_test.go index c23c2c348d9..9ae0de62a3a 100644 --- a/pkg/services/authn/clients/oauth_test.go +++ b/pkg/services/authn/clients/oauth_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/login/social/socialtest" @@ -485,7 +486,7 @@ func TestOAuth_Logout(t *testing.T) { } c := ProvideOAuth(authn.ClientWithPrefix("azuread"), tt.cfg, mockService, fakeSocialSvc, &setting.OSSImpl{Cfg: tt.cfg}, featuremgmt.WithFeatures()) - redirect, ok := c.Logout(context.Background(), &authn.Identity{ID: identity.NewTypedIDString(identity.TypeUser, "1")}) + redirect, ok := c.Logout(context.Background(), &authn.Identity{ID: identity.NewTypedIDString(claims.TypeUser, "1")}) assert.Equal(t, tt.expectedOK, ok) if tt.expectedOK { diff --git a/pkg/services/authn/clients/proxy.go b/pkg/services/authn/clients/proxy.go index 9f853ccc889..7c838d119ef 100644 --- a/pkg/services/authn/clients/proxy.go +++ b/pkg/services/authn/clients/proxy.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" @@ -125,7 +126,7 @@ func (c *Proxy) retrieveIDFromCache(ctx context.Context, cacheKey string, r *aut } return &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, uid), + ID: identity.NewTypedID(claims.TypeUser, uid), OrgID: r.OrgID, // FIXME: This does not match the actual auth module used, but should not have any impact // Maybe caching the auth module used with the user ID would be a good idea @@ -150,7 +151,7 @@ func (c *Proxy) Hook(ctx context.Context, id *authn.Identity, r *authn.Request) return nil } - if !id.ID.IsType(identity.TypeUser) { + if !id.ID.IsType(claims.TypeUser) { return nil } diff --git a/pkg/services/authn/clients/proxy_test.go b/pkg/services/authn/clients/proxy_test.go index 59bec50fbaf..7aa39efaa71 100644 --- a/pkg/services/authn/clients/proxy_test.go +++ b/pkg/services/authn/clients/proxy_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn/authntest" @@ -204,7 +205,7 @@ func TestProxy_Hook(t *testing.T) { } cache := &fakeCache{data: make(map[string][]byte)} userId := int64(1) - userID := identity.NewTypedID(identity.TypeUser, userId) + userID := identity.NewTypedID(claims.TypeUser, userId) // withRole creates a test case for a user with a specific role. withRole := func(role string) func(t *testing.T) { diff --git a/pkg/services/authn/clients/render.go b/pkg/services/authn/clients/render.go index 9491d946e39..6ed57114af0 100644 --- a/pkg/services/authn/clients/render.go +++ b/pkg/services/authn/clients/render.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/authn" @@ -43,7 +44,7 @@ func (c *Render) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide if renderUsr.UserID <= 0 { return &authn.Identity{ - ID: identity.NewTypedID(identity.TypeRenderService, 0), + ID: identity.NewTypedID(claims.TypeRenderService, 0), OrgID: renderUsr.OrgID, OrgRoles: map[int64]org.RoleType{renderUsr.OrgID: org.RoleType(renderUsr.OrgRole)}, ClientParams: authn.ClientParams{SyncPermissions: true}, @@ -53,7 +54,7 @@ func (c *Render) Authenticate(ctx context.Context, r *authn.Request) (*authn.Ide } return &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, renderUsr.UserID), + ID: identity.NewTypedID(claims.TypeUser, renderUsr.UserID), LastSeenAt: time.Now(), AuthenticatedBy: login.RenderModule, ClientParams: authn.ClientParams{FetchSyncedUser: true, SyncPermissions: true}, diff --git a/pkg/services/authn/clients/session.go b/pkg/services/authn/clients/session.go index 3527d372d98..9945612c79f 100644 --- a/pkg/services/authn/clients/session.go +++ b/pkg/services/authn/clients/session.go @@ -6,6 +6,7 @@ import ( "net/url" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/auth" @@ -58,7 +59,7 @@ func (s *Session) Authenticate(ctx context.Context, r *authn.Request) (*authn.Id } ident := &authn.Identity{ - ID: identity.NewTypedID(identity.TypeUser, token.UserId), + ID: identity.NewTypedID(claims.TypeUser, token.UserId), SessionToken: token, ClientParams: authn.ClientParams{ FetchSyncedUser: true, diff --git a/pkg/services/authn/identity.go b/pkg/services/authn/identity.go index a1b9da18c62..aa16ebb636f 100644 --- a/pkg/services/authn/identity.go +++ b/pkg/services/authn/identity.go @@ -99,7 +99,7 @@ func (i *Identity) GetInternalID() (int64, error) { } // GetIdentityType implements Requester. -func (i *Identity) GetIdentityType() identity.IdentityType { +func (i *Identity) GetIdentityType() claims.IdentityType { return i.UID.Type() } @@ -243,9 +243,9 @@ func (i *Identity) HasRole(role org.RoleType) bool { func (i *Identity) HasUniqueId() bool { typ := i.GetID().Type() - return typ == identity.TypeUser || - typ == identity.TypeServiceAccount || - typ == identity.TypeAPIKey + return typ == claims.TypeUser || + typ == claims.TypeServiceAccount || + typ == claims.TypeAPIKey } func (i *Identity) IsAuthenticatedBy(providers ...string) bool { @@ -273,7 +273,7 @@ func (i *Identity) SignedInUser() *user.SignedInUser { AuthID: i.AuthID, AuthenticatedBy: i.AuthenticatedBy, IsGrafanaAdmin: i.GetIsGrafanaAdmin(), - IsAnonymous: i.ID.IsType(identity.TypeAnonymous), + IsAnonymous: i.ID.IsType(claims.TypeAnonymous), IsDisabled: i.IsDisabled, HelpFlags1: i.HelpFlags1, LastSeenAt: i.LastSeenAt, @@ -283,14 +283,14 @@ func (i *Identity) SignedInUser() *user.SignedInUser { FallbackType: i.ID.Type(), } - if i.ID.IsType(identity.TypeAPIKey) { + if i.ID.IsType(claims.TypeAPIKey) { id, _ := i.ID.ParseInt() u.ApiKeyID = id } else { id, _ := i.ID.UserID() u.UserID = id u.UserUID = i.UID.ID() - u.IsServiceAccount = i.ID.IsType(identity.TypeServiceAccount) + u.IsServiceAccount = i.ID.IsType(claims.TypeServiceAccount) } return u diff --git a/pkg/services/contexthandler/contexthandler.go b/pkg/services/contexthandler/contexthandler.go index 24ac584a9b8..d5ea06013fa 100644 --- a/pkg/services/contexthandler/contexthandler.go +++ b/pkg/services/contexthandler/contexthandler.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" + "github.com/grafana/authlib/claims" authnClients "github.com/grafana/grafana/pkg/services/authn/clients" "github.com/grafana/grafana/pkg/api/response" @@ -157,9 +158,9 @@ func (h *ContextHandler) addIDHeaderEndOfRequestFunc(ident identity.Requester) w id := ident.GetID() if !identity.IsIdentityType( id, - identity.TypeUser, - identity.TypeServiceAccount, - identity.TypeAPIKey, + claims.TypeUser, + claims.TypeServiceAccount, + claims.TypeAPIKey, ) || id.ID() == "0" { return } diff --git a/pkg/services/contexthandler/contexthandler_test.go b/pkg/services/contexthandler/contexthandler_test.go index ae8807ad209..27f3e1cd03c 100644 --- a/pkg/services/contexthandler/contexthandler_test.go +++ b/pkg/services/contexthandler/contexthandler_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/tracing" @@ -43,7 +44,7 @@ func TestContextHandler(t *testing.T) { }) t.Run("should set identity on successful authentication", func(t *testing.T) { - id := &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, 1), OrgID: 1} + id := &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, 1), OrgID: 1} handler := contexthandler.ProvideService( setting.NewCfg(), tracing.InitializeTracerForTest(), diff --git a/pkg/services/dashboards/database/database.go b/pkg/services/dashboards/database/database.go index 17b9d752c5e..4b85d539a8a 100644 --- a/pkg/services/dashboards/database/database.go +++ b/pkg/services/dashboards/database/database.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" @@ -879,7 +879,7 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F } // only list k6 folders when requested by a service account - prevents showing k6 folders in the UI for users - if query.SignedInUser == nil || query.SignedInUser.GetID().Type() != identity.TypeServiceAccount { + if query.SignedInUser == nil || query.SignedInUser.GetID().Type() != claims.TypeServiceAccount { filters = append(filters, searchstore.K6FolderFilter{}) } diff --git a/pkg/services/dashboards/service/dashboard_service.go b/pkg/services/dashboards/service/dashboard_service.go index 7d70832a8d6..d3dc27e2b28 100644 --- a/pkg/services/dashboards/service/dashboard_service.go +++ b/pkg/services/dashboards/service/dashboard_service.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" "github.com/prometheus/client_golang/prometheus" "golang.org/x/exp/slices" @@ -492,7 +493,7 @@ func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto * userID, err := identity.IntIdentifier(dto.User.GetID()) if err != nil { dr.log.Error("Could not make user admin", "dashboard", dash.Title, "id", dto.User.GetID(), "error", err) - } else if identity.IsIdentityType(dto.User.GetID(), identity.TypeUser) { + } else if identity.IsIdentityType(dto.User.GetID(), claims.TypeUser) { permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(), }) @@ -528,7 +529,7 @@ func (dr *DashboardServiceImpl) setDefaultFolderPermissions(ctx context.Context, userID, err := identity.IntIdentifier(cmd.SignedInUser.GetID()) if err != nil { dr.log.Error("Could not make user admin", "folder", cmd.Title, "id", cmd.SignedInUser.GetID()) - } else if identity.IsIdentityType(cmd.SignedInUser.GetID(), identity.TypeUser) { + } else if identity.IsIdentityType(cmd.SignedInUser.GetID(), claims.TypeUser) { permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{ UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(), }) diff --git a/pkg/services/dashboardsnapshots/database/database.go b/pkg/services/dashboardsnapshots/database/database.go index a040133e43a..bcc7c9c32e8 100644 --- a/pkg/services/dashboardsnapshots/database/database.go +++ b/pkg/services/dashboardsnapshots/database/database.go @@ -4,6 +4,7 @@ import ( "context" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/db" @@ -123,7 +124,7 @@ func (d *DashboardSnapshotStore) SearchDashboardSnapshots(ctx context.Context, q } var userID int64 - if identity.IsIdentityType(query.SignedInUser.GetID(), identity.TypeUser, identity.TypeServiceAccount) { + if identity.IsIdentityType(query.SignedInUser.GetID(), claims.TypeUser, claims.TypeServiceAccount) { var err error userID, err = identity.UserIdentifier(query.SignedInUser.GetID()) if err != nil { diff --git a/pkg/services/folder/folderimpl/sqlstore.go b/pkg/services/folder/folderimpl/sqlstore.go index d5c1e1c54af..d2b0c7a2bc3 100644 --- a/pkg/services/folder/folderimpl/sqlstore.go +++ b/pkg/services/folder/folderimpl/sqlstore.go @@ -7,9 +7,9 @@ import ( "strings" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/dskit/concurrency" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/metrics" @@ -323,7 +323,7 @@ func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetChildrenQuery) } // only list k6 folders when requested by a service account - prevents showing k6 folders in the UI for users - if q.SignedInUser == nil || q.SignedInUser.GetID().Type() != identity.TypeServiceAccount { + if q.SignedInUser == nil || q.SignedInUser.GetID().Type() != claims.TypeServiceAccount { sql.WriteString(" AND uid != ?") args = append(args, accesscontrol.K6FolderUID) } @@ -484,7 +484,7 @@ func (ss *sqlStore) GetFolders(ctx context.Context, q getFoldersQuery) ([]*folde } // only list k6 folders when requested by a service account - prevents showing k6 folders in the UI for users - if q.SignedInUser == nil || q.SignedInUser.GetID().Type() != identity.TypeServiceAccount { + if q.SignedInUser == nil || q.SignedInUser.GetID().Type() != claims.TypeServiceAccount { s.WriteString(" AND f0.uid != ? AND (f0.parent_uid != ? OR f0.parent_uid IS NULL)") args = append(args, accesscontrol.K6FolderUID, accesscontrol.K6FolderUID) } diff --git a/pkg/services/oauthtoken/oauth_token.go b/pkg/services/oauthtoken/oauth_token.go index fa351fddeae..cc736d959b4 100644 --- a/pkg/services/oauthtoken/oauth_token.go +++ b/pkg/services/oauthtoken/oauth_token.go @@ -12,6 +12,7 @@ import ( "golang.org/x/oauth2" "golang.org/x/sync/singleflight" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/log" @@ -94,7 +95,7 @@ func (o *Service) HasOAuthEntry(ctx context.Context, usr identity.Requester) (*l return nil, false, nil } - if !identity.IsIdentityType(usr.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(usr.GetID(), claims.TypeUser) { return nil, false, nil } @@ -135,7 +136,7 @@ func (o *Service) TryTokenRefresh(ctx context.Context, usr identity.Requester) e return nil } - if !identity.IsIdentityType(usr.GetID(), identity.TypeUser) { + if !identity.IsIdentityType(usr.GetID(), claims.TypeUser) { ctxLogger.Warn("Can only refresh OAuth tokens for users", "id", usr.GetID()) return nil } diff --git a/pkg/services/pluginsintegration/clientmiddleware/user_header_middleware.go b/pkg/services/pluginsintegration/clientmiddleware/user_header_middleware.go index 6caebc23859..e15ae29e7db 100644 --- a/pkg/services/pluginsintegration/clientmiddleware/user_header_middleware.go +++ b/pkg/services/pluginsintegration/clientmiddleware/user_header_middleware.go @@ -3,6 +3,7 @@ package clientmiddleware import ( "context" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -35,7 +36,7 @@ func (m *UserHeaderMiddleware) applyUserHeader(ctx context.Context, h backend.Fo } h.DeleteHTTPHeader(proxyutil.UserHeaderName) - if !identity.IsIdentityType(reqCtx.SignedInUser.GetID(), identity.TypeAnonymous) { + if !identity.IsIdentityType(reqCtx.SignedInUser.GetID(), claims.TypeAnonymous) { h.SetHTTPHeader(proxyutil.UserHeaderName, reqCtx.SignedInUser.GetLogin()) } } diff --git a/pkg/services/serviceaccounts/api/api.go b/pkg/services/serviceaccounts/api/api.go index 7104dbd06ff..a14f05993bd 100644 --- a/pkg/services/serviceaccounts/api/api.go +++ b/pkg/services/serviceaccounts/api/api.go @@ -4,6 +4,7 @@ import ( "net/http" "strconv" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/routing" @@ -99,7 +100,7 @@ func (api *ServiceAccountsAPI) CreateServiceAccount(c *contextmodel.ReqContext) } if api.cfg.RBAC.PermissionsOnCreation("service-account") { - if identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) { + if identity.IsIdentityType(c.SignedInUser.GetID(), claims.TypeUser) { userID, err := c.SignedInUser.GetID().ParseInt() if err != nil { return response.Error(http.StatusInternalServerError, "Failed to parse user id", err) diff --git a/pkg/services/team/teamapi/team.go b/pkg/services/team/teamapi/team.go index 1b83bb49ebb..0fc23039fc0 100644 --- a/pkg/services/team/teamapi/team.go +++ b/pkg/services/team/teamapi/team.go @@ -5,9 +5,9 @@ import ( "net/http" "strconv" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/response" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/accesscontrol" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" "github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess" @@ -51,7 +51,7 @@ func (tapi *TeamAPI) createTeam(c *contextmodel.ReqContext) response.Response { // an additional check whether it is an actual user is required namespace, identifier := c.SignedInUser.GetID().Type(), c.SignedInUser.GetID().ID() switch namespace { - case identity.TypeUser: + case claims.TypeUser: userID, err := strconv.ParseInt(identifier, 10, 64) if err != nil { c.Logger.Error("Could not add creator to team because user id is not a number", "error", err) diff --git a/pkg/services/user/identity.go b/pkg/services/user/identity.go index 37671cd2fe3..e9b0a4c2011 100644 --- a/pkg/services/user/identity.go +++ b/pkg/services/user/identity.go @@ -50,7 +50,7 @@ type SignedInUser struct { IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] `json:"-" xorm:"-"` // When other settings are not deterministic, this value is used - FallbackType identity.IdentityType + FallbackType claims.IdentityType } // Access implements claims.AuthInfo. @@ -89,18 +89,18 @@ func (u *SignedInUser) GetInternalID() (int64, error) { } // GetIdentityType implements Requester. -func (u *SignedInUser) GetIdentityType() identity.IdentityType { +func (u *SignedInUser) GetIdentityType() claims.IdentityType { switch { case u.ApiKeyID != 0: - return identity.TypeAPIKey + return claims.TypeAPIKey case u.IsServiceAccount: - return identity.TypeServiceAccount + return claims.TypeServiceAccount case u.UserID > 0: - return identity.TypeUser + return claims.TypeUser case u.IsAnonymous: - return identity.TypeAnonymous + return claims.TypeAnonymous case u.AuthenticatedBy == "render" && u.UserID == 0: - return identity.TypeRenderService + return claims.TypeRenderService } return u.FallbackType } @@ -263,18 +263,18 @@ func (u *SignedInUser) GetID() identity.TypedID { return identity.NewTypedIDString(ns, id) } -func (u *SignedInUser) getTypeAndID() (identity.IdentityType, string) { +func (u *SignedInUser) getTypeAndID() (claims.IdentityType, string) { switch { case u.ApiKeyID != 0: - return identity.TypeAPIKey, strconv.FormatInt(u.ApiKeyID, 10) + return claims.TypeAPIKey, strconv.FormatInt(u.ApiKeyID, 10) case u.IsServiceAccount: - return identity.TypeServiceAccount, strconv.FormatInt(u.UserID, 10) + return claims.TypeServiceAccount, strconv.FormatInt(u.UserID, 10) case u.UserID > 0: - return identity.TypeUser, strconv.FormatInt(u.UserID, 10) + return claims.TypeUser, strconv.FormatInt(u.UserID, 10) case u.IsAnonymous: - return identity.TypeAnonymous, "0" + return claims.TypeAnonymous, "0" case u.AuthenticatedBy == "render" && u.UserID == 0: - return identity.TypeRenderService, "0" + return claims.TypeRenderService, "0" } return u.FallbackType, strconv.FormatInt(u.UserID, 10) diff --git a/pkg/services/user/userimpl/verifier.go b/pkg/services/user/userimpl/verifier.go index 4bbb9b6ea3c..1569ba46285 100644 --- a/pkg/services/user/userimpl/verifier.go +++ b/pkg/services/user/userimpl/verifier.go @@ -7,6 +7,7 @@ import ( "net/mail" "time" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/services/auth" @@ -153,6 +154,6 @@ func (s *Verifier) Complete(ctx context.Context, cmd user.CompleteEmailVerifyCom // remove the current token, so a new one can be generated with correct values. return s.is.RemoveIDToken( ctx, - &authn.Identity{ID: identity.NewTypedID(identity.TypeUser, usr.ID), OrgID: usr.OrgID}, + &authn.Identity{ID: identity.NewTypedID(claims.TypeUser, usr.ID), OrgID: usr.OrgID}, ) } diff --git a/pkg/storage/unified/apistore/store_test.go b/pkg/storage/unified/apistore/store_test.go index 3ac93500675..8977693c966 100644 --- a/pkg/storage/unified/apistore/store_test.go +++ b/pkg/storage/unified/apistore/store_test.go @@ -20,6 +20,7 @@ import ( examplev1 "k8s.io/apiserver/pkg/apis/example/v1" "k8s.io/apiserver/pkg/storage" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" storagetesting "github.com/grafana/grafana/pkg/apiserver/storage/testing" ) @@ -32,7 +33,7 @@ func init() { // Make sure there is a user in every context storagetesting.NewContext = func() context.Context { testUserA := &identity.StaticRequester{ - Type: identity.TypeUser, + Type: claims.TypeUser, Login: "testuser", UserID: 123, UserUID: "u123", diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 2ce0faad857..885dc1de9ae 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -5,6 +5,7 @@ go 1.22.4 require ( github.com/fullstorydev/grpchan v1.1.1 github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 + github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/prometheus/client_golang v1.19.1 diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 602360e405f..fe078d7593d 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -349,6 +349,8 @@ github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBH github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 h1:EiaupmOnt6XF/LPxvagjTofWmByzYaf5VyMIF+w/71M= github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1/go.mod h1:YA9We4kTafu7mlMnUh3In6Q2wpg8fYN3ycgCKOK1TB8= +github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 h1:uD1LcKwvEAqzDsgVChBudPqo5BhPxkj9AgylT5QCReo= +github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e h1:3vNpomyzv714Hgls5vn+fC0vgv8wUOSHepUl7PB5nUs= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808164224-787abccfbc9e/go.mod h1:ORVFiW/KNRY52lNjkGwnFWCxNVfE97bJG2jr2fetq0I= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= diff --git a/pkg/storage/unified/resource/grpc/authenticator.go b/pkg/storage/unified/resource/grpc/authenticator.go index ad0efe0a46f..23322c998af 100644 --- a/pkg/storage/unified/resource/grpc/authenticator.go +++ b/pkg/storage/unified/resource/grpc/authenticator.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/grafana/authlib/authn" + "github.com/grafana/authlib/claims" "google.golang.org/grpc" "google.golang.org/grpc/metadata" @@ -76,7 +77,7 @@ func (f *Authenticator) decodeMetadata(ctx context.Context, meta metadata.MD) (i // TODO, remove after this has been deployed to unified storage if getter(mdUserID) == "" { var err error - user.Type = identity.TypeUser + user.Type = claims.TypeUser user.UserID, err = strconv.ParseInt(getter("grafana-userid"), 10, 64) if err != nil { return nil, fmt.Errorf("invalid user id: %w", err) diff --git a/pkg/storage/unified/resource/grpc/authenticator_test.go b/pkg/storage/unified/resource/grpc/authenticator_test.go index 40279763b1e..d44268a0232 100644 --- a/pkg/storage/unified/resource/grpc/authenticator_test.go +++ b/pkg/storage/unified/resource/grpc/authenticator_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" ) @@ -14,7 +15,7 @@ func TestBasicEncodeDecode(t *testing.T) { UserID: 123, UserUID: "abc", Login: "test", - Type: identity.TypeUser, + Type: claims.TypeUser, OrgID: 456, OrgName: "org", OrgRole: identity.RoleAdmin, diff --git a/pkg/storage/unified/resource/server.go b/pkg/storage/unified/resource/server.go index ac2c02cb947..e3dc65dc696 100644 --- a/pkg/storage/unified/resource/server.go +++ b/pkg/storage/unified/resource/server.go @@ -15,6 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" ) @@ -122,7 +123,7 @@ func NewResourceServer(opts ResourceServerOptions) (ResourceServer, error) { // Make this cancelable ctx, cancel := context.WithCancel(identity.WithRequester(context.Background(), &identity.StaticRequester{ - Type: identity.TypeServiceAccount, + Type: claims.TypeServiceAccount, Login: "watcher", // admin user for watch UserID: 1, IsGrafanaAdmin: true, diff --git a/pkg/storage/unified/resource/server_test.go b/pkg/storage/unified/resource/server_test.go index 63023aa3d34..30a81554c68 100644 --- a/pkg/storage/unified/resource/server_test.go +++ b/pkg/storage/unified/resource/server_test.go @@ -13,13 +13,14 @@ import ( "gocloud.dev/blob/memblob" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" ) func TestSimpleServer(t *testing.T) { testUserA := &identity.StaticRequester{ - Type: identity.TypeUser, + Type: claims.TypeUser, Login: "testuser", UserID: 123, UserUID: "u123", diff --git a/pkg/storage/unified/sql/test/integration_test.go b/pkg/storage/unified/sql/test/integration_test.go index 1636de069ad..f519480d061 100644 --- a/pkg/storage/unified/sql/test/integration_test.go +++ b/pkg/storage/unified/sql/test/integration_test.go @@ -10,6 +10,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "github.com/grafana/authlib/claims" "github.com/grafana/dskit/services" "github.com/grafana/grafana/pkg/apimachinery/identity" infraDB "github.com/grafana/grafana/pkg/infra/db" @@ -66,7 +67,7 @@ func TestIntegrationBackendHappyPath(t *testing.T) { } testUserA := &identity.StaticRequester{ - Type: identity.TypeUser, + Type: claims.TypeUser, Login: "testuser", UserID: 123, UserUID: "u123", @@ -341,7 +342,7 @@ func TestClientServer(t *testing.T) { // Test with an admin identity clientCtx := identity.WithRequester(ctx, &identity.StaticRequester{ - Type: identity.TypeUser, + Type: claims.TypeUser, Login: "testuser", UserID: 123, UserUID: "u123", diff --git a/pkg/util/proxyutil/proxyutil.go b/pkg/util/proxyutil/proxyutil.go index f53bff01146..4323d7c3f1b 100644 --- a/pkg/util/proxyutil/proxyutil.go +++ b/pkg/util/proxyutil/proxyutil.go @@ -7,6 +7,7 @@ import ( "sort" "strings" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/apimachinery/identity" ) @@ -114,7 +115,7 @@ func ApplyUserHeader(sendUserHeader bool, req *http.Request, user identity.Reque return } - if identity.IsIdentityType(user.GetID(), identity.TypeUser) { + if identity.IsIdentityType(user.GetID(), claims.TypeUser) { req.Header.Set(UserHeaderName, user.GetLogin()) } } diff --git a/pkg/util/proxyutil/proxyutil_test.go b/pkg/util/proxyutil/proxyutil_test.go index 40836a75d40..4c89cc9bb19 100644 --- a/pkg/util/proxyutil/proxyutil_test.go +++ b/pkg/util/proxyutil/proxyutil_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/apimachinery/identity" + "github.com/grafana/authlib/claims" "github.com/grafana/grafana/pkg/services/user" ) @@ -175,7 +175,7 @@ func TestApplyUserHeader(t *testing.T) { require.NoError(t, err) req.Header.Set("X-Grafana-User", "admin") - ApplyUserHeader(false, req, &user.SignedInUser{Login: "admin", UserID: 1, FallbackType: identity.TypeUser}) + ApplyUserHeader(false, req, &user.SignedInUser{Login: "admin", UserID: 1, FallbackType: claims.TypeUser}) require.NotContains(t, req.Header, "X-Grafana-User") }) @@ -192,7 +192,7 @@ func TestApplyUserHeader(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "/", nil) require.NoError(t, err) - ApplyUserHeader(true, req, &user.SignedInUser{IsAnonymous: true, FallbackType: identity.TypeAnonymous}) + ApplyUserHeader(true, req, &user.SignedInUser{IsAnonymous: true, FallbackType: claims.TypeAnonymous}) require.NotContains(t, req.Header, "X-Grafana-User") }) @@ -200,7 +200,7 @@ func TestApplyUserHeader(t *testing.T) { req, err := http.NewRequest(http.MethodGet, "/", nil) require.NoError(t, err) - ApplyUserHeader(true, req, &user.SignedInUser{Login: "admin", UserID: 1, FallbackType: identity.TypeUser}) + ApplyUserHeader(true, req, &user.SignedInUser{Login: "admin", UserID: 1, FallbackType: claims.TypeUser}) require.Equal(t, "admin", req.Header.Get("X-Grafana-User")) }) }