influxdb/query/preauthorizer.go

117 lines
4.2 KiB
Go

package query
import (
"context"
"github.com/pkg/errors"
"github.com/influxdata/flux/ast"
platform "github.com/influxdata/influxdb"
)
// PreAuthorizer provides a method for ensuring that the buckets accessed by a query spec
// are allowed access by the given Authorization. This is a pre-check provided as a way for
// callers to fail early for operations that are not allowed. However, it's still possible
// for authorization to be denied at runtime even if this check passes.
type PreAuthorizer interface {
PreAuthorize(ctx context.Context, ast *ast.Package, auth platform.Authorizer, orgID *platform.ID) error
RequiredPermissions(ctx context.Context, ast *ast.Package, orgID *platform.ID) ([]platform.Permission, error)
}
// NewPreAuthorizer creates a new PreAuthorizer
func NewPreAuthorizer(bucketService platform.BucketService) PreAuthorizer {
return &preAuthorizer{bucketService: bucketService}
}
type preAuthorizer struct {
bucketService platform.BucketService
}
// PreAuthorize finds all the buckets read and written by the given spec, and ensures that execution is allowed
// given the Authorizer. Returns nil on success, and an error with an appropriate message otherwise.
func (a *preAuthorizer) PreAuthorize(ctx context.Context, ast *ast.Package, auth platform.Authorizer, orgID *platform.ID) error {
readBuckets, writeBuckets, err := BucketsAccessed(ast, orgID)
if err != nil {
return errors.Wrap(err, "could not retrieve buckets for query.Spec")
}
for _, readBucketFilter := range readBuckets {
bucket, err := a.bucketService.FindBucket(ctx, readBucketFilter)
if err != nil {
return errors.Wrapf(err, "could not find read bucket with filter: %s", readBucketFilter)
}
if bucket == nil {
return errors.New("bucket service returned nil bucket")
}
reqPerm, err := platform.NewPermissionAtID(bucket.ID, platform.ReadAction, platform.BucketsResourceType, bucket.OrgID)
if err != nil {
return errors.Wrapf(err, "could not create read bucket permission")
}
if !auth.Allowed(*reqPerm) {
return errors.New("no read permission for bucket: \"" + bucket.Name + "\"")
}
}
for _, writeBucketFilter := range writeBuckets {
bucket, err := a.bucketService.FindBucket(ctx, writeBucketFilter)
if err != nil {
return errors.Wrapf(err, "could not find write bucket with filter: %s", writeBucketFilter)
}
reqPerm, err := platform.NewPermissionAtID(bucket.ID, platform.WriteAction, platform.BucketsResourceType, bucket.OrgID)
if err != nil {
return errors.Wrapf(err, "could not create write bucket permission")
}
if !auth.Allowed(*reqPerm) {
return errors.New("no write permission for bucket: \"" + bucket.Name + "\"")
}
}
return nil
}
// RequiredPermissions returns a slice of permissions required for the query contained in spec.
// This method also validates that the buckets exist.
func (a *preAuthorizer) RequiredPermissions(ctx context.Context, ast *ast.Package, orgID *platform.ID) ([]platform.Permission, error) {
readBuckets, writeBuckets, err := BucketsAccessed(ast, orgID)
if err != nil {
return nil, errors.Wrap(err, "could not retrieve buckets for query.Spec")
}
ps := make([]platform.Permission, 0, len(readBuckets)+len(writeBuckets))
for _, readBucketFilter := range readBuckets {
bucket, err := a.bucketService.FindBucket(ctx, readBucketFilter)
if err != nil {
return nil, errors.Wrapf(err, "could not find read bucket with filter: %s", readBucketFilter)
}
if bucket == nil {
return nil, errors.New("bucket service returned nil bucket")
}
reqPerm, err := platform.NewPermissionAtID(bucket.ID, platform.ReadAction, platform.BucketsResourceType, bucket.OrgID)
if err != nil {
return nil, errors.Wrapf(err, "could not create read bucket permission")
}
ps = append(ps, *reqPerm)
}
for _, writeBucketFilter := range writeBuckets {
bucket, err := a.bucketService.FindBucket(ctx, writeBucketFilter)
if err != nil {
return nil, errors.Wrapf(err, "could not find write bucket with filter: %s", writeBucketFilter)
}
reqPerm, err := platform.NewPermissionAtID(bucket.ID, platform.WriteAction, platform.BucketsResourceType, bucket.OrgID)
if err != nil {
return nil, errors.Wrapf(err, "could not create write bucket permission")
}
ps = append(ps, *reqPerm)
}
return ps, nil
}