Authn: Always set namespace (#96230)

* Rename from AllowedKubernetesNamespace to Namespace

* Use a sync hook to always set namespace for Identity.

* format

* Don't set uid when authenticating as user
This commit is contained in:
Karl Persson 2024-11-12 10:12:47 +01:00 committed by GitHub
parent 7eb4b974e0
commit 8d74296b6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 156 additions and 125 deletions

View File

@ -52,9 +52,9 @@ type Requester interface {
GetOrgName() string
// GetAuthID returns external id for entity.
GetAuthID() string
// GetAllowedKubernetesNamespace returns either "*" or the single namespace this requester has access to
// GetNamespace returns either "*" or the single namespace this requester has access to
// An empty value means the implementation has not specified a kubernetes namespace.
GetAllowedKubernetesNamespace() string
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.

View File

@ -27,7 +27,7 @@ type StaticRequester struct {
EmailVerified bool
AuthID string
AuthenticatedBy string
AllowedKubernetesNamespace string
Namespace string
IsGrafanaAdmin bool
// Permissions grouped by orgID and actions
Permissions map[int64]map[string][]string
@ -175,8 +175,8 @@ func (u *StaticRequester) GetAuthID() string {
return u.AuthID
}
func (u *StaticRequester) GetAllowedKubernetesNamespace() string {
return u.AllowedKubernetesNamespace
func (u *StaticRequester) GetNamespace() string {
return u.Namespace
}
func (u *StaticRequester) GetAuthenticatedBy() string {

View File

@ -85,7 +85,7 @@ func (i *IDClaimsWrapper) JTI() string {
// GetNamespace implements claims.AccessClaims.
func (i *IDClaimsWrapper) Namespace() string {
return i.Source.GetAllowedKubernetesNamespace()
return i.Source.GetNamespace()
}
// GetNotBefore implements claims.AccessClaims.

View File

@ -46,7 +46,7 @@ func WithRequester(handler http.Handler) http.Handler {
OrgRole: identity.RoleAdmin,
IsGrafanaAdmin: true,
AllowedKubernetesNamespace: "default",
Namespace: "default",
Permissions: map[int64]map[string][]string{
orgId: {

View File

@ -119,5 +119,7 @@ func ProvideRegistration(
authnSvc.RegisterPostAuthHook(rbacSync.SyncPermissionsHook, 120)
authnSvc.RegisterPostLoginHook(orgSync.SetDefaultOrgHook, 140)
nsSync := sync.ProvideNamespaceSync(cfg)
authnSvc.RegisterPostAuthHook(nsSync.SyncNamespace, 150)
return Registration{}
}

View File

@ -0,0 +1,28 @@
package sync
import (
"context"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/setting"
)
func ProvideNamespaceSync(cfg *setting.Cfg) *NamespaceSync {
return &NamespaceSync{
mapper: request.GetNamespaceMapper(cfg),
}
}
type NamespaceSync struct {
mapper request.NamespaceMapper
}
func (s *NamespaceSync) SyncNamespace(ctx context.Context, id *authn.Identity, _ *authn.Request) error {
if id.Namespace != "" {
return nil
}
id.Namespace = s.mapper(id.OrgID)
return nil
}

View File

@ -129,10 +129,10 @@ func (s *ExtendedJWT) authenticateAsUser(
}
// For use in service layer, allow higher privilege
allowedKubernetesNamespace := accessTokenClaims.Rest.Namespace
namespace := accessTokenClaims.Rest.Namespace
if len(s.cfg.StackID) > 0 {
// For single-tenant cloud use, choose the lower of the two (id token will always have the specific namespace)
allowedKubernetesNamespace = idTokenClaims.Rest.Namespace
namespace = idTokenClaims.Rest.Namespace
}
return &authn.Identity{
@ -143,7 +143,7 @@ func (s *ExtendedJWT) authenticateAsUser(
IDTokenClaims: &idTokenClaims,
AuthenticatedBy: login.ExtendedJWTModule,
AuthID: accessTokenClaims.Subject,
AllowedKubernetesNamespace: allowedKubernetesNamespace,
Namespace: namespace,
ClientParams: authn.ClientParams{
SyncPermissions: true,
FetchPermissionsParams: authn.FetchPermissionsParams{
@ -192,7 +192,7 @@ func (s *ExtendedJWT) authenticateAsService(accessTokenClaims authlib.Claims[aut
AccessTokenClaims: &accessTokenClaims,
AuthenticatedBy: login.ExtendedJWTModule,
AuthID: accessTokenClaims.Subject,
AllowedKubernetesNamespace: accessTokenClaims.Rest.Namespace,
Namespace: accessTokenClaims.Rest.Namespace,
ClientParams: authn.ClientParams{
SyncPermissions: true,
FetchPermissionsParams: fetchPermissionsParams,

View File

@ -232,7 +232,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
Type: claims.TypeAccessPolicy,
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaims,
AllowedKubernetesNamespace: "default",
Namespace: "default",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -251,7 +251,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
Type: claims.TypeAccessPolicy,
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWildcard,
AllowedKubernetesNamespace: "*",
Namespace: "*",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -270,7 +270,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaims,
IDTokenClaims: &validIDTokenClaims,
AllowedKubernetesNamespace: "default",
Namespace: "default",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -293,7 +293,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWildcard,
IDTokenClaims: &validIDTokenClaims,
AllowedKubernetesNamespace: "*",
Namespace: "*",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -321,7 +321,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWildcard,
IDTokenClaims: &validIDTokenClaimsWithStackSet,
AllowedKubernetesNamespace: "stacks-1234",
Namespace: "stacks-1234",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -349,7 +349,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
Type: claims.TypeAccessPolicy,
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWithStackSet,
AllowedKubernetesNamespace: "stacks-1234",
Namespace: "stacks-1234",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -376,7 +376,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
Type: claims.TypeAccessPolicy,
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWithDeprecatedStackClaimSet,
AllowedKubernetesNamespace: "stack-1234",
Namespace: "stack-1234",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -403,7 +403,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWithDeprecatedStackClaimSet,
IDTokenClaims: &validIDTokenClaimsWithDeprecatedStackClaimSet,
AllowedKubernetesNamespace: "stack-1234",
Namespace: "stack-1234",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{
@ -431,7 +431,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
OrgID: 1,
AccessTokenClaims: &validAccessTokenClaimsWildcard,
IDTokenClaims: &validIDTokenClaimsWithStackSet,
AllowedKubernetesNamespace: "stacks-1234",
Namespace: "stacks-1234",
AuthenticatedBy: "extendedjwt",
AuthID: "access-policy:this-uid",
ClientParams: authn.ClientParams{

View File

@ -49,8 +49,8 @@ type Identity struct {
// AuthId is the unique identifier for the entity in the external system.
// Empty if the identity is provided by Grafana.
AuthID string
// AllowedKubernetesNamespace
AllowedKubernetesNamespace string
// Namespace
Namespace string
// IsDisabled is true if the entity is disabled.
IsDisabled bool
// HelpFlags1 is the help flags for the entity.
@ -189,8 +189,8 @@ func (i *Identity) GetLogin() string {
return i.Login
}
func (i *Identity) GetAllowedKubernetesNamespace() string {
return i.AllowedKubernetesNamespace
func (i *Identity) GetNamespace() string {
return i.Namespace
}
func (i *Identity) GetOrgID() int64 {
@ -287,6 +287,7 @@ func (i *Identity) SignedInUser() *user.SignedInUser {
Permissions: i.Permissions,
IDToken: i.IDToken,
FallbackType: i.Type,
Namespace: i.Namespace,
}
if i.IsIdentityType(claims.TypeAPIKey) {

View File

@ -31,7 +31,7 @@ type SignedInUser struct {
AuthID string
// AuthenticatedBy be set if user signed in using external method
AuthenticatedBy string
AllowedKubernetesNamespace string
Namespace string
ApiKeyID int64 `xorm:"api_key_id"`
IsServiceAccount bool `xorm:"is_service_account"`
@ -173,8 +173,8 @@ func (u *SignedInUser) HasUniqueId() bool {
return u.IsRealUser() || u.IsApiKeyUser() || u.IsServiceAccountUser()
}
func (u *SignedInUser) GetAllowedKubernetesNamespace() string {
return u.AllowedKubernetesNamespace
func (u *SignedInUser) GetNamespace() string {
return u.Namespace
}
// GetCacheKey returns a unique key for the entity.