mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AuthZ service: Support anonymous access (#98322)
support anonymous access
This commit is contained in:
parent
efb7cc0343
commit
1334caa6c8
@ -8,6 +8,10 @@ func userIdentifierCacheKeyById(namespace, ID string) string {
|
||||
return "ID_" + namespace + "_" + ID
|
||||
}
|
||||
|
||||
func anonymousPermCacheKey(namespace, action string) string {
|
||||
return namespace + "_anonymous_" + action
|
||||
}
|
||||
|
||||
func userPermCacheKey(namespace, userUID, action string) string {
|
||||
return namespace + "_" + userUID + "_" + action
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import "github.com/grafana/authlib/claims"
|
||||
|
||||
type CheckRequest struct {
|
||||
Namespace claims.NamespaceInfo
|
||||
IdentityType claims.IdentityType
|
||||
UserUID string
|
||||
Action string
|
||||
Group string
|
||||
@ -14,12 +15,13 @@ type CheckRequest struct {
|
||||
}
|
||||
|
||||
type ListRequest struct {
|
||||
Namespace claims.NamespaceInfo
|
||||
UserUID string
|
||||
Group string
|
||||
Resource string
|
||||
Verb string
|
||||
Action string
|
||||
Namespace claims.NamespaceInfo
|
||||
IdentityType claims.IdentityType
|
||||
UserUID string
|
||||
Group string
|
||||
Resource string
|
||||
Verb string
|
||||
Action string
|
||||
}
|
||||
|
||||
type FolderNode struct {
|
||||
|
@ -86,7 +86,7 @@ func (s *Service) Check(ctx context.Context, req *authzv1.CheckRequest) (*authzv
|
||||
}
|
||||
ctx = request.WithNamespace(ctx, req.GetNamespace())
|
||||
|
||||
permissions, err := s.getUserPermissions(ctx, checkReq.Namespace, checkReq.UserUID, checkReq.Action)
|
||||
permissions, err := s.getUserPermissions(ctx, checkReq.Namespace, checkReq.IdentityType, checkReq.UserUID, checkReq.Action)
|
||||
if err != nil {
|
||||
ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err)
|
||||
return deny, err
|
||||
@ -112,7 +112,7 @@ func (s *Service) List(ctx context.Context, req *authzv1.ListRequest) (*authzv1.
|
||||
}
|
||||
ctx = request.WithNamespace(ctx, req.GetNamespace())
|
||||
|
||||
permissions, err := s.getUserPermissions(ctx, listReq.Namespace, listReq.UserUID, listReq.Action)
|
||||
permissions, err := s.getUserPermissions(ctx, listReq.Namespace, listReq.IdentityType, listReq.UserUID, listReq.Action)
|
||||
if err != nil {
|
||||
ctxLogger.Error("could not get user permissions", "subject", req.GetSubject(), "error", err)
|
||||
return nil, err
|
||||
@ -127,7 +127,7 @@ func (s *Service) validateCheckRequest(ctx context.Context, req *authzv1.CheckRe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userUID, err := s.validateSubject(ctx, req.GetSubject())
|
||||
userUID, idType, err := s.validateSubject(ctx, req.GetSubject())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -140,6 +140,7 @@ func (s *Service) validateCheckRequest(ctx context.Context, req *authzv1.CheckRe
|
||||
checkReq := &CheckRequest{
|
||||
Namespace: ns,
|
||||
UserUID: userUID,
|
||||
IdentityType: idType,
|
||||
Action: action,
|
||||
Group: req.GetGroup(),
|
||||
Resource: req.GetResource(),
|
||||
@ -156,7 +157,7 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userUID, err := s.validateSubject(ctx, req.GetSubject())
|
||||
userUID, idType, err := s.validateSubject(ctx, req.GetSubject())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -167,12 +168,13 @@ func (s *Service) validateListRequest(ctx context.Context, req *authzv1.ListRequ
|
||||
}
|
||||
|
||||
listReq := &ListRequest{
|
||||
Namespace: ns,
|
||||
UserUID: userUID,
|
||||
Action: action,
|
||||
Group: req.GetGroup(),
|
||||
Resource: req.GetResource(),
|
||||
Verb: req.GetVerb(),
|
||||
Namespace: ns,
|
||||
UserUID: userUID,
|
||||
IdentityType: idType,
|
||||
Action: action,
|
||||
Group: req.GetGroup(),
|
||||
Resource: req.GetResource(),
|
||||
Verb: req.GetVerb(),
|
||||
}
|
||||
return listReq, nil
|
||||
}
|
||||
@ -196,21 +198,22 @@ func validateNamespace(ctx context.Context, nameSpace string) (claims.NamespaceI
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func (s *Service) validateSubject(ctx context.Context, subject string) (string, error) {
|
||||
func (s *Service) validateSubject(ctx context.Context, subject string) (string, claims.IdentityType, error) {
|
||||
if subject == "" {
|
||||
return "", status.Error(codes.InvalidArgument, "subject is required")
|
||||
return "", "", status.Error(codes.InvalidArgument, "subject is required")
|
||||
}
|
||||
|
||||
ctxLogger := s.logger.FromContext(ctx)
|
||||
identityType, userUID, err := claims.ParseTypeID(subject)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
// Permission check currently only checks user and service account permissions, so might return a false negative for other types
|
||||
if !(identityType == claims.TypeUser || identityType == claims.TypeServiceAccount) {
|
||||
ctxLogger.Warn("unsupported identity type", "type", identityType)
|
||||
// Permission check currently only checks user, anonymous user and service account permissions
|
||||
if !(identityType == claims.TypeUser || identityType == claims.TypeServiceAccount || identityType == claims.TypeAnonymous) {
|
||||
ctxLogger.Error("unsupported identity type", "type", identityType)
|
||||
return "", "", status.Error(codes.PermissionDenied, "unsupported identity type")
|
||||
}
|
||||
return userUID, nil
|
||||
return userUID, identityType, nil
|
||||
}
|
||||
|
||||
func (s *Service) validateAction(ctx context.Context, group, resource, verb string) (string, error) {
|
||||
@ -226,7 +229,11 @@ func (s *Service) validateAction(ctx context.Context, group, resource, verb stri
|
||||
return action, nil
|
||||
}
|
||||
|
||||
func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInfo, userID, action string) (map[string]bool, error) {
|
||||
func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInfo, idType claims.IdentityType, userID, action string) (map[string]bool, error) {
|
||||
if idType == claims.TypeAnonymous {
|
||||
return s.getAnonymousPermissions(ctx, ns, action)
|
||||
}
|
||||
|
||||
userIdentifiers, err := s.GetUserIdentifiers(ctx, ns, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -264,6 +271,21 @@ func (s *Service) getUserPermissions(ctx context.Context, ns claims.NamespaceInf
|
||||
return scopeMap, nil
|
||||
}
|
||||
|
||||
func (s *Service) getAnonymousPermissions(ctx context.Context, ns claims.NamespaceInfo, action string) (map[string]bool, error) {
|
||||
anonPermKey := anonymousPermCacheKey(ns.Value, action)
|
||||
if cached, ok := s.permCache.Get(anonPermKey); ok {
|
||||
return cached.(map[string]bool), nil
|
||||
}
|
||||
|
||||
permissions, err := s.store.GetUserPermissions(ctx, ns, store.PermissionsQuery{Action: action, Role: "Viewer"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scopeMap := getScopeMap(permissions)
|
||||
s.permCache.Set(anonPermKey, scopeMap, 0)
|
||||
return scopeMap, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetUserIdentifiers(ctx context.Context, ns claims.NamespaceInfo, userUID string) (*store.UserIdentifiers, error) {
|
||||
uidCacheKey := userIdentifierCacheKey(ns.Value, userUID)
|
||||
if cached, ok := s.idCache.Get(uidCacheKey); ok {
|
||||
|
@ -370,7 +370,7 @@ func TestService_getUserPermissions(t *testing.T) {
|
||||
teamCache: localcache.New(shortCacheTTL, shortCleanupInterval),
|
||||
}
|
||||
|
||||
perms, err := s.getUserPermissions(ctx, ns, userID.UID, action)
|
||||
perms, err := s.getUserPermissions(ctx, ns, claims.TypeUser, userID.UID, action)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, perms, len(tc.expectedPerms))
|
||||
for _, perm := range tc.permissions {
|
||||
|
@ -1,13 +1,15 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM {{ .Ident .PermissionTable }} as p
|
||||
WHERE p.action = {{ .Arg .Query.Action }} AND p.role_id IN (
|
||||
SELECT role_id FROM {{ .Ident .BuiltinRoleTable }} as br WHERE (br.role = {{ .Arg .Query.Role }} AND (br.org_id = {{ .Arg .Query.OrgID }} OR br.org_id = 0))
|
||||
{{ if .Query.IsServerAdmin }}
|
||||
OR (br.role = 'Grafana Admin')
|
||||
{{ end }}
|
||||
{{ if .Query.UserID }}
|
||||
UNION
|
||||
SELECT role_id FROM {{ .Ident .UserRoleTable }} as ur WHERE ur.user_id = {{ .Arg .Query.UserID }} AND (ur.org_id = {{ .Arg .Query.OrgID }} OR ur.org_id = 0)
|
||||
{{ if .Query.TeamIDs }}
|
||||
{{ end }}
|
||||
{{ if .Query.TeamIDs }}
|
||||
UNION
|
||||
SELECT role_id FROM {{ .Ident .TeamRoleTable }} as tr WHERE tr.team_id IN ({{ .ArgList .Query.TeamIDs }}) AND tr.org_id = {{ .Arg .Query.OrgID }}
|
||||
{{ end }}
|
||||
UNION ALL
|
||||
SELECT role_id FROM {{ .Ident .BuiltinRoleTable }} as br WHERE (br.role = {{ .Arg .Query.Role }} AND (br.org_id = {{ .Arg .Query.OrgID }} OR br.org_id = 0))
|
||||
{{ if .Query.IsServerAdmin }}
|
||||
OR (br.role = 'Grafana Admin')
|
||||
{{ end }}
|
||||
)
|
||||
|
@ -97,6 +97,14 @@ func TestIdentityQueries(t *testing.T) {
|
||||
TeamIDs: []int64{1, 2},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "anonymous_user",
|
||||
Data: getPermissions(&PermissionsQuery{
|
||||
OrgID: 1,
|
||||
Action: "folders:read",
|
||||
Role: "Viewer",
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlFolders: {
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION ALL
|
||||
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
OR (br.role = 'Grafana Admin')
|
||||
OR (br.role = 'Grafana Admin')
|
||||
UNION
|
||||
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
)
|
||||
|
4
pkg/services/authz/rbac/store/testdata/mysql--permission_query-anonymous_user.sql
vendored
Executable file
4
pkg/services/authz/rbac/store/testdata/mysql--permission_query-anonymous_user.sql
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
)
|
@ -1,8 +1,8 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
UNION
|
||||
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION
|
||||
SELECT role_id FROM `grafana`.`team_role` as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
|
||||
UNION ALL
|
||||
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM `grafana`.`permission` as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION ALL
|
||||
SELECT role_id FROM `grafana`.`builtin_role` as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
UNION
|
||||
SELECT role_id FROM `grafana`.`user_role` as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION ALL
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
OR (br.role = 'Grafana Admin')
|
||||
OR (br.role = 'Grafana Admin')
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
)
|
||||
|
4
pkg/services/authz/rbac/store/testdata/postgres--permission_query-anonymous_user.sql
vendored
Executable file
4
pkg/services/authz/rbac/store/testdata/postgres--permission_query-anonymous_user.sql
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
)
|
@ -1,8 +1,8 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
|
||||
UNION ALL
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION ALL
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION ALL
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Admin' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
OR (br.role = 'Grafana Admin')
|
||||
OR (br.role = 'Grafana Admin')
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
)
|
||||
|
4
pkg/services/authz/rbac/store/testdata/sqlite--permission_query-anonymous_user.sql
vendored
Executable file
4
pkg/services/authz/rbac/store/testdata/sqlite--permission_query-anonymous_user.sql
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
)
|
@ -1,8 +1,8 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."team_role" as tr WHERE tr.team_id IN (1, 2) AND tr.org_id = 1
|
||||
UNION ALL
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'None' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
SELECT p.action, p.kind, p.attribute, p.identifier, p.scope FROM "grafana"."permission" as p
|
||||
WHERE p.action = 'folders:read' AND p.role_id IN (
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
UNION ALL
|
||||
SELECT role_id FROM "grafana"."builtin_role" as br WHERE (br.role = 'Viewer' AND (br.org_id = 1 OR br.org_id = 0))
|
||||
UNION
|
||||
SELECT role_id FROM "grafana"."user_role" as ur WHERE ur.user_id = 1 AND (ur.org_id = 1 OR ur.org_id = 0)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user