You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
5.8 KiB
167 lines
5.8 KiB
package s3api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/policy_engine"
|
|
)
|
|
|
|
// BucketPolicyEngine wraps the policy_engine to provide bucket policy evaluation
|
|
type BucketPolicyEngine struct {
|
|
engine *policy_engine.PolicyEngine
|
|
}
|
|
|
|
// NewBucketPolicyEngine creates a new bucket policy engine
|
|
func NewBucketPolicyEngine() *BucketPolicyEngine {
|
|
return &BucketPolicyEngine{
|
|
engine: policy_engine.NewPolicyEngine(),
|
|
}
|
|
}
|
|
|
|
// LoadBucketPolicy loads a bucket policy into the engine from the filer entry
|
|
func (bpe *BucketPolicyEngine) LoadBucketPolicy(bucket string, entry *filer_pb.Entry) error {
|
|
if entry == nil || entry.Extended == nil {
|
|
return nil
|
|
}
|
|
|
|
policyJSON, exists := entry.Extended[BUCKET_POLICY_METADATA_KEY]
|
|
if !exists || len(policyJSON) == 0 {
|
|
// No policy for this bucket - remove it if it exists
|
|
bpe.engine.DeleteBucketPolicy(bucket)
|
|
return nil
|
|
}
|
|
|
|
// Set the policy in the engine
|
|
if err := bpe.engine.SetBucketPolicy(bucket, string(policyJSON)); err != nil {
|
|
glog.Errorf("Failed to load bucket policy for %s: %v", bucket, err)
|
|
return err
|
|
}
|
|
|
|
glog.V(3).Infof("Loaded bucket policy for %s into policy engine", bucket)
|
|
return nil
|
|
}
|
|
|
|
// LoadBucketPolicyFromCache loads a bucket policy from a cached BucketConfig
|
|
//
|
|
// This function loads the policy directly into the engine
|
|
func (bpe *BucketPolicyEngine) LoadBucketPolicyFromCache(bucket string, policyDoc *policy_engine.PolicyDocument) error {
|
|
if policyDoc == nil {
|
|
// No policy for this bucket - remove it if it exists
|
|
bpe.engine.DeleteBucketPolicy(bucket)
|
|
return nil
|
|
}
|
|
|
|
// Policy is already in correct format, just load it
|
|
// We need to re-marshal to string because SetBucketPolicy expects JSON string
|
|
policyJSON, err := json.Marshal(policyDoc)
|
|
if err != nil {
|
|
glog.Errorf("Failed to marshal bucket policy for %s: %v", bucket, err)
|
|
return err
|
|
}
|
|
|
|
// Set the policy in the engine
|
|
if err := bpe.engine.SetBucketPolicy(bucket, string(policyJSON)); err != nil {
|
|
glog.Errorf("Failed to load bucket policy for %s: %v", bucket, err)
|
|
return err
|
|
}
|
|
|
|
glog.V(4).Infof("Loaded bucket policy for %s into policy engine from cache", bucket)
|
|
return nil
|
|
}
|
|
|
|
// DeleteBucketPolicy removes a bucket policy from the engine
|
|
func (bpe *BucketPolicyEngine) DeleteBucketPolicy(bucket string) error {
|
|
return bpe.engine.DeleteBucketPolicy(bucket)
|
|
}
|
|
|
|
// HasPolicyForBucket checks if a bucket has a policy configured
|
|
func (bpe *BucketPolicyEngine) HasPolicyForBucket(bucket string) bool {
|
|
return bpe.engine.HasPolicyForBucket(bucket)
|
|
}
|
|
|
|
// EvaluatePolicy evaluates whether an action is allowed by bucket policy
|
|
//
|
|
// Parameters:
|
|
// - bucket: the bucket name
|
|
// - object: the object key (can be empty for bucket-level operations)
|
|
// - action: the action being performed (e.g., "Read", "Write")
|
|
// - principal: the principal ARN or identifier
|
|
// - r: the HTTP request (optional, used for condition evaluation and action resolution)
|
|
// - objectEntry: the object's metadata from entry.Extended (can be nil at auth time,
|
|
// should be passed when available for tag-based conditions like s3:ExistingObjectTag)
|
|
//
|
|
// Returns:
|
|
// - allowed: whether the policy allows the action
|
|
// - evaluated: whether a policy was found and evaluated (false = no policy exists)
|
|
// - error: any error during evaluation
|
|
func (bpe *BucketPolicyEngine) EvaluatePolicy(bucket, object, action, principal string, r *http.Request, claims map[string]interface{}, objectEntry map[string][]byte) (allowed bool, evaluated bool, err error) {
|
|
// Validate required parameters
|
|
if bucket == "" {
|
|
return false, false, fmt.Errorf("bucket cannot be empty")
|
|
}
|
|
if action == "" {
|
|
return false, false, fmt.Errorf("action cannot be empty")
|
|
}
|
|
|
|
// Convert action to S3 action format
|
|
// ResolveS3Action handles nil request internally (falls back to mapBaseActionToS3Format)
|
|
s3Action := ResolveS3Action(r, action, bucket, object)
|
|
|
|
// Build resource ARN
|
|
resource := buildResourceARN(bucket, object)
|
|
|
|
glog.V(4).Infof("EvaluatePolicy: bucket=%s, resource=%s, action=%s, principal=%s",
|
|
bucket, resource, s3Action, principal)
|
|
|
|
// Evaluate using the policy engine
|
|
args := &policy_engine.PolicyEvaluationArgs{
|
|
Action: s3Action,
|
|
Resource: resource,
|
|
Principal: principal,
|
|
ObjectEntry: objectEntry,
|
|
}
|
|
|
|
// glog.V(4).Infof("EvaluatePolicy [Wrapper]: bucket=%s, resource=%s, action=%s, principal=%s",
|
|
// bucket, resource, s3Action, principal)
|
|
|
|
// Extract conditions and claims from request if available
|
|
if r != nil {
|
|
args.Conditions = policy_engine.ExtractConditionValuesFromRequest(r)
|
|
|
|
// Extract principal-related variables (aws:username, etc.) from principal ARN
|
|
principalVars := policy_engine.ExtractPrincipalVariables(principal)
|
|
for k, v := range principalVars {
|
|
args.Conditions[k] = v
|
|
}
|
|
|
|
// Extract JWT claims if authenticated via JWT or STS
|
|
if claims != nil {
|
|
args.Claims = claims
|
|
} else {
|
|
// If claims were not provided directly, try to get them from context Identity?
|
|
// But the caller is responsible for passing them.
|
|
// Falling back to empty claims if not provided.
|
|
}
|
|
}
|
|
|
|
result := bpe.engine.EvaluatePolicy(bucket, args)
|
|
|
|
switch result {
|
|
case policy_engine.PolicyResultAllow:
|
|
// glog.V(4).Infof("EvaluatePolicy [Wrapper]: ALLOW - bucket=%s, action=%s, principal=%s", bucket, s3Action, principal)
|
|
return true, true, nil
|
|
case policy_engine.PolicyResultDeny:
|
|
// glog.V(4).Infof("EvaluatePolicy [Wrapper]: DENY - bucket=%s, action=%s, principal=%s", bucket, s3Action, principal)
|
|
return false, true, nil
|
|
case policy_engine.PolicyResultIndeterminate:
|
|
// No policy exists for this bucket
|
|
// glog.V(4).Infof("EvaluatePolicy [Wrapper]: INDETERMINATE (no policy) - bucket=%s", bucket)
|
|
return false, false, nil
|
|
default:
|
|
return false, false, fmt.Errorf("unknown policy result: %v", result)
|
|
}
|
|
}
|