mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Use claims.AuthInfo in requester (#91739)
This commit is contained in:
parent
d52626be3f
commit
243c0935fc
5
go.mod
5
go.mod
@ -75,7 +75,8 @@ require (
|
||||
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/alerting v0.0.0-20240723124849-f2ab7c7b8f7d // @grafana/alerting-backend
|
||||
github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 // @grafana/identity-access-team
|
||||
github.com/grafana/authlib v0.0.0-20240809101159-74eaccc31a06 // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 // @grafana/identity-access-team
|
||||
github.com/grafana/codejen v0.0.3 // @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
|
||||
@ -475,7 +476,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect; indirect0.0.0-20240809095826-8eb5495c0b2a
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
)
|
||||
|
6
go.sum
6
go.sum
@ -2309,8 +2309,10 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20240723124849-f2ab7c7b8f7d h1:d2NZeTs+zBPVMd8uOOV5+6lyfs0BCDKxtiNxIMjnPNA=
|
||||
github.com/grafana/alerting v0.0.0-20240723124849-f2ab7c7b8f7d/go.mod h1:DLj8frbtCaITljC2jc0L85JQViPF3mPfOSiYhm1osso=
|
||||
github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 h1:EiaupmOnt6XF/LPxvagjTofWmByzYaf5VyMIF+w/71M=
|
||||
github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1/go.mod h1:YA9We4kTafu7mlMnUh3In6Q2wpg8fYN3ycgCKOK1TB8=
|
||||
github.com/grafana/authlib v0.0.0-20240809101159-74eaccc31a06 h1:qks7nEo/A0+mWvjMjWEIfFD9eIVipb5Lxjfg+HcB5u4=
|
||||
github.com/grafana/authlib v0.0.0-20240809101159-74eaccc31a06/go.mod h1:5uu+ADz2c8bVsXheavXS735IcDuO6M3dr+evuDl8rIE=
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 h1:uD1LcKwvEAqzDsgVChBudPqo5BhPxkj9AgylT5QCReo=
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A=
|
||||
github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw=
|
||||
github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
|
||||
github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ=
|
||||
|
@ -29,6 +29,7 @@ cloud.google.com/go/channel v1.17.5 h1:/omiBnyFjm4S1ETHoOmJbL7LH7Ljcei4rYG6Sj3hc
|
||||
cloud.google.com/go/cloudbuild v1.15.1 h1:ZB6oOmJo+MTov9n629fiCrO9YZPOg25FZvQ7gIHu5ng=
|
||||
cloud.google.com/go/clouddms v1.7.4 h1:Sr0Zo5EAcPQiCBgHWICg3VGkcdS/LLP1d9SR7qQBM/s=
|
||||
cloud.google.com/go/cloudtasks v1.12.6 h1:EUt1hIZ9bLv8Iz9yWaCrqgMnIU+Tdh0yXM1MMVGhjfE=
|
||||
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
|
||||
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
|
||||
cloud.google.com/go/contactcenterinsights v1.13.0 h1:6Vs/YnDG5STGjlWMEjN/xtmft7MrOTOnOZYUZtGTx0w=
|
||||
cloud.google.com/go/container v1.31.0 h1:MAaNH7VRNPWEhvqOypq2j+7ONJKrKzon4v9nS3nLZe0=
|
||||
@ -408,8 +409,8 @@ github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv
|
||||
github.com/grafana/alerting v0.0.0-20240712142914-5558735b4462 h1:MWpvVoPcSej4YfxSIuAllr9vg0UgVEG5CQifD5fK+ps=
|
||||
github.com/grafana/alerting v0.0.0-20240712142914-5558735b4462/go.mod h1:DLj8frbtCaITljC2jc0L85JQViPF3mPfOSiYhm1osso=
|
||||
github.com/grafana/authlib v0.0.0-20240611075137-331cbe4e840f/go.mod h1:+MjD5sxxgLOIvw0ox18wJmjBzz8tOECo7quiiZAmgJY=
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809095826-8eb5495c0b2a/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A=
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.235.0/go.mod h1:6n9LbrjGL3xAATntYVNcIi90G9BVHRJjzHKz5FXVfWw=
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240701135906-559738ce6ae1/go.mod h1:DkxMin+qOh1Fgkxfbt+CUfBqqsCQJMG9op8Os/irBPA=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240422145632-c33c6b5b6e6b h1:HCbWyVL6vi7gxyO76gQksSPH203oBJ1MJ3JcG1OQlsg=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240422145632-c33c6b5b6e6b/go.mod h1:01sXtHoRwI8W324IPAzuxDFOmALqYLCOhvSC2fUHWXc=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
@ -864,7 +865,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
|
||||
google.golang.org/genproto/googleapis/bytestream v0.0.0-20240325203815-454cdb8f5daa h1:wBkzraZsSqhj1M4L/nMrljUU6XasJkgHvUsq8oRGwF0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
|
@ -3,7 +3,8 @@ module github.com/grafana/grafana/pkg/apimachinery
|
||||
go 1.22.4
|
||||
|
||||
require (
|
||||
github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1
|
||||
github.com/grafana/authlib v0.0.0-20240809101159-74eaccc31a06 // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 // @grafana/identity-access-team
|
||||
github.com/stretchr/testify v1.9.0
|
||||
k8s.io/apimachinery v0.31.0-rc.1
|
||||
k8s.io/apiserver v0.31.0-rc.1
|
||||
|
@ -28,8 +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-20240730122259-a0d13672efb1 h1:EiaupmOnt6XF/LPxvagjTofWmByzYaf5VyMIF+w/71M=
|
||||
github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1/go.mod h1:YA9We4kTafu7mlMnUh3In6Q2wpg8fYN3ycgCKOK1TB8=
|
||||
github.com/grafana/authlib v0.0.0-20240809101159-74eaccc31a06 h1:qks7nEo/A0+mWvjMjWEIfFD9eIVipb5Lxjfg+HcB5u4=
|
||||
github.com/grafana/authlib v0.0.0-20240809101159-74eaccc31a06/go.mod h1:5uu+ADz2c8bVsXheavXS735IcDuO6M3dr+evuDl8rIE=
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06 h1:uD1LcKwvEAqzDsgVChBudPqo5BhPxkj9AgylT5QCReo=
|
||||
github.com/grafana/authlib/claims v0.0.0-20240809101159-74eaccc31a06/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A=
|
||||
github.com/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=
|
||||
|
@ -4,12 +4,15 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
)
|
||||
|
||||
type ctxUserKey struct{}
|
||||
|
||||
// WithRequester attaches the requester to the context.
|
||||
func WithRequester(ctx context.Context, usr Requester) context.Context {
|
||||
ctx = claims.WithClaims(ctx, usr) // also set the upstream auth info claims
|
||||
return context.WithValue(ctx, ctxUserKey{}, usr)
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,13 @@ import (
|
||||
"strconv"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/claims"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
type Requester interface {
|
||||
user.Info
|
||||
claims.AuthInfo
|
||||
|
||||
// GetIdentityType returns the type for the requester
|
||||
GetIdentityType() IdentityType
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/claims"
|
||||
)
|
||||
|
||||
var _ Requester = &StaticRequester{}
|
||||
@ -35,6 +36,19 @@ type StaticRequester struct {
|
||||
CacheKey string
|
||||
}
|
||||
|
||||
// Access implements Requester.
|
||||
func (u *StaticRequester) GetAccess() claims.AccessClaims {
|
||||
return &IDClaimsWrapper{Source: u}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
115
pkg/apimachinery/identity/wrapper.go
Normal file
115
pkg/apimachinery/identity/wrapper.go
Normal file
@ -0,0 +1,115 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
)
|
||||
|
||||
var _ claims.IdentityClaims = &IDClaimsWrapper{}
|
||||
var _ claims.AccessClaims = &IDClaimsWrapper{}
|
||||
|
||||
type IDClaimsWrapper struct {
|
||||
Source Requester
|
||||
}
|
||||
|
||||
// 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.GetDisplayName()
|
||||
}
|
||||
|
||||
// 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 claims.IdentityType(i.Source.GetIdentityType())
|
||||
}
|
||||
|
||||
// GetInternalID implements claims.IdentityClaims.
|
||||
func (i *IDClaimsWrapper) InternalID() int64 {
|
||||
v, _ := i.Source.GetInternalID()
|
||||
return v
|
||||
}
|
||||
|
||||
// GetOrgID implements claims.IdentityClaims.
|
||||
func (i *IDClaimsWrapper) OrgID() int64 {
|
||||
return i.Source.GetOrgID()
|
||||
}
|
||||
|
||||
// GetRawUID implements claims.IdentityClaims.
|
||||
func (i *IDClaimsWrapper) UID() 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{}
|
||||
}
|
||||
|
||||
// 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.GetAllowedKubernetesNamespace()
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
}
|
@ -5,9 +5,9 @@ import (
|
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
)
|
||||
@ -39,7 +39,7 @@ func (b *DashboardsAPIBuilder) GetAuthorizer() authorizer.Authorizer {
|
||||
return authorizer.DecisionDeny, "expected namespace", nil
|
||||
}
|
||||
|
||||
info, err := request.ParseNamespace(attr.GetNamespace())
|
||||
info, err := claims.ParseNamespace(attr.GetNamespace())
|
||||
if err != nil {
|
||||
return authorizer.DecisionDeny, "error reading org from namespace", err
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
dashboard "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
@ -43,7 +43,7 @@ func isDashboardKey(key *resource.ResourceKey, requireName bool) error {
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) WriteEvent(ctx context.Context, event resource.WriteEvent) (rv int64, err error) {
|
||||
info, err := request.ParseNamespace(event.Key.Namespace)
|
||||
info, err := claims.ParseNamespace(event.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(event.Key, true)
|
||||
}
|
||||
@ -125,7 +125,7 @@ func (a *dashboardSqlAccess) GetDashboard(ctx context.Context, orgId int64, uid
|
||||
// Read implements ResourceStoreServer.
|
||||
func (a *dashboardSqlAccess) ReadResource(ctx context.Context, req *resource.ReadRequest) *resource.ReadResponse {
|
||||
rsp := &resource.ReadResponse{}
|
||||
info, err := request.ParseNamespace(req.Key.Namespace)
|
||||
info, err := claims.ParseNamespace(req.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(req.Key, true)
|
||||
}
|
||||
@ -160,7 +160,7 @@ func (a *dashboardSqlAccess) ReadResource(ctx context.Context, req *resource.Rea
|
||||
// List implements AppendingStore.
|
||||
func (a *dashboardSqlAccess) ListIterator(ctx context.Context, req *resource.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
opts := req.Options
|
||||
info, err := request.ParseNamespace(opts.Key.Namespace)
|
||||
info, err := claims.ParseNamespace(opts.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(opts.Key, false)
|
||||
}
|
||||
@ -237,7 +237,7 @@ func (a *dashboardSqlAccess) Read(ctx context.Context, req *resource.ReadRequest
|
||||
}
|
||||
|
||||
func (a *dashboardSqlAccess) History(ctx context.Context, req *resource.HistoryRequest) (*resource.HistoryResponse, error) {
|
||||
info, err := request.ParseNamespace(req.Key.Namespace)
|
||||
info, err := claims.ParseNamespace(req.Key.Namespace)
|
||||
if err == nil {
|
||||
err = isDashboardKey(req.Key, false)
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
dashboardsnapshot "github.com/grafana/grafana/pkg/apis/dashboardsnapshot/v0alpha1"
|
||||
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
@ -242,7 +243,7 @@ func (b *SnapshotsAPIBuilder) GetAPIRoutes() *builder.APIRoutes {
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
info, err := request.ParseNamespace(vars["namespace"])
|
||||
info, err := claims.ParseNamespace(vars["namespace"])
|
||||
if err != nil {
|
||||
wrap.JsonApiErr(http.StatusBadRequest, "expected namespace", nil)
|
||||
return
|
||||
|
@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
grafanarequest "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
)
|
||||
@ -31,7 +31,7 @@ func (auth orgIDAuthorizer) Authorize(ctx context.Context, a authorizer.Attribut
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("error getting signed in user: %v", err), nil
|
||||
}
|
||||
|
||||
info, err := grafanarequest.ParseNamespace(a.GetNamespace())
|
||||
info, err := claims.ParseNamespace(a.GetNamespace())
|
||||
if err != nil {
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("error reading namespace: %v", err), nil
|
||||
}
|
||||
@ -45,7 +45,7 @@ func (auth orgIDAuthorizer) Authorize(ctx context.Context, a authorizer.Attribut
|
||||
return authorizer.DecisionDeny, "org id is required", nil
|
||||
}
|
||||
|
||||
if info.StackID != "" {
|
||||
if info.StackID != 0 {
|
||||
return authorizer.DecisionDeny, "using a stack namespace requires deployment with a fixed stack id", nil
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,11 @@ package authorizer
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
grafanarequest "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
)
|
||||
@ -15,13 +16,17 @@ var _ authorizer.Authorizer = &stackIDAuthorizer{}
|
||||
|
||||
type stackIDAuthorizer struct {
|
||||
log log.Logger
|
||||
stackID string
|
||||
stackID int64
|
||||
}
|
||||
|
||||
func newStackIDAuthorizer(cfg *setting.Cfg) *stackIDAuthorizer {
|
||||
stackID, err := strconv.ParseInt(cfg.StackID, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &stackIDAuthorizer{
|
||||
log: log.New("grafana-apiserver.authorizer.stackid"),
|
||||
stackID: cfg.StackID, // this lets a single tenant grafana validate stack id (rather than orgs)
|
||||
stackID: stackID, // this lets a single tenant grafana validate stack id (rather than orgs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +36,7 @@ func (auth stackIDAuthorizer) Authorize(ctx context.Context, a authorizer.Attrib
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("error getting signed in user: %v", err), nil
|
||||
}
|
||||
|
||||
info, err := grafanarequest.ParseNamespace(a.GetNamespace())
|
||||
info, err := claims.ParseNamespace(a.GetNamespace())
|
||||
if err != nil {
|
||||
return authorizer.DecisionDeny, fmt.Sprintf("error reading namespace: %v", err), nil
|
||||
}
|
||||
|
@ -3,82 +3,34 @@ package request
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type NamespaceInfo struct {
|
||||
// OrgID defined in namespace (1 when using stack ids)
|
||||
OrgID int64
|
||||
|
||||
// The cloud stack ID (must match the value in cfg.Settings)
|
||||
StackID string
|
||||
|
||||
// The original namespace string regardless the input
|
||||
Value string
|
||||
}
|
||||
|
||||
// NamespaceMapper converts an orgID into a namespace
|
||||
type NamespaceMapper = func(orgId int64) string
|
||||
type NamespaceMapper = claims.NamespaceFormatter
|
||||
|
||||
// GetNamespaceMapper returns a function that will convert orgIds into a consistent namespace
|
||||
func GetNamespaceMapper(cfg *setting.Cfg) NamespaceMapper {
|
||||
if cfg != nil && cfg.StackID != "" {
|
||||
//val := claims.CloudNamespaceFormatter(cfg.Sta)
|
||||
return func(orgId int64) string { return "stack-" + cfg.StackID }
|
||||
}
|
||||
return func(orgId int64) string {
|
||||
if orgId == 1 {
|
||||
return "default"
|
||||
}
|
||||
return fmt.Sprintf("org-%d", orgId)
|
||||
}
|
||||
return claims.OrgNamespaceFormatter
|
||||
}
|
||||
|
||||
func NamespaceInfoFrom(ctx context.Context, requireOrgID bool) (NamespaceInfo, error) {
|
||||
info, err := ParseNamespace(request.NamespaceValue(ctx))
|
||||
func NamespaceInfoFrom(ctx context.Context, requireOrgID bool) (claims.NamespaceInfo, error) {
|
||||
info, err := claims.ParseNamespace(request.NamespaceValue(ctx))
|
||||
if err == nil && requireOrgID && info.OrgID < 1 {
|
||||
return info, fmt.Errorf("expected valid orgId in namespace")
|
||||
}
|
||||
return info, err
|
||||
}
|
||||
|
||||
func ParseNamespace(ns string) (NamespaceInfo, error) {
|
||||
info := NamespaceInfo{Value: ns, OrgID: -1}
|
||||
if ns == "default" {
|
||||
info.OrgID = 1
|
||||
return info, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ns, "org-") {
|
||||
id, err := strconv.Atoi(ns[4:])
|
||||
if id < 1 {
|
||||
return info, fmt.Errorf("invalid org id")
|
||||
}
|
||||
if id == 1 {
|
||||
return info, fmt.Errorf("use default rather than org-1")
|
||||
}
|
||||
info.OrgID = int64(id)
|
||||
return info, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ns, "stack-") {
|
||||
stackIDStr := ns[6:]
|
||||
stackID, err := strconv.Atoi(stackIDStr)
|
||||
if err != nil || stackID < 1 {
|
||||
return info, fmt.Errorf("invalid stack id")
|
||||
}
|
||||
info.StackID = stackIDStr
|
||||
info.OrgID = 1
|
||||
return info, nil
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func OrgIDForList(ctx context.Context) (int64, error) {
|
||||
ns := request.NamespaceValue(ctx)
|
||||
if ns == "" {
|
||||
@ -88,6 +40,6 @@ func OrgIDForList(ctx context.Context) (int64, error) {
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
info, err := ParseNamespace(ns)
|
||||
info, err := claims.ParseNamespace(ns)
|
||||
return info.OrgID, err
|
||||
}
|
||||
|
@ -9,134 +9,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestParseNamespace(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
namespace string
|
||||
expected request.NamespaceInfo
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty namespace",
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "incorrect number of parts",
|
||||
namespace: "org-123-a",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org id not a number",
|
||||
namespace: "org-invalid",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid org id",
|
||||
namespace: "org-123",
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: 123,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org should not be 1 in the namespace",
|
||||
namespace: "org-1",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "can not be negative",
|
||||
namespace: "org--5",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "can not be zero",
|
||||
namespace: "org-0",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default is org 1",
|
||||
namespace: "default",
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid stack id (must be an int)",
|
||||
expectErr: true,
|
||||
namespace: "stack-abcdef",
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid stack id (must be provided)",
|
||||
namespace: "stack-",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid stack id (cannot be 0)",
|
||||
namespace: "stack-0",
|
||||
expectErr: true,
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid stack",
|
||||
namespace: "stack-1",
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: 1,
|
||||
StackID: "1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "other namespace",
|
||||
namespace: "anything",
|
||||
expected: request.NamespaceInfo{
|
||||
OrgID: -1,
|
||||
Value: "anything",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
info, err := request.ParseNamespace(tt.namespace)
|
||||
if tt.expectErr != (err != nil) {
|
||||
t.Errorf("ParseNamespace() returned %+v, expected an error", info)
|
||||
}
|
||||
if info.OrgID != tt.expected.OrgID {
|
||||
t.Errorf("ParseNamespace() [OrgID] returned %d, expected %d", info.OrgID, tt.expected.OrgID)
|
||||
}
|
||||
if info.StackID != tt.expected.StackID {
|
||||
t.Errorf("ParseNamespace() [StackID] returned %s, expected %s", info.StackID, tt.expected.StackID)
|
||||
}
|
||||
if info.Value != tt.namespace {
|
||||
t.Errorf("ParseNamespace() [Value] returned %s, expected %s", info.Value, tt.namespace)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceMapper(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/claims"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
@ -74,6 +75,19 @@ type Identity struct {
|
||||
IDTokenClaims *authn.Claims[authn.IDTokenClaims]
|
||||
}
|
||||
|
||||
// Access implements claims.AuthInfo.
|
||||
func (i *Identity) GetAccess() claims.AccessClaims {
|
||||
return &identity.IDClaimsWrapper{Source: i}
|
||||
}
|
||||
|
||||
// Identity implements claims.AuthInfo.
|
||||
func (i *Identity) GetIdentity() claims.IdentityClaims {
|
||||
if i.IDTokenClaims != nil {
|
||||
return authn.NewIdentityClaims(*i.IDTokenClaims)
|
||||
}
|
||||
return &identity.IDClaimsWrapper{Source: i}
|
||||
}
|
||||
|
||||
// GetRawIdentifier implements Requester.
|
||||
func (i *Identity) GetRawIdentifier() string {
|
||||
return i.UID.ID()
|
||||
|
@ -104,6 +104,7 @@ func newInProcLegacyClient(server *legacyServer) (authzlib.MultiTenantClient, er
|
||||
return authzlib.NewLegacyClient(
|
||||
&authzlib.MultiTenantClientConfig{},
|
||||
authzlib.WithGrpcConnectionLCOption(channel),
|
||||
// nolint:staticcheck
|
||||
authzlib.WithNamespaceFormatterLCOption(authnlib.OnPremNamespaceFormatter),
|
||||
authzlib.WithDisableAccessTokenLCOption(),
|
||||
)
|
||||
@ -127,6 +128,7 @@ func newGrpcLegacyClient(address string) (authzlib.MultiTenantClient, error) {
|
||||
grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor),
|
||||
grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor),
|
||||
),
|
||||
// nolint:staticcheck
|
||||
authzlib.WithNamespaceFormatterLCOption(authnlib.OnPremNamespaceFormatter),
|
||||
// TODO(drclau): remove this once we have access token support on-prem
|
||||
authzlib.WithDisableAccessTokenLCOption(),
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
authnlib "github.com/grafana/authlib/authn"
|
||||
"github.com/grafana/authlib/claims"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
@ -52,6 +53,19 @@ type SignedInUser struct {
|
||||
FallbackType identity.IdentityType
|
||||
}
|
||||
|
||||
// Access implements claims.AuthInfo.
|
||||
func (u *SignedInUser) GetAccess() claims.AccessClaims {
|
||||
return &identity.IDClaimsWrapper{Source: u}
|
||||
}
|
||||
|
||||
// Identity implements claims.AuthInfo.
|
||||
func (u *SignedInUser) GetIdentity() claims.IdentityClaims {
|
||||
if u.IDTokenClaims != nil {
|
||||
return authnlib.NewIdentityClaims(*u.IDTokenClaims)
|
||||
}
|
||||
return &identity.IDClaimsWrapper{Source: u}
|
||||
}
|
||||
|
||||
// GetRawIdentifier implements Requester.
|
||||
func (u *SignedInUser) GetRawIdentifier() string {
|
||||
if u.UserUID == "" {
|
||||
|
Loading…
Reference in New Issue
Block a user