diff options
| author | chrislu <chris.lu@gmail.com> | 2025-07-18 01:27:46 -0700 |
|---|---|---|
| committer | chrislu <chris.lu@gmail.com> | 2025-07-18 01:27:46 -0700 |
| commit | 0d4eeb67be4eee895f28d3967365984e52c2ad27 (patch) | |
| tree | 71d40a5a53f53c2ef26742e136f7821eda60cefc /weed/s3api/object_lock_utils.go | |
| parent | 85e34df21409a03e701ab2689fec81e53740df0c (diff) | |
| download | seaweedfs-0d4eeb67be4eee895f28d3967365984e52c2ad27.tar.xz seaweedfs-0d4eeb67be4eee895f28d3967365984e52c2ad27.zip | |
admin ui and api handler are consistent now
Diffstat (limited to 'weed/s3api/object_lock_utils.go')
| -rw-r--r-- | weed/s3api/object_lock_utils.go | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/weed/s3api/object_lock_utils.go b/weed/s3api/object_lock_utils.go new file mode 100644 index 000000000..6d2df7854 --- /dev/null +++ b/weed/s3api/object_lock_utils.go @@ -0,0 +1,228 @@ +package s3api + +import ( + "encoding/xml" + "fmt" + "strconv" + "strings" + + "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" +) + +// ObjectLockUtils provides shared utilities for Object Lock configuration +// These functions are used by both Admin UI and S3 API handlers to ensure consistency + +// VersioningUtils provides shared utilities for bucket versioning configuration +// These functions ensure Admin UI and S3 API use the same versioning keys + +// StoreVersioningInExtended stores versioning configuration in entry extended attributes +func StoreVersioningInExtended(entry *filer_pb.Entry, enabled bool) error { + if entry.Extended == nil { + entry.Extended = make(map[string][]byte) + } + + if enabled { + entry.Extended[s3_constants.ExtVersioningKey] = []byte(s3_constants.VersioningEnabled) + } else { + entry.Extended[s3_constants.ExtVersioningKey] = []byte(s3_constants.VersioningSuspended) + } + + return nil +} + +// LoadVersioningFromExtended loads versioning configuration from entry extended attributes +func LoadVersioningFromExtended(entry *filer_pb.Entry) (bool, bool) { + if entry == nil || entry.Extended == nil { + return false, false // not found, default to suspended + } + + // Check for S3 API compatible key + if versioningBytes, exists := entry.Extended[s3_constants.ExtVersioningKey]; exists { + enabled := string(versioningBytes) == s3_constants.VersioningEnabled + return enabled, true + } + + return false, false // not found +} + +// CreateObjectLockConfiguration creates a new ObjectLockConfiguration with the specified parameters +func CreateObjectLockConfiguration(enabled bool, mode string, days int, years int) *ObjectLockConfiguration { + if !enabled { + return nil + } + + config := &ObjectLockConfiguration{ + ObjectLockEnabled: s3_constants.ObjectLockEnabled, + } + + // Add default retention rule if mode and period are specified + if mode != "" && (days > 0 || years > 0) { + config.Rule = &ObjectLockRule{ + DefaultRetention: &DefaultRetention{ + Mode: mode, + Days: days, + Years: years, + }, + } + } + + return config +} + +// ObjectLockConfigurationToXML converts ObjectLockConfiguration to XML bytes +func ObjectLockConfigurationToXML(config *ObjectLockConfiguration) ([]byte, error) { + if config == nil { + return nil, fmt.Errorf("object lock configuration is nil") + } + + return xml.Marshal(config) +} + +// XMLToObjectLockConfiguration parses XML bytes to ObjectLockConfiguration +func XMLToObjectLockConfiguration(xmlData []byte) (*ObjectLockConfiguration, error) { + if len(xmlData) == 0 { + return nil, fmt.Errorf("XML data is empty") + } + + var config ObjectLockConfiguration + if err := xml.Unmarshal(xmlData, &config); err != nil { + return nil, fmt.Errorf("failed to parse Object Lock configuration XML: %w", err) + } + + return &config, nil +} + +// StoreObjectLockConfigurationInExtended stores Object Lock configuration in entry extended attributes +func StoreObjectLockConfigurationInExtended(entry *filer_pb.Entry, config *ObjectLockConfiguration) error { + if entry.Extended == nil { + entry.Extended = make(map[string][]byte) + } + + if config == nil { + // Remove Object Lock configuration + delete(entry.Extended, s3_constants.ExtObjectLockEnabledKey) + delete(entry.Extended, s3_constants.ExtObjectLockConfigKey) + return nil + } + + // Store the enabled flag + entry.Extended[s3_constants.ExtObjectLockEnabledKey] = []byte(config.ObjectLockEnabled) + + // Store the full XML configuration + configXML, err := ObjectLockConfigurationToXML(config) + if err != nil { + return fmt.Errorf("failed to marshal Object Lock configuration: %w", err) + } + entry.Extended[s3_constants.ExtObjectLockConfigKey] = configXML + + return nil +} + +// LoadObjectLockConfigurationFromExtended loads Object Lock configuration from entry extended attributes +func LoadObjectLockConfigurationFromExtended(entry *filer_pb.Entry) (*ObjectLockConfiguration, bool) { + if entry == nil || entry.Extended == nil { + return nil, false + } + + // Check if Object Lock is enabled + enabledBytes, exists := entry.Extended[s3_constants.ExtObjectLockEnabledKey] + if !exists { + return nil, false + } + + enabled := string(enabledBytes) + if enabled != s3_constants.ObjectLockEnabled && enabled != "true" { + return nil, false + } + + // Try to load full XML configuration + if configXML, exists := entry.Extended[s3_constants.ExtObjectLockConfigKey]; exists { + if config, err := XMLToObjectLockConfiguration(configXML); err == nil { + return config, true + } + } + + // Fallback: create minimal configuration for enabled Object Lock + return &ObjectLockConfiguration{ + ObjectLockEnabled: s3_constants.ObjectLockEnabled, + }, true +} + +// ExtractObjectLockInfoFromConfig extracts basic Object Lock information from configuration +// Returns: enabled, mode, duration (for UI display) +func ExtractObjectLockInfoFromConfig(config *ObjectLockConfiguration) (bool, string, int32) { + if config == nil || config.ObjectLockEnabled != s3_constants.ObjectLockEnabled { + return false, "", 0 + } + + if config.Rule == nil || config.Rule.DefaultRetention == nil { + return true, "", 0 + } + + defaultRetention := config.Rule.DefaultRetention + + // Convert years to days for consistent representation + days := defaultRetention.Days + if defaultRetention.Years > 0 { + days += defaultRetention.Years * 365 + } + + return true, defaultRetention.Mode, int32(days) +} + +// CreateObjectLockConfigurationFromParams creates ObjectLockConfiguration from individual parameters +// This is a convenience function for Admin UI usage +func CreateObjectLockConfigurationFromParams(enabled bool, mode string, duration int32) *ObjectLockConfiguration { + if !enabled { + return nil + } + + return CreateObjectLockConfiguration(enabled, mode, int(duration), 0) +} + +// ValidateObjectLockParameters validates Object Lock parameters before creating configuration +func ValidateObjectLockParameters(enabled bool, mode string, duration int32) error { + if !enabled { + return nil + } + + if mode != s3_constants.RetentionModeGovernance && mode != s3_constants.RetentionModeCompliance { + return fmt.Errorf("invalid object lock mode: %s, must be GOVERNANCE or COMPLIANCE", mode) + } + + if duration <= 0 { + return fmt.Errorf("object lock duration must be greater than 0 days") + } + + if duration > MaxRetentionDays { + return fmt.Errorf("object lock duration exceeds maximum allowed days: %d", MaxRetentionDays) + } + + return nil +} + +// SimpleXMLParseObjectLockMode extracts mode from XML string using simple string parsing +// This is used as a fallback when full XML parsing is not needed +func SimpleXMLParseObjectLockMode(xmlStr string) string { + if strings.Contains(xmlStr, "<Mode>GOVERNANCE</Mode>") { + return "GOVERNANCE" + } else if strings.Contains(xmlStr, "<Mode>COMPLIANCE</Mode>") { + return "COMPLIANCE" + } + return "" +} + +// SimpleXMLParseObjectLockDays extracts days from XML string using simple string parsing +// This is used as a fallback when full XML parsing is not needed +func SimpleXMLParseObjectLockDays(xmlStr string) int32 { + if daysStart := strings.Index(xmlStr, "<Days>"); daysStart != -1 { + daysStart += 6 // length of "<Days>" + if daysEnd := strings.Index(xmlStr[daysStart:], "</Days>"); daysEnd != -1 { + if duration, err := strconv.ParseInt(xmlStr[daysStart:daysStart+daysEnd], 10, 32); err == nil { + return int32(duration) + } + } + } + return 0 +} |
