Auth: Use claims.AuthInfo in requester (#91739)
This commit is contained in:
@@ -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 == "" {
|
||||
|
||||
Reference in New Issue
Block a user