garage-webui/backend/schema/s3_permissions.go
2025-09-25 18:07:53 -03:00

319 lines
9.1 KiB
Go

package schema
import (
"encoding/json"
"time"
)
// S3Action represents AWS S3 API actions
type S3Action string
const (
// Object-level permissions
S3ActionGetObject S3Action = "s3:GetObject"
S3ActionPutObject S3Action = "s3:PutObject"
S3ActionDeleteObject S3Action = "s3:DeleteObject"
S3ActionGetObjectAcl S3Action = "s3:GetObjectAcl"
S3ActionPutObjectAcl S3Action = "s3:PutObjectAcl"
S3ActionGetObjectVersion S3Action = "s3:GetObjectVersion"
S3ActionDeleteObjectVersion S3Action = "s3:DeleteObjectVersion"
// Object locking permissions
S3ActionPutObjectLegalHold S3Action = "s3:PutObjectLegalHold"
S3ActionGetObjectLegalHold S3Action = "s3:GetObjectLegalHold"
S3ActionPutObjectRetention S3Action = "s3:PutObjectRetention"
S3ActionGetObjectRetention S3Action = "s3:GetObjectRetention"
S3ActionBypassGovernanceRetention S3Action = "s3:BypassGovernanceRetention"
// Multipart upload permissions
S3ActionAbortMultipartUpload S3Action = "s3:AbortMultipartUpload"
S3ActionListMultipartUploadParts S3Action = "s3:ListMultipartUploadParts"
// Bucket-level permissions
S3ActionListBucket S3Action = "s3:ListBucket"
S3ActionListBucketVersions S3Action = "s3:ListBucketVersions"
S3ActionGetBucketLocation S3Action = "s3:GetBucketLocation"
S3ActionGetBucketAcl S3Action = "s3:GetBucketAcl"
S3ActionPutBucketAcl S3Action = "s3:PutBucketAcl"
S3ActionGetBucketPolicy S3Action = "s3:GetBucketPolicy"
S3ActionPutBucketPolicy S3Action = "s3:PutBucketPolicy"
S3ActionDeleteBucketPolicy S3Action = "s3:DeleteBucketPolicy"
S3ActionGetBucketVersioning S3Action = "s3:GetBucketVersioning"
S3ActionPutBucketVersioning S3Action = "s3:PutBucketVersioning"
S3ActionGetBucketObjectLockConfiguration S3Action = "s3:GetBucketObjectLockConfiguration"
S3ActionPutBucketObjectLockConfiguration S3Action = "s3:PutBucketObjectLockConfiguration"
// Bucket management permissions
S3ActionCreateBucket S3Action = "s3:CreateBucket"
S3ActionDeleteBucket S3Action = "s3:DeleteBucket"
// List permissions
S3ActionListAllMyBuckets S3Action = "s3:ListAllMyBuckets"
S3ActionListBucketMultipartUploads S3Action = "s3:ListBucketMultipartUploads"
)
// S3Effect represents permission effect (Allow/Deny)
type S3Effect string
const (
S3EffectAllow S3Effect = "Allow"
S3EffectDeny S3Effect = "Deny"
)
// S3Statement represents a policy statement
type S3Statement struct {
ID string `json:"id,omitempty"`
Effect S3Effect `json:"effect"`
Actions []S3Action `json:"actions"`
Resources []string `json:"resources"`
Condition *S3Condition `json:"condition,omitempty"`
}
// S3Condition represents policy conditions
type S3Condition struct {
StringEquals map[string]interface{} `json:"StringEquals,omitempty"`
StringNotEquals map[string]interface{} `json:"StringNotEquals,omitempty"`
StringLike map[string]interface{} `json:"StringLike,omitempty"`
StringNotLike map[string]interface{} `json:"StringNotLike,omitempty"`
IpAddress map[string]interface{} `json:"IpAddress,omitempty"`
NotIpAddress map[string]interface{} `json:"NotIpAddress,omitempty"`
DateGreaterThan map[string]interface{} `json:"DateGreaterThan,omitempty"`
DateLessThan map[string]interface{} `json:"DateLessThan,omitempty"`
}
// S3Policy represents a complete S3 IAM policy
type S3Policy struct {
Version string `json:"version"`
ID string `json:"id,omitempty"`
Statements []S3Statement `json:"statements"`
}
// S3KeyPermissions represents enhanced key permissions with S3 actions
type S3KeyPermissions struct {
AccessKeyID string `json:"access_key_id"`
Name string `json:"name"`
Policy S3Policy `json:"policy"`
LegacyPermissions *Permissions `json:"legacy_permissions,omitempty"` // For backward compatibility
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ObjectLockConfiguration represents bucket object lock settings
type ObjectLockConfiguration struct {
ObjectLockEnabled bool `json:"object_lock_enabled"`
Rule *ObjectLockRule `json:"rule,omitempty"`
}
// ObjectLockRule represents object lock rule
type ObjectLockRule struct {
DefaultRetention *DefaultRetention `json:"default_retention,omitempty"`
}
// DefaultRetention represents default retention settings
type DefaultRetention struct {
Mode ObjectLockRetentionMode `json:"mode"`
Days *int `json:"days,omitempty"`
Years *int `json:"years,omitempty"`
}
// ObjectLockRetentionMode represents retention mode
type ObjectLockRetentionMode string
const (
ObjectLockRetentionCompliance ObjectLockRetentionMode = "COMPLIANCE"
ObjectLockRetentionGovernance ObjectLockRetentionMode = "GOVERNANCE"
)
// ObjectRetention represents object-level retention
type ObjectRetention struct {
Mode ObjectLockRetentionMode `json:"mode"`
RetainUntilDate time.Time `json:"retain_until_date"`
}
// ObjectLegalHold represents object legal hold
type ObjectLegalHold struct {
Status ObjectLegalHoldStatus `json:"status"`
}
// ObjectLegalHoldStatus represents legal hold status
type ObjectLegalHoldStatus string
const (
ObjectLegalHoldOn ObjectLegalHoldStatus = "ON"
ObjectLegalHoldOff ObjectLegalHoldStatus = "OFF"
)
// HasAction checks if the policy allows a specific action on a resource
func (p *S3Policy) HasAction(action S3Action, resource string) bool {
for _, statement := range p.Statements {
// Check if action matches
actionMatches := false
for _, stmtAction := range statement.Actions {
if stmtAction == action || stmtAction == "s3:*" {
actionMatches = true
break
}
}
if !actionMatches {
continue
}
// Check if resource matches
resourceMatches := false
for _, stmtResource := range statement.Resources {
if matchResource(stmtResource, resource) {
resourceMatches = true
break
}
}
if !resourceMatches {
continue
}
// If we have a match, check effect
if statement.Effect == S3EffectAllow {
return true
}
}
return false
}
// matchResource checks if a resource pattern matches a specific resource
func matchResource(pattern, resource string) bool {
if pattern == "*" {
return true
}
if pattern == resource {
return true
}
// Simple wildcard matching for now
// In a full implementation, you'd want proper ARN matching
return false
}
// GetPresetPolicies returns common preset policies
func GetPresetPolicies() map[string]S3Policy {
return map[string]S3Policy{
"ReadOnly": {
Version: "2012-10-17",
ID: "ReadOnlyPolicy",
Statements: []S3Statement{
{
Effect: S3EffectAllow,
Actions: []S3Action{
S3ActionGetObject,
S3ActionListBucket,
S3ActionGetBucketLocation,
},
Resources: []string{"*"},
},
},
},
"ReadWrite": {
Version: "2012-10-17",
ID: "ReadWritePolicy",
Statements: []S3Statement{
{
Effect: S3EffectAllow,
Actions: []S3Action{
S3ActionGetObject,
S3ActionPutObject,
S3ActionDeleteObject,
S3ActionListBucket,
S3ActionGetBucketLocation,
S3ActionAbortMultipartUpload,
S3ActionListMultipartUploadParts,
},
Resources: []string{"*"},
},
},
},
"FullAccess": {
Version: "2012-10-17",
ID: "FullAccessPolicy",
Statements: []S3Statement{
{
Effect: S3EffectAllow,
Actions: []S3Action{"s3:*"},
Resources: []string{"*"},
},
},
},
"ObjectLockManager": {
Version: "2012-10-17",
ID: "ObjectLockManagerPolicy",
Statements: []S3Statement{
{
Effect: S3EffectAllow,
Actions: []S3Action{
S3ActionGetObject,
S3ActionPutObject,
S3ActionGetObjectRetention,
S3ActionPutObjectRetention,
S3ActionGetObjectLegalHold,
S3ActionPutObjectLegalHold,
S3ActionListBucket,
S3ActionGetBucketObjectLockConfiguration,
S3ActionPutBucketObjectLockConfiguration,
},
Resources: []string{"*"},
},
},
},
}
}
// ConvertLegacyPermissions converts old permission format to new S3 policy format
func ConvertLegacyPermissions(legacy Permissions) S3Policy {
var actions []S3Action
if legacy.Read {
actions = append(actions,
S3ActionGetObject,
S3ActionListBucket,
S3ActionGetBucketLocation,
)
}
if legacy.Write {
actions = append(actions,
S3ActionPutObject,
S3ActionDeleteObject,
S3ActionAbortMultipartUpload,
S3ActionListMultipartUploadParts,
)
}
if legacy.Owner {
actions = append(actions,
S3ActionGetBucketAcl,
S3ActionPutBucketAcl,
S3ActionGetBucketPolicy,
S3ActionPutBucketPolicy,
S3ActionDeleteBucketPolicy,
)
}
return S3Policy{
Version: "2012-10-17",
ID: "ConvertedLegacyPolicy",
Statements: []S3Statement{
{
Effect: S3EffectAllow,
Actions: actions,
Resources: []string{"*"},
},
},
}
}
// ToJSON converts policy to JSON string
func (p *S3Policy) ToJSON() (string, error) {
data, err := json.MarshalIndent(p, "", " ")
return string(data), err
}
// FromJSON creates policy from JSON string
func (p *S3Policy) FromJSON(data string) error {
return json.Unmarshal([]byte(data), p)
}