2025-04-01 10:38:02 +09:00

127 lines
3.2 KiB
Go

package client
import (
"context"
"go.opentelemetry.io/otel"
"google.golang.org/grpc"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
authlib "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/infra/log"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
)
var _ authlib.AccessClient = (*Client)(nil)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/authz/zanzana/client")
type Client struct {
logger log.Logger
authz authzv1.AuthzServiceClient
authzext authzextv1.AuthzExtentionServiceClient
}
func New(cc grpc.ClientConnInterface) (*Client, error) {
c := &Client{
authz: authzv1.NewAuthzServiceClient(cc),
authzext: authzextv1.NewAuthzExtentionServiceClient(cc),
logger: log.New("zanzana-client"),
}
return c, nil
}
func (c *Client) Check(ctx context.Context, id authlib.AuthInfo, req authlib.CheckRequest) (authlib.CheckResponse, error) {
ctx, span := tracer.Start(ctx, "authlib.zanzana.client.Check")
defer span.End()
res, err := c.authz.Check(ctx, &authzv1.CheckRequest{
Subject: id.GetUID(),
Verb: req.Verb,
Group: req.Group,
Resource: req.Resource,
Namespace: req.Namespace,
Name: req.Name,
Subresource: req.Subresource,
Path: req.Path,
Folder: req.Folder,
})
if err != nil {
return authlib.CheckResponse{}, err
}
return authlib.CheckResponse{Allowed: res.GetAllowed()}, nil
}
func (c *Client) Compile(ctx context.Context, id authlib.AuthInfo, req authlib.ListRequest) (authlib.ItemChecker, error) {
ctx, span := tracer.Start(ctx, "authlib.zanzana.client.Compile")
defer span.End()
res, err := c.authz.List(ctx, &authzv1.ListRequest{
Subject: id.GetUID(),
Group: req.Group,
Verb: utils.VerbList,
Resource: req.Resource,
Namespace: req.Namespace,
})
if err != nil {
return nil, err
}
return newItemChecker(res), nil
}
func newItemChecker(res *authzv1.ListResponse) authlib.ItemChecker {
// if we can see all resource of this type we can just return a function that always return true
if res.GetAll() {
return func(_, _ string) bool { return true }
}
folders := make(map[string]struct{}, len(res.Folders))
for _, f := range res.Folders {
folders[f] = struct{}{}
}
items := make(map[string]struct{}, len(res.Items))
for _, i := range res.Items {
items[i] = struct{}{}
}
return func(name, folder string) bool {
if _, ok := items[name]; ok {
return true
}
if _, ok := folders[folder]; ok {
return true
}
return false
}
}
func (c *Client) Read(ctx context.Context, req *authzextv1.ReadRequest) (*authzextv1.ReadResponse, error) {
ctx, span := tracer.Start(ctx, "authlib.zanzana.client.Read")
defer span.End()
return c.authzext.Read(ctx, req)
}
func (c *Client) Write(ctx context.Context, req *authzextv1.WriteRequest) error {
ctx, span := tracer.Start(ctx, "authlib.zanzana.client.Write")
defer span.End()
_, err := c.authzext.Write(ctx, req)
return err
}
func (c *Client) BatchCheck(ctx context.Context, req *authzextv1.BatchCheckRequest) (*authzextv1.BatchCheckResponse, error) {
ctx, span := tracer.Start(ctx, "authlib.zanzana.client.Check")
defer span.End()
return c.authzext.BatchCheck(ctx, req)
}