diff --git a/go.mod b/go.mod index be3e78865eb..4b7520f3c1a 100644 --- a/go.mod +++ b/go.mod @@ -74,8 +74,8 @@ require ( github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad github.com/grafana/alerting v0.0.0-20241021123319-be61d61f71e7 // @grafana/alerting-backend - github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a // @grafana/identity-access-team - github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20241202090931-5f1dcf611f9c // @grafana/identity-access-team + github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 // @grafana/identity-access-team github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // @grafana/dataviz-squad github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics diff --git a/go.sum b/go.sum index ef4fc160ac3..136865877ae 100644 --- a/go.sum +++ b/go.sum @@ -2290,10 +2290,10 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grafana/alerting v0.0.0-20241021123319-be61d61f71e7 h1:lsM/QscEX+ZDIJm48ynQscH+msETyGYV6ug8L4f2DtM= github.com/grafana/alerting v0.0.0-20241021123319-be61d61f71e7/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU= -github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a h1:X3mroOOXdryRY6SEIQp7S6l1oxt964ywSmcknszHwn0= -github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a/go.mod h1:XFhcSCEDeOgV43x41we7mRBcizDpsTScb7XOuYipQZg= -github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d h1:7nZfaXdC4Xc2ocMz5/Bx/3EsaEO34KsmA2RRcuogLnc= -github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/authlib v0.0.0-20241202090931-5f1dcf611f9c h1:10aFnI5iFk0XSw19FD26oS4nIwCunfbkYT800hpHBzo= +github.com/grafana/authlib v0.0.0-20241202090931-5f1dcf611f9c/go.mod h1:x7df73G3xuSD35Xv9cjaMLyPJCgM9Z/Wj5ISouoAfiI= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 h1:3DHH81RJCi8Bcgn2MdBh7vgWUshmAFjZzBCVuxiQ0uk= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk= github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= diff --git a/go.work.sum b/go.work.sum index f2a9af25e11..84f1a5ce497 100644 --- a/go.work.sum +++ b/go.work.sum @@ -987,11 +987,10 @@ github.com/grafana/alerting v0.0.0-20240830172655-aa466962ea18 h1:3cQ+d+fkNL2Eqp github.com/grafana/alerting v0.0.0-20240830172655-aa466962ea18/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10 h1:oDbLKM34O+JUF9EQFS+9aYhdYoeNfUpXqNjFCLIxwF4= github.com/grafana/alerting v0.0.0-20240917171353-6c25eb6eff10/go.mod h1:GMLi6d09Xqo96fCVUjNk//rcjP5NKEdjOzfWIffD5r4= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 h1:3DHH81RJCi8Bcgn2MdBh7vgWUshmAFjZzBCVuxiQ0uk= github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2 h1:qhugDMdQ4Vp68H0tp/0iN17DM2ehRo1rLEdOFe/gB8I= github.com/grafana/cloudflare-go v0.0.0-20230110200409-c627cf6792f2/go.mod h1:w/aiO1POVIeXUQyl0VQSZjl5OAGDTL5aX+4v0RA1tcw= github.com/grafana/cog v0.0.4/go.mod h1:lzetOuhGUl/JaSACiJoHvBokf9/fS6PEFaWZvnQu2vs= -github.com/grafana/cog v0.0.5 h1:BCa+10i3KvV+KMSQuxlN1DS9cZEwN+EAFc7ZmXqHxQE= -github.com/grafana/cog v0.0.5/go.mod h1:lzetOuhGUl/JaSACiJoHvBokf9/fS6PEFaWZvnQu2vs= github.com/grafana/cuetsy v0.1.10/go.mod h1:Ix97+CPD8ws9oSSxR3/Lf4ahU1I4Np83kjJmDVnLZvc= github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= diff --git a/pkg/api/user.go b/pkg/api/user.go index 292aa7fc3cd..8c8fca56d11 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -281,7 +281,7 @@ func (hs *HTTPServer) StartEmailVerificaton(c *contextmodel.ReqContext) response return response.Error(http.StatusBadRequest, "Only users can verify their email", nil) } - if c.SignedInUser.IsEmailVerified() { + if c.SignedInUser.GetEmailVerified() { // email is already verified so we don't need to trigger the flow. return response.Respond(http.StatusNotModified, nil) } diff --git a/pkg/apimachinery/go.mod b/pkg/apimachinery/go.mod index d306b0d9f13..0e15ed81eea 100644 --- a/pkg/apimachinery/go.mod +++ b/pkg/apimachinery/go.mod @@ -3,8 +3,8 @@ module github.com/grafana/grafana/pkg/apimachinery go 1.23.1 require ( - github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a // @grafana/identity-access-team - github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d // @grafana/identity-access-team + github.com/grafana/authlib v0.0.0-20241202090931-5f1dcf611f9c // @grafana/identity-access-team + github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 // @grafana/identity-access-team github.com/stretchr/testify v1.9.0 k8s.io/apimachinery v0.31.3 k8s.io/apiserver v0.31.3 diff --git a/pkg/apimachinery/go.sum b/pkg/apimachinery/go.sum index d47f3912c5c..5e04abe6a50 100644 --- a/pkg/apimachinery/go.sum +++ b/pkg/apimachinery/go.sum @@ -28,10 +28,10 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a h1:X3mroOOXdryRY6SEIQp7S6l1oxt964ywSmcknszHwn0= -github.com/grafana/authlib v0.0.0-20241024120339-84cd3a898e8a/go.mod h1:XFhcSCEDeOgV43x41we7mRBcizDpsTScb7XOuYipQZg= -github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d h1:7nZfaXdC4Xc2ocMz5/Bx/3EsaEO34KsmA2RRcuogLnc= -github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/authlib v0.0.0-20241202090931-5f1dcf611f9c h1:10aFnI5iFk0XSw19FD26oS4nIwCunfbkYT800hpHBzo= +github.com/grafana/authlib v0.0.0-20241202090931-5f1dcf611f9c/go.mod h1:x7df73G3xuSD35Xv9cjaMLyPJCgM9Z/Wj5ISouoAfiI= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 h1:3DHH81RJCi8Bcgn2MdBh7vgWUshmAFjZzBCVuxiQ0uk= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= diff --git a/pkg/apimachinery/identity/requester.go b/pkg/apimachinery/identity/requester.go index c8fbf6fd3e7..93f82c3394c 100644 --- a/pkg/apimachinery/identity/requester.go +++ b/pkg/apimachinery/identity/requester.go @@ -14,48 +14,59 @@ type Requester interface { // GetIdentityType returns the type for the requester GetIdentityType() claims.IdentityType + // IsIdentityType returns true if identity type for requester matches any expected identity type IsIdentityType(expected ...claims.IdentityType) bool + + // FIXME: remove and use GetIdentifier instead. // GetRawIdentifier returns only the identifier part of the UID, excluding the type GetRawIdentifier() string - // GetInternalID returns only the identifier part of the ID, excluding the type - GetInternalID() (int64, error) + // GetID returns namespaced internalID for the entity // Deprecated: use GetUID instead GetID() string + // GetEmail returns the email of the active entity. // Can be empty. GetEmail() string - // IsEmailVerified returns if email is verified for entity. - IsEmailVerified() bool + + // GetInternalID returns only the identifier part of the ID, excluding the type + GetInternalID() (int64, error) + // GetIsGrafanaAdmin returns true if the user is a server admin GetIsGrafanaAdmin() bool + + // FIXME: remove and use GetUsername instead // GetLogin returns the login of the active entity // Can be empty. GetLogin() string + // GetOrgID returns the ID of the active organization GetOrgID() int64 + // GetOrgRole returns the role of the active entity in the active organization. GetOrgRole() RoleType + // GetPermissions returns the permissions of the active entity. GetPermissions() map[string][]string + // GetGlobalPermissions returns the permissions of the active entity that are available across all organizations. GetGlobalPermissions() map[string][]string + // DEPRECATED: GetTeams returns the teams the entity is a member of. // Retrieve the teams from the team service instead of using this method. GetTeams() []int64 + // DEPRECATED: GetOrgName returns the name of the active organization. // Retrieve the organization name from the organization service instead of using this method. GetOrgName() string + // GetAuthID returns external id for entity. GetAuthID() string - // GetNamespace returns either "*" or the single namespace this requester has access to - // An empty value means the implementation has not specified a kubernetes namespace. - GetNamespace() string - // GetAuthenticatedBy returns the authentication method used to authenticate the entity. - GetAuthenticatedBy() string + // IsAuthenticatedBy returns true if entity was authenticated by any of supplied providers. IsAuthenticatedBy(providers ...string) bool + // IsNil returns true if the identity is nil // FIXME: remove this method once all services are using an interface IsNil() bool diff --git a/pkg/apimachinery/identity/static.go b/pkg/apimachinery/identity/static.go index e520e63348c..ab689716061 100644 --- a/pkg/apimachinery/identity/static.go +++ b/pkg/apimachinery/identity/static.go @@ -2,12 +2,13 @@ package identity import ( "fmt" + "strconv" authnlib "github.com/grafana/authlib/authn" "github.com/grafana/authlib/claims" ) -var _ Requester = &StaticRequester{} +var _ Requester = (*StaticRequester)(nil) // StaticRequester allows creating requester values explicitly. // It is helpful in tests! @@ -35,45 +36,63 @@ type StaticRequester struct { CacheKey string } -// Access implements Requester. -func (u *StaticRequester) GetAccess() claims.AccessClaims { - return &IDClaimsWrapper{Source: u} +// GetID returns typed id for the entity +func (u *StaticRequester) GetID() string { + return claims.NewTypeID(u.Type, strconv.FormatInt(u.UserID, 10)) } -// Identity implements Requester. -func (u *StaticRequester) GetIdentity() claims.IdentityClaims { - if u.IDTokenClaims != nil { - return authnlib.NewIdentityClaims(*u.IDTokenClaims) - } - return &IDClaimsWrapper{Source: u} -} - -// GetRawIdentifier implements Requester. func (u *StaticRequester) GetUID() string { - return fmt.Sprintf("%s:%s", u.Type, u.UserUID) + return claims.NewTypeID(u.Type, u.UserUID) } -// GetRawIdentifier implements Requester. -func (u *StaticRequester) GetRawIdentifier() string { +func (u *StaticRequester) GetIdentifier() string { return u.UserUID } -// GetInternalID implements Requester. -func (u *StaticRequester) GetInternalID() (int64, error) { - return u.UserID, nil -} - -// GetIdentityType implements Requester. func (u *StaticRequester) GetIdentityType() claims.IdentityType { return u.Type } -// IsIdentityType implements Requester. +func (u *StaticRequester) GetSubject() string { + return claims.NewTypeID(u.Type, strconv.FormatInt(u.UserID, 10)) +} + +func (u *StaticRequester) GetAudience() []string { + return []string{fmt.Sprintf("org:%d", u.OrgID)} +} + +func (u *StaticRequester) GetTokenPermissions() []string { + return []string{} +} + +func (u *StaticRequester) GetTokenDelegatedPermissions() []string { + return []string{} +} + +func (u *StaticRequester) GetEmail() string { + return u.Email +} + +func (u *StaticRequester) GetEmailVerified() bool { + return u.EmailVerified +} + +func (u *StaticRequester) GetUsername() string { + return u.Login +} + +func (u *StaticRequester) GetRawIdentifier() string { + return u.UserUID +} + +func (u *StaticRequester) GetInternalID() (int64, error) { + return u.UserID, nil +} + func (u *StaticRequester) IsIdentityType(expected ...claims.IdentityType) bool { return claims.IsIdentityType(u.GetIdentityType(), expected...) } -// GetExtra implements Requester. func (u *StaticRequester) GetExtra() map[string][]string { if u.IDToken != "" { return map[string][]string{"id-token": {u.IDToken}} @@ -81,12 +100,10 @@ func (u *StaticRequester) GetExtra() map[string][]string { return map[string][]string{} } -// GetGroups implements Requester. func (u *StaticRequester) GetGroups() []string { return []string{} } -// GetName implements Requester. func (u *StaticRequester) GetName() string { if u.Name != "" { return u.Name @@ -171,11 +188,6 @@ func (u *StaticRequester) HasUniqueId() bool { return u.UserID > 0 } -// GetID returns typed id for the entity -func (u *StaticRequester) GetID() string { - return claims.NewTypeID(u.Type, fmt.Sprintf("%d", u.UserID)) -} - func (u *StaticRequester) GetAuthID() string { return u.AuthID } @@ -202,12 +214,6 @@ func (u *StaticRequester) IsNil() bool { return u == nil } -// GetEmail returns the email of the active entity -// Can be empty. -func (u *StaticRequester) GetEmail() string { - return u.Email -} - func (u *StaticRequester) IsEmailVerified() bool { return u.EmailVerified } diff --git a/pkg/apimachinery/identity/wrapper.go b/pkg/apimachinery/identity/wrapper.go deleted file mode 100644 index 09eab3eea41..00000000000 --- a/pkg/apimachinery/identity/wrapper.go +++ /dev/null @@ -1,109 +0,0 @@ -package identity - -import ( - "fmt" - "time" - - "github.com/grafana/authlib/claims" -) - -var _ claims.IdentityClaims = &IDClaimsWrapper{} -var _ claims.AccessClaims = &IDClaimsWrapper{} - -type IDClaimsWrapper struct { - Source Requester -} - -func (i *IDClaimsWrapper) IsNil() bool { - return i.Source.IsNil() -} - -// GetAuthenticatedBy implements claims.IdentityClaims. -func (i *IDClaimsWrapper) AuthenticatedBy() string { - return i.Source.GetAuthenticatedBy() -} - -// GetDisplayName implements claims.IdentityClaims. -func (i *IDClaimsWrapper) DisplayName() string { - return i.Source.GetName() -} - -// GetEmail implements claims.IdentityClaims. -func (i *IDClaimsWrapper) Email() string { - return i.Source.GetEmail() -} - -// GetEmailVerified implements claims.IdentityClaims. -func (i *IDClaimsWrapper) EmailVerified() bool { - return i.Source.IsEmailVerified() -} - -// GetIdentityType implements claims.IdentityClaims. -func (i *IDClaimsWrapper) IdentityType() claims.IdentityType { - return i.Source.GetIdentityType() -} - -// GetRawUID implements claims.IdentityClaims. -func (i *IDClaimsWrapper) Identifier() string { - return i.Source.GetRawIdentifier() -} - -// GetUsername implements claims.IdentityClaims. -func (i *IDClaimsWrapper) Username() string { - return i.Source.GetLogin() -} - -// GetAudience implements claims.AccessClaims. -func (i *IDClaimsWrapper) Audience() []string { - return []string{fmt.Sprintf("org:%d", i.Source.GetOrgID())} -} - -// GetDelegatedPermissions implements claims.AccessClaims. -func (i *IDClaimsWrapper) DelegatedPermissions() []string { - return []string{} -} - -// GetExpiry implements claims.AccessClaims. -func (i *IDClaimsWrapper) Expiry() *time.Time { - return nil -} - -// GetIssuedAt implements claims.AccessClaims. -func (i *IDClaimsWrapper) IssuedAt() *time.Time { - return nil -} - -// GetIssuer implements claims.AccessClaims. -func (i *IDClaimsWrapper) Issuer() string { - return "" -} - -// GetJTI implements claims.AccessClaims. -func (i *IDClaimsWrapper) JTI() string { - return "" -} - -// GetNamespace implements claims.AccessClaims. -func (i *IDClaimsWrapper) Namespace() string { - return i.Source.GetNamespace() -} - -// GetNotBefore implements claims.AccessClaims. -func (i *IDClaimsWrapper) NotBefore() *time.Time { - return nil -} - -// GetPermissions implements claims.AccessClaims. -func (i *IDClaimsWrapper) Permissions() []string { - return []string{} -} - -// GetScopes implements claims.AccessClaims. -func (i *IDClaimsWrapper) Scopes() []string { - return []string{} -} - -// GetSubject implements claims.AccessClaims. -func (i *IDClaimsWrapper) Subject() string { - return i.Source.GetID() -} diff --git a/pkg/apiserver/go.mod b/pkg/apiserver/go.mod index 1440a9eba93..57671579dde 100644 --- a/pkg/apiserver/go.mod +++ b/pkg/apiserver/go.mod @@ -4,7 +4,7 @@ go 1.23.1 require ( github.com/google/go-cmp v0.6.0 - github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d + github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1 github.com/prometheus/client_golang v1.20.5 github.com/stretchr/testify v1.9.0 diff --git a/pkg/apiserver/go.sum b/pkg/apiserver/go.sum index c3b7855d3a0..0c030cf3627 100644 --- a/pkg/apiserver/go.sum +++ b/pkg/apiserver/go.sum @@ -78,8 +78,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d h1:7nZfaXdC4Xc2ocMz5/Bx/3EsaEO34KsmA2RRcuogLnc= -github.com/grafana/authlib/claims v0.0.0-20241024115517-d30b00d7666d/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335 h1:3DHH81RJCi8Bcgn2MdBh7vgWUshmAFjZzBCVuxiQ0uk= +github.com/grafana/authlib/claims v0.0.0-20241202085737-df90af04f335/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 4a47bcf3f5d..f44fd66cf64 100644 --- a/pkg/registry/apis/dashboard/legacy/sql_dashboards.go +++ b/pkg/registry/apis/dashboard/legacy/sql_dashboards.go @@ -382,10 +382,9 @@ func (a *dashboardSqlAccess) SaveDashboard(ctx context.Context, orgId int64, das } var userID int64 - idClaims := user.GetIdentity() - if claims.IsIdentityType(idClaims.IdentityType(), claims.TypeUser) { + if claims.IsIdentityType(user.GetIdentityType(), claims.TypeUser) { var err error - userID, err = identity.UserIdentifier(idClaims.Subject()) + userID, err = identity.UserIdentifier(user.GetSubject()) if err != nil { return nil, false, err } diff --git a/pkg/services/auth/idimpl/service.go b/pkg/services/auth/idimpl/service.go index 8c11374d910..f584b226952 100644 --- a/pkg/services/auth/idimpl/service.go +++ b/pkg/services/auth/idimpl/service.go @@ -86,7 +86,7 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri now := time.Now() idClaims := &auth.IDClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: s.cfg.AppURL, Audience: getAudience(id.GetOrgID()), Subject: id.GetID(), @@ -102,7 +102,7 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri if id.IsIdentityType(claims.TypeUser) { idClaims.Rest.Email = id.GetEmail() - idClaims.Rest.EmailVerified = id.IsEmailVerified() + idClaims.Rest.EmailVerified = id.GetEmailVerified() idClaims.Rest.AuthenticatedBy = id.GetAuthenticatedBy() idClaims.Rest.Username = id.GetLogin() idClaims.Rest.DisplayName = id.GetName() diff --git a/pkg/services/auth/idtest/internal_token.go b/pkg/services/auth/idtest/internal_token.go deleted file mode 100644 index 2f1b96bcb38..00000000000 --- a/pkg/services/auth/idtest/internal_token.go +++ /dev/null @@ -1,54 +0,0 @@ -package idtest - -import ( - "time" - - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" - authnlib "github.com/grafana/authlib/authn" - "github.com/grafana/authlib/claims" - - "github.com/grafana/grafana/pkg/services/auth" -) - -func CreateInternalToken(authInfo claims.AuthInfo, secret []byte) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) { - signerOpts := jose.SignerOptions{} - signerOpts.WithType("jwt") // Should be uppercase, but this is what authlib expects - signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: secret}, &signerOpts) - if err != nil { - return "", nil, err - } - - identity := authInfo.GetIdentity() - now := time.Now() - tokenTTL := 10 * time.Minute - idClaims := &auth.IDClaims{ - Claims: &jwt.Claims{ - Audience: identity.Audience(), - Subject: identity.Subject(), - Expiry: jwt.NewNumericDate(now.Add(tokenTTL)), - IssuedAt: jwt.NewNumericDate(now), - }, - Rest: authnlib.IDTokenClaims{ - Namespace: identity.Namespace(), - Identifier: identity.Identifier(), - Type: identity.IdentityType(), - }, - } - - if claims.IsIdentityType(identity.IdentityType(), claims.TypeUser) { - idClaims.Rest.Email = identity.Email() - idClaims.Rest.EmailVerified = identity.EmailVerified() - idClaims.Rest.AuthenticatedBy = identity.AuthenticatedBy() - idClaims.Rest.Username = identity.Username() - idClaims.Rest.DisplayName = identity.DisplayName() - } - - builder := jwt.Signed(signer).Claims(&idClaims.Rest).Claims(idClaims.Claims) - token, err := builder.CompactSerialize() - if err != nil { - return "", nil, err - } - - return token, idClaims, nil -} diff --git a/pkg/services/authn/clients/ext_jwt.go b/pkg/services/authn/clients/ext_jwt.go index cd918d32f85..571f9ff5c35 100644 --- a/pkg/services/authn/clients/ext_jwt.go +++ b/pkg/services/authn/clients/ext_jwt.go @@ -100,12 +100,12 @@ func (s *ExtendedJWT) authenticateAsUser( accessTokenClaims authlib.Claims[authlib.AccessTokenClaims], ) (*authn.Identity, error) { // Only allow id tokens signed for namespace configured for this instance. - if allowedNamespace := s.namespaceMapper(s.cfg.DefaultOrgID()); !claims.NamespaceMatches(authlib.NewIdentityClaims(idTokenClaims), allowedNamespace) { + if allowedNamespace := s.namespaceMapper(s.cfg.DefaultOrgID()); !claims.NamespaceMatches(idTokenClaims.Rest.Namespace, allowedNamespace) { return nil, errExtJWTDisallowedNamespaceClaim.Errorf("unexpected id token namespace: %s", idTokenClaims.Rest.Namespace) } // Allow access tokens with either the same namespace as the validated id token namespace or wildcard (`*`). - if !claims.NamespaceMatches(authlib.NewAccessClaims(accessTokenClaims), idTokenClaims.Rest.Namespace) { + if !claims.NamespaceMatches(accessTokenClaims.Rest.Namespace, idTokenClaims.Rest.Namespace) { return nil, errExtJWTMisMatchedNamespaceClaims.Errorf("unexpected access token namespace: %s", accessTokenClaims.Rest.Namespace) } @@ -155,7 +155,7 @@ func (s *ExtendedJWT) authenticateAsUser( func (s *ExtendedJWT) authenticateAsService(accessTokenClaims authlib.Claims[authlib.AccessTokenClaims]) (*authn.Identity, error) { // Allow access tokens with that has a wildcard namespace or a namespace matching this instance. - if allowedNamespace := s.namespaceMapper(s.cfg.DefaultOrgID()); !claims.NamespaceMatches(authlib.NewAccessClaims(accessTokenClaims), allowedNamespace) { + if allowedNamespace := s.namespaceMapper(s.cfg.DefaultOrgID()); !claims.NamespaceMatches(accessTokenClaims.Rest.Namespace, allowedNamespace) { return nil, errExtJWTDisallowedNamespaceClaim.Errorf("unexpected access token namespace: %s", accessTokenClaims.Rest.Namespace) } diff --git a/pkg/services/authn/clients/ext_jwt_test.go b/pkg/services/authn/clients/ext_jwt_test.go index 10bd830d7f6..f09cd95ed29 100644 --- a/pkg/services/authn/clients/ext_jwt_test.go +++ b/pkg/services/authn/clients/ext_jwt_test.go @@ -27,7 +27,7 @@ type ( var ( validAccessTokenClaims = accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "access-policy:this-uid", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -40,7 +40,7 @@ var ( }, } validIDTokenClaims = idTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "user:2", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -51,7 +51,7 @@ var ( }, } validIDTokenClaimsWithStackSet = idTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "user:2", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -62,7 +62,7 @@ var ( }, } validIDTokenClaimsWithDeprecatedStackClaimSet = idTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "user:2", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -73,7 +73,7 @@ var ( }, } validAccessTokenClaimsWildcard = accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "access-policy:this-uid", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -83,7 +83,7 @@ var ( }, } validAccessTokenClaimsWithStackSet = accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "access-policy:this-uid", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -93,7 +93,7 @@ var ( }, } validAccessTokenClaimsWithDeprecatedStackClaimSet = accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "access-policy:this-uid", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -103,7 +103,7 @@ var ( }, } invalidNamespaceIDTokenClaims = idTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "user:2", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -114,7 +114,7 @@ var ( }, } invalidSubjectIDTokenClaims = idTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "service-account:2", Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), @@ -459,7 +459,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) { { name: "should return error when the subject is not an access-policy", accessToken: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "user:2", Audience: jwt.Audience{"http://localhost:3000"}, @@ -523,7 +523,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "missing iss", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://localhost:3000"}, ID: "1234567890", @@ -538,7 +538,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "missing expiry", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://localhost:3000"}, @@ -553,7 +553,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "expired token", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://localhost:3000"}, @@ -569,7 +569,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "missing aud", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", ID: "1234567890", @@ -584,7 +584,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "wrong aud", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://some-other-host:3000"}, @@ -605,7 +605,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "missing sub", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Audience: jwt.Audience{"http://localhost:3000"}, ID: "1234567890", @@ -620,7 +620,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "missing iat", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://localhost:3000"}, @@ -635,7 +635,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "iat later than current time", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://localhost:3000"}, @@ -651,7 +651,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { { name: "unsupported alg", payload: &accessTokenClaims{ - Claims: &jwt.Claims{ + Claims: jwt.Claims{ Issuer: "http://localhost:3000", Subject: "access-policy:this-uid", Audience: jwt.Audience{"http://localhost:3000"}, diff --git a/pkg/services/authn/identity.go b/pkg/services/authn/identity.go index 382530c9ca8..ca6391dcef2 100644 --- a/pkg/services/authn/identity.go +++ b/pkg/services/authn/identity.go @@ -74,32 +74,30 @@ type Identity struct { // Permissions is the list of permissions the entity has. Permissions map[int64]map[string][]string // IDToken is a signed token representing the identity that can be forwarded to plugins and external services. - IDToken string - IDTokenClaims *authn.Claims[authn.IDTokenClaims] + IDToken string + IDTokenClaims *authn.Claims[authn.IDTokenClaims] AccessTokenClaims *authn.Claims[authn.AccessTokenClaims] } -func (i *Identity) GetAccess() claims.AccessClaims { - if i.AccessTokenClaims != nil { - return authn.NewAccessClaims(*i.AccessTokenClaims) - } - return &identity.IDClaimsWrapper{Source: i} +func (i *Identity) GetID() string { + return i.GetSubject() } -func (i *Identity) GetIdentity() claims.IdentityClaims { - if i.IDTokenClaims != nil { - return authn.NewIdentityClaims(*i.IDTokenClaims) - } - return &identity.IDClaimsWrapper{Source: i} +func (i *Identity) GetInternalID() (int64, error) { + return identity.IntIdentifier(i.GetID()) +} + +func (i *Identity) GetUID() string { + return claims.NewTypeID(i.Type, i.UID) } func (i *Identity) GetRawIdentifier() string { return i.UID } -func (i *Identity) GetInternalID() (int64, error) { - return identity.IntIdentifier(i.GetID()) +func (i *Identity) GetIdentifier() string { + return i.UID } func (i *Identity) GetIdentityType() claims.IdentityType { @@ -110,6 +108,43 @@ func (i *Identity) IsIdentityType(expected ...claims.IdentityType) bool { return claims.IsIdentityType(i.GetIdentityType(), expected...) } +func (i *Identity) GetNamespace() string { + return i.Namespace +} + +func (i *Identity) GetSubject() string { + return claims.NewTypeID(i.Type, i.ID) +} + +func (i *Identity) GetAudience() []string { + if i.AccessTokenClaims != nil { + return i.AccessTokenClaims.Audience + } + return []string{} +} + +func (i *Identity) GetEmailVerified() bool { + return i.EmailVerified +} + +func (i *Identity) GetTokenPermissions() []string { + if i.AccessTokenClaims != nil { + return i.AccessTokenClaims.Rest.Permissions + } + return []string{} +} + +func (i *Identity) GetTokenDelegatedPermissions() []string { + if i.AccessTokenClaims != nil { + return i.AccessTokenClaims.Rest.DelegatedPermissions + } + return []string{} +} + +func (i *Identity) GetGroups() []string { + return []string{} +} + func (i *Identity) GetExtra() map[string][]string { extra := map[string][]string{} if i.IDToken != "" { @@ -121,10 +156,6 @@ func (i *Identity) GetExtra() map[string][]string { return extra } -func (i *Identity) GetGroups() []string { - return []string{} // teams? -} - func (i *Identity) GetName() string { if i.Name != "" { return i.Name @@ -135,14 +166,6 @@ func (i *Identity) GetName() string { return i.Email } -func (i *Identity) GetID() string { - return claims.NewTypeID(i.Type, i.ID) -} - -func (i *Identity) GetUID() string { - return claims.NewTypeID(i.Type, i.UID) -} - func (i *Identity) GetAuthID() string { return i.AuthID } @@ -166,10 +189,6 @@ func (i *Identity) GetEmail() string { return i.Email } -func (i *Identity) IsEmailVerified() bool { - return i.EmailVerified -} - func (i *Identity) GetIDToken() string { return i.IDToken } @@ -178,12 +197,12 @@ func (i *Identity) GetIsGrafanaAdmin() bool { return i.IsGrafanaAdmin != nil && *i.IsGrafanaAdmin } -func (i *Identity) GetLogin() string { +func (i *Identity) GetUsername() string { return i.Login } -func (i *Identity) GetNamespace() string { - return i.Namespace +func (i *Identity) GetLogin() string { + return i.Login } func (i *Identity) GetOrgID() int64 { @@ -263,24 +282,27 @@ func (i *Identity) IsNil() bool { // SignedInUser returns a SignedInUser from the identity. func (i *Identity) SignedInUser() *user.SignedInUser { u := &user.SignedInUser{ - OrgID: i.OrgID, - OrgName: i.OrgName, - OrgRole: i.GetOrgRole(), - Login: i.Login, - Name: i.Name, - Email: i.Email, - AuthID: i.AuthID, - AuthenticatedBy: i.AuthenticatedBy, - IsGrafanaAdmin: i.GetIsGrafanaAdmin(), - IsAnonymous: i.IsIdentityType(claims.TypeAnonymous), - IsDisabled: i.IsDisabled, - HelpFlags1: i.HelpFlags1, - LastSeenAt: i.LastSeenAt, - Teams: i.Teams, - Permissions: i.Permissions, - IDToken: i.IDToken, - FallbackType: i.Type, - Namespace: i.Namespace, + OrgID: i.OrgID, + OrgName: i.OrgName, + OrgRole: i.GetOrgRole(), + Login: i.Login, + Name: i.Name, + Email: i.Email, + EmailVerified: i.EmailVerified, + AuthID: i.AuthID, + AuthenticatedBy: i.AuthenticatedBy, + Namespace: i.Namespace, + IsGrafanaAdmin: i.GetIsGrafanaAdmin(), + IsAnonymous: i.IsIdentityType(claims.TypeAnonymous), + IsDisabled: i.IsDisabled, + HelpFlags1: i.HelpFlags1, + LastSeenAt: i.LastSeenAt, + Teams: i.Teams, + Permissions: i.Permissions, + IDToken: i.IDToken, + IDTokenClaims: i.IDTokenClaims, + AccessTokenClaims: i.AccessTokenClaims, + FallbackType: i.Type, } if i.IsIdentityType(claims.TypeAPIKey) { diff --git a/pkg/services/user/identity.go b/pkg/services/user/identity.go index 2ea72e1cb9a..34b9f5c378b 100644 --- a/pkg/services/user/identity.go +++ b/pkg/services/user/identity.go @@ -15,7 +15,7 @@ const ( GlobalOrgID = int64(0) ) -var _ identity.Requester = &SignedInUser{} +var _ identity.Requester = (*SignedInUser)(nil) type SignedInUser struct { UserID int64 `xorm:"user_id"` @@ -45,28 +45,28 @@ type SignedInUser struct { Permissions map[int64]map[string][]string `json:"-"` // IDToken is a signed token representing the identity that can be forwarded to plugins and external services. - IDToken string `json:"-" xorm:"-"` - IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] `json:"-" xorm:"-"` + IDToken string `json:"-" xorm:"-"` + IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] `json:"-" xorm:"-"` + AccessTokenClaims *authnlib.Claims[authnlib.AccessTokenClaims] `json:"-" xorm:"-"` // When other settings are not deterministic, this value is used FallbackType claims.IdentityType } -// Access implements claims.AuthInfo. -func (u *SignedInUser) GetAccess() claims.AccessClaims { - return &identity.IDClaimsWrapper{Source: u} +func (u *SignedInUser) GetID() string { + ns, id := u.getTypeAndID() + return claims.NewTypeID(ns, id) } -// Identity implements claims.AuthInfo. -func (u *SignedInUser) GetIdentity() claims.IdentityClaims { - if u.IDTokenClaims != nil { - return authnlib.NewIdentityClaims(*u.IDTokenClaims) - } - return &identity.IDClaimsWrapper{Source: u} +func (u *SignedInUser) GetInternalID() (int64, error) { + return identity.IntIdentifier(u.GetID()) } -// GetRawIdentifier implements Requester. -func (u *SignedInUser) GetRawIdentifier() string { +func (u *SignedInUser) GetUID() string { + return claims.NewTypeID(u.GetIdentityType(), u.GetIdentifier()) +} + +func (u *SignedInUser) GetIdentifier() string { if u.UserUID == "" { // nolint:staticcheck id, _ := u.GetInternalID() @@ -75,12 +75,10 @@ func (u *SignedInUser) GetRawIdentifier() string { return u.UserUID } -// GetInternalID implements Requester. -func (u *SignedInUser) GetInternalID() (int64, error) { - return identity.IntIdentifier(u.GetID()) +func (u *SignedInUser) GetRawIdentifier() string { + return u.GetIdentifier() } -// GetIdentityType implements Requester. func (u *SignedInUser) GetIdentityType() claims.IdentityType { switch { case u.ApiKeyID != 0: @@ -97,7 +95,6 @@ func (u *SignedInUser) GetIdentityType() claims.IdentityType { return u.FallbackType } -// IsIdentityType implements Requester. func (u *SignedInUser) IsIdentityType(expected ...claims.IdentityType) bool { return claims.IsIdentityType(u.GetIdentityType(), expected...) } @@ -115,7 +112,21 @@ func (u *SignedInUser) GetName() string { return u.Email } -// GetExtra implements Requester. +func (u *SignedInUser) GetNamespace() string { + return u.Namespace +} + +func (u *SignedInUser) GetSubject() string { + return u.GetID() +} + +func (u *SignedInUser) GetAudience() []string { + if u.AccessTokenClaims != nil { + return u.AccessTokenClaims.Audience + } + return []string{} +} + func (u *SignedInUser) GetExtra() map[string][]string { extra := map[string][]string{} if u.IDToken != "" { @@ -127,7 +138,6 @@ func (u *SignedInUser) GetExtra() map[string][]string { return extra } -// GetGroups implements Requester. func (u *SignedInUser) GetGroups() []string { groups := []string{} for _, t := range u.Teams { @@ -136,6 +146,28 @@ func (u *SignedInUser) GetGroups() []string { return groups } +func (u *SignedInUser) GetTokenPermissions() []string { + if u.AccessTokenClaims != nil { + return u.AccessTokenClaims.Rest.Permissions + } + return []string{} +} + +func (u *SignedInUser) GetTokenDelegatedPermissions() []string { + if u.AccessTokenClaims != nil { + return u.AccessTokenClaims.Rest.DelegatedPermissions + } + return []string{} +} + +func (u *SignedInUser) GetEmail() string { + return u.Email +} + +func (u *SignedInUser) GetEmailVerified() bool { + return u.EmailVerified +} + func (u *SignedInUser) ShouldUpdateLastSeenAt() bool { return u.UserID > 0 && time.Since(u.LastSeenAt) > time.Minute*5 } @@ -171,10 +203,6 @@ func (u *SignedInUser) HasUniqueId() bool { return u.IsRealUser() || u.IsApiKeyUser() || u.IsServiceAccountUser() } -func (u *SignedInUser) GetNamespace() string { - return u.Namespace -} - // GetCacheKey returns a unique key for the entity. // Add an extra prefix to avoid collisions with other caches func (u *SignedInUser) GetCacheKey() string { @@ -193,7 +221,6 @@ func (u *SignedInUser) GetCacheKey() string { return fmt.Sprintf("%d-%s-%s", u.GetOrgID(), typ, id) } -// GetIsGrafanaAdmin returns true if the user is a server admin func (u *SignedInUser) GetIsGrafanaAdmin() bool { return u.IsGrafanaAdmin } @@ -204,6 +231,11 @@ func (u *SignedInUser) GetLogin() string { return u.Login } +// GetUsername implements identity.Requester. +func (u *SignedInUser) GetUsername() string { + return u.Login +} + // GetOrgID returns the ID of the active organization func (u *SignedInUser) GetOrgID() int64 { return u.OrgID @@ -252,12 +284,6 @@ func (u *SignedInUser) GetOrgRole() identity.RoleType { return u.OrgRole } -// GetID returns namespaced id for the entity -func (u *SignedInUser) GetID() string { - ns, id := u.getTypeAndID() - return claims.NewTypeID(ns, id) -} - func (u *SignedInUser) getTypeAndID() (claims.IdentityType, string) { switch { case u.ApiKeyID != 0: @@ -275,10 +301,6 @@ func (u *SignedInUser) getTypeAndID() (claims.IdentityType, string) { return u.FallbackType, strconv.FormatInt(u.UserID, 10) } -func (u *SignedInUser) GetUID() string { - return fmt.Sprintf("%s:%s", u.GetIdentityType(), u.GetRawIdentifier()) -} - func (u *SignedInUser) GetAuthID() string { return u.AuthID } @@ -301,16 +323,6 @@ func (u *SignedInUser) IsNil() bool { return u == nil } -// GetEmail returns the email of the active entity -// Can be empty. -func (u *SignedInUser) GetEmail() string { - return u.Email -} - -func (u *SignedInUser) IsEmailVerified() bool { - return u.EmailVerified -} - func (u *SignedInUser) GetIDToken() string { return u.IDToken } diff --git a/pkg/storage/unified/resource/client.go b/pkg/storage/unified/resource/client.go index c06cd065463..5bbd0d88b9a 100644 --- a/pkg/storage/unified/resource/client.go +++ b/pkg/storage/unified/resource/client.go @@ -142,13 +142,12 @@ func idTokenExtractor(ctx context.Context) (string, error) { // If no token is found, create an internal token. // This is a workaround for StaticRequester not having a signed ID token. if staticRequester, ok := authInfo.(*identity.StaticRequester); ok { - token, idClaims, err := createInternalToken(staticRequester) + token, _, err := createInternalToken(staticRequester) if err != nil { return "", fmt.Errorf("failed to create internal token: %w", err) } staticRequester.IDToken = token - staticRequester.IDTokenClaims = idClaims return token, nil } @@ -170,29 +169,28 @@ func createInternalToken(authInfo claims.AuthInfo) (string, *authnlib.Claims[aut return "", nil, err } - identity := authInfo.GetIdentity() now := time.Now() tokenTTL := 10 * time.Minute idClaims := &auth.IDClaims{ - Claims: &jwt.Claims{ - Audience: identity.Audience(), - Subject: identity.Subject(), + Claims: jwt.Claims{ + Audience: authInfo.GetAudience(), + Subject: authInfo.GetSubject(), Expiry: jwt.NewNumericDate(now.Add(tokenTTL)), IssuedAt: jwt.NewNumericDate(now), }, Rest: authnlib.IDTokenClaims{ - Namespace: identity.Namespace(), - Identifier: identity.Identifier(), - Type: identity.IdentityType(), + Namespace: authInfo.GetNamespace(), + Identifier: authInfo.GetIdentifier(), + Type: authInfo.GetIdentityType(), }, } - if claims.IsIdentityType(identity.IdentityType(), claims.TypeUser) { - idClaims.Rest.Email = identity.Email() - idClaims.Rest.EmailVerified = identity.EmailVerified() - idClaims.Rest.AuthenticatedBy = identity.AuthenticatedBy() - idClaims.Rest.Username = identity.Username() - idClaims.Rest.DisplayName = identity.DisplayName() + if claims.IsIdentityType(authInfo.GetIdentityType(), claims.TypeUser) { + idClaims.Rest.Email = authInfo.GetEmail() + idClaims.Rest.EmailVerified = authInfo.GetEmailVerified() + idClaims.Rest.AuthenticatedBy = authInfo.GetAuthenticatedBy() + idClaims.Rest.Username = authInfo.GetUsername() + idClaims.Rest.DisplayName = authInfo.GetName() } builder := jwt.Signed(signer).Claims(&idClaims.Rest).Claims(idClaims.Claims)