IAM: align AuthInfo interface (#97228)
* Update to use updated interface
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
+1
-2
@@ -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=
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user