95 lines
2.8 KiB
Go
95 lines
2.8 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
|
"go.opentelemetry.io/otel/codes"
|
|
)
|
|
|
|
func (s *Server) Query(ctx context.Context, req *authzextv1.QueryRequest) (*authzextv1.QueryResponse, error) {
|
|
ctx, span := s.tracer.Start(ctx, "server.Query")
|
|
defer span.End()
|
|
|
|
defer func(t time.Time) {
|
|
s.metrics.requestDurationSeconds.WithLabelValues("server.Query", req.GetNamespace()).Observe(time.Since(t).Seconds())
|
|
}(time.Now())
|
|
|
|
res, err := s.query(ctx, req)
|
|
if err != nil {
|
|
span.RecordError(err)
|
|
span.SetStatus(codes.Error, err.Error())
|
|
s.logger.Error("failed to perform query request", "error", err, "namespace", req.GetNamespace())
|
|
return nil, errors.New("failed to perform query request")
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (s *Server) query(ctx context.Context, req *authzextv1.QueryRequest) (*authzextv1.QueryResponse, error) {
|
|
if err := authorize(ctx, req.GetNamespace(), s.cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
storeInf, err := s.getStoreInfo(ctx, req.Namespace)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get openfga store: %w", err)
|
|
}
|
|
|
|
if req.Operation == nil {
|
|
return nil, errors.New("operation cannot be nil")
|
|
}
|
|
|
|
switch op := req.Operation.Operation.(type) {
|
|
case *authzextv1.QueryOperation_GetFolderParents:
|
|
return s.queryFolderParents(ctx, storeInf, op.GetFolderParents)
|
|
default:
|
|
return nil, errors.New("unsupported query operation type")
|
|
}
|
|
}
|
|
|
|
func (s *Server) queryFolderParents(ctx context.Context, store *storeInfo, req *authzextv1.GetFolderParentsQuery) (*authzextv1.QueryResponse, error) {
|
|
ctx, span := s.tracer.Start(ctx, "server.queryFolderParents")
|
|
defer span.End()
|
|
|
|
if req.GetFolder() == "" {
|
|
return nil, errors.New("folder UID cannot be empty")
|
|
}
|
|
|
|
// Get raw tuples from OpenFGA
|
|
tuples, err := s.listFolderParents(ctx, store, req.GetFolder())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list folder parents: %w", err)
|
|
}
|
|
|
|
// Extract parent UIDs from tuples (business logic now server-side)
|
|
parentUIDs := make([]string, 0, len(tuples))
|
|
for _, tuple := range tuples {
|
|
// Extract UID from format "folder:UID" or "folder:UID#relation"
|
|
userParts := strings.Split(tuple.Key.User, ":")
|
|
if len(userParts) != 2 {
|
|
return nil, fmt.Errorf("invalid user format: %s, expected format: folder:UID or folder:UID#relation", tuple.Key.User)
|
|
}
|
|
|
|
// Remove any relation part after #
|
|
uidAndRelationParts := strings.Split(userParts[1], "#")
|
|
if len(uidAndRelationParts) == 0 {
|
|
return nil, fmt.Errorf("invalid user format: %s, expected format: folder:UID or folder:UID#relation", tuple.Key.User)
|
|
}
|
|
|
|
parentUIDs = append(parentUIDs, uidAndRelationParts[0])
|
|
}
|
|
|
|
return &authzextv1.QueryResponse{
|
|
Result: &authzextv1.QueryResponse_FolderParents{
|
|
FolderParents: &authzextv1.GetFolderParentsResult{
|
|
ParentUids: parentUIDs,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|