|
|
|
|
@ -2,8 +2,6 @@ package server |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"strings" |
|
|
|
|
|
|
|
|
|
authzv1 "github.com/grafana/authlib/authz/proto/v1" |
|
|
|
|
openfgav1 "github.com/openfga/api/proto/openfga/v1" |
|
|
|
|
@ -12,7 +10,7 @@ import ( |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.CheckResponse, error) { |
|
|
|
|
ctx, span := tracer.Start(ctx, "authzServer.Check") |
|
|
|
|
ctx, span := tracer.Start(ctx, "server.Check") |
|
|
|
|
defer span.End() |
|
|
|
|
|
|
|
|
|
if err := authorize(ctx, r.GetNamespace()); err != nil { |
|
|
|
|
@ -26,8 +24,13 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C |
|
|
|
|
|
|
|
|
|
relation := common.VerbMapping[r.GetVerb()] |
|
|
|
|
|
|
|
|
|
contextuals, err := s.getContextuals(ctx, r.GetSubject()) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
resource := common.NewResourceInfoFromCheck(r) |
|
|
|
|
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, store) |
|
|
|
|
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, contextuals, store) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
@ -37,20 +40,20 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if resource.IsGeneric() { |
|
|
|
|
return s.checkGeneric(ctx, r.GetSubject(), relation, resource, store) |
|
|
|
|
return s.checkGeneric(ctx, r.GetSubject(), relation, resource, contextuals, store) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return s.checkTyped(ctx, r.GetSubject(), relation, resource, store) |
|
|
|
|
return s.checkTyped(ctx, r.GetSubject(), relation, resource, contextuals, store) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// checkGroupResource check if subject has access to the full "GroupResource", if they do they can access every object
|
|
|
|
|
// within it.
|
|
|
|
|
func (s *Server) checkGroupResource(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.CheckResponse, error) { |
|
|
|
|
func (s *Server) checkGroupResource(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.CheckResponse, error) { |
|
|
|
|
if !common.IsGroupResourceRelation(relation) { |
|
|
|
|
return &authzv1.CheckResponse{Allowed: false}, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
req := &openfgav1.CheckRequest{ |
|
|
|
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
StoreId: store.ID, |
|
|
|
|
AuthorizationModelId: store.ModelID, |
|
|
|
|
TupleKey: &openfgav1.CheckRequestTupleKey{ |
|
|
|
|
@ -58,13 +61,8 @@ func (s *Server) checkGroupResource(ctx context.Context, subject, relation strin |
|
|
|
|
Relation: relation, |
|
|
|
|
Object: resource.GroupResourceIdent(), |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if strings.HasPrefix(subject, fmt.Sprintf("%s:", common.TypeRenderService)) { |
|
|
|
|
common.AddRenderContext(req) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
res, err := s.check(ctx, req) |
|
|
|
|
ContextualTuples: contextuals, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
@ -73,13 +71,13 @@ func (s *Server) checkGroupResource(ctx context.Context, subject, relation strin |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// checkTyped checks on our typed resources e.g. folder.
|
|
|
|
|
func (s *Server) checkTyped(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.CheckResponse, error) { |
|
|
|
|
func (s *Server) checkTyped(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.CheckResponse, error) { |
|
|
|
|
if !resource.IsValidRelation(relation) { |
|
|
|
|
return &authzv1.CheckResponse{Allowed: false}, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check if subject has direct access to resource
|
|
|
|
|
res, err := s.check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
StoreId: store.ID, |
|
|
|
|
AuthorizationModelId: store.ModelID, |
|
|
|
|
TupleKey: &openfgav1.CheckRequestTupleKey{ |
|
|
|
|
@ -87,6 +85,7 @@ func (s *Server) checkTyped(ctx context.Context, subject, relation string, resou |
|
|
|
|
Relation: relation, |
|
|
|
|
Object: resource.ResourceIdent(), |
|
|
|
|
}, |
|
|
|
|
ContextualTuples: contextuals, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
@ -102,7 +101,7 @@ func (s *Server) checkTyped(ctx context.Context, subject, relation string, resou |
|
|
|
|
// checkGeneric check our generic "resource" type. It checks:
|
|
|
|
|
// 1. If subject has access as a sub resource for a folder.
|
|
|
|
|
// 2. If subject has direct access to resource.
|
|
|
|
|
func (s *Server) checkGeneric(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.CheckResponse, error) { |
|
|
|
|
func (s *Server) checkGeneric(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.CheckResponse, error) { |
|
|
|
|
var ( |
|
|
|
|
folderIdent = resource.FolderIdent() |
|
|
|
|
resourceCtx = resource.Context() |
|
|
|
|
@ -111,7 +110,7 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res |
|
|
|
|
|
|
|
|
|
if folderIdent != "" && common.IsFolderResourceRelation(folderRelation) { |
|
|
|
|
// Check if subject has access as a sub resource for the folder
|
|
|
|
|
res, err := s.check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
StoreId: store.ID, |
|
|
|
|
AuthorizationModelId: store.ModelID, |
|
|
|
|
TupleKey: &openfgav1.CheckRequestTupleKey{ |
|
|
|
|
@ -119,7 +118,8 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res |
|
|
|
|
Relation: folderRelation, |
|
|
|
|
Object: folderIdent, |
|
|
|
|
}, |
|
|
|
|
Context: resourceCtx, |
|
|
|
|
Context: resourceCtx, |
|
|
|
|
ContextualTuples: contextuals, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
@ -137,7 +137,7 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Check if subject has direct access to resource
|
|
|
|
|
res, err := s.check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{ |
|
|
|
|
StoreId: store.ID, |
|
|
|
|
AuthorizationModelId: store.ModelID, |
|
|
|
|
TupleKey: &openfgav1.CheckRequestTupleKey{ |
|
|
|
|
@ -145,7 +145,8 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res |
|
|
|
|
Relation: relation, |
|
|
|
|
Object: resourceIdent, |
|
|
|
|
}, |
|
|
|
|
Context: resourceCtx, |
|
|
|
|
Context: resourceCtx, |
|
|
|
|
ContextualTuples: contextuals, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
@ -154,12 +155,3 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res |
|
|
|
|
|
|
|
|
|
return &authzv1.CheckResponse{Allowed: res.GetAllowed()}, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (s *Server) check(ctx context.Context, req *openfgav1.CheckRequest) (*openfgav1.CheckResponse, error) { |
|
|
|
|
err := s.addCheckAuthorizationContext(ctx, req) |
|
|
|
|
if err != nil { |
|
|
|
|
s.logger.Error("failed to add authorization context", "error", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return s.openfga.Check(ctx, req) |
|
|
|
|
} |
|
|
|
|
|