| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- package s3api
- import (
- "encoding/xml"
- "fmt"
- "strconv"
- "time"
- "github.com/seaweedfs/seaweedfs/weed/glog"
- "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,
- DaysSet: days > 0,
- YearsSet: years > 0,
- },
- }
- }
- 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)
- }
- // 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.ExtObjectLockDefaultModeKey)
- delete(entry.Extended, s3_constants.ExtObjectLockDefaultDaysKey)
- delete(entry.Extended, s3_constants.ExtObjectLockDefaultYearsKey)
- return nil
- }
- // Store the enabled flag
- entry.Extended[s3_constants.ExtObjectLockEnabledKey] = []byte(config.ObjectLockEnabled)
- // Store default retention configuration if present
- if config.Rule != nil && config.Rule.DefaultRetention != nil {
- defaultRetention := config.Rule.DefaultRetention
- // Store mode
- if defaultRetention.Mode != "" {
- entry.Extended[s3_constants.ExtObjectLockDefaultModeKey] = []byte(defaultRetention.Mode)
- }
- // Store days
- if defaultRetention.DaysSet && defaultRetention.Days > 0 {
- entry.Extended[s3_constants.ExtObjectLockDefaultDaysKey] = []byte(strconv.Itoa(defaultRetention.Days))
- }
- // Store years
- if defaultRetention.YearsSet && defaultRetention.Years > 0 {
- entry.Extended[s3_constants.ExtObjectLockDefaultYearsKey] = []byte(strconv.Itoa(defaultRetention.Years))
- }
- } else {
- // Remove default retention if not present
- delete(entry.Extended, s3_constants.ExtObjectLockDefaultModeKey)
- delete(entry.Extended, s3_constants.ExtObjectLockDefaultDaysKey)
- delete(entry.Extended, s3_constants.ExtObjectLockDefaultYearsKey)
- }
- 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
- }
- // Create basic configuration
- config := &ObjectLockConfiguration{
- ObjectLockEnabled: s3_constants.ObjectLockEnabled,
- }
- // Load default retention configuration if present
- if modeBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultModeKey]; exists {
- mode := string(modeBytes)
- // Parse days and years
- var days, years int
- if daysBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultDaysKey]; exists {
- if parsed, err := strconv.Atoi(string(daysBytes)); err == nil {
- days = parsed
- }
- }
- if yearsBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultYearsKey]; exists {
- if parsed, err := strconv.Atoi(string(yearsBytes)); err == nil {
- years = parsed
- }
- }
- // Create rule if we have a mode and at least days or years
- if mode != "" && (days > 0 || years > 0) {
- config.Rule = &ObjectLockRule{
- DefaultRetention: &DefaultRetention{
- Mode: mode,
- Days: days,
- Years: years,
- DaysSet: days > 0,
- YearsSet: years > 0,
- },
- }
- }
- }
- return config, 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 := 0
- if defaultRetention.DaysSet {
- days = defaultRetention.Days
- }
- if defaultRetention.YearsSet && 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 ErrInvalidObjectLockMode
- }
- if duration <= 0 {
- return ErrInvalidObjectLockDuration
- }
- if duration > MaxRetentionDays {
- return ErrObjectLockDurationExceeded
- }
- return nil
- }
- // ====================================================================
- // OBJECT LOCK VALIDATION FUNCTIONS
- // ====================================================================
- // These validation functions provide comprehensive validation for
- // all Object Lock related configurations and requests.
- // ValidateRetention validates retention configuration for object-level retention
- func ValidateRetention(retention *ObjectRetention) error {
- // Check if mode is specified
- if retention.Mode == "" {
- return ErrRetentionMissingMode
- }
- // Check if retain until date is specified
- if retention.RetainUntilDate == nil {
- return ErrRetentionMissingRetainUntilDate
- }
- // Check if mode is valid
- if retention.Mode != s3_constants.RetentionModeGovernance && retention.Mode != s3_constants.RetentionModeCompliance {
- return ErrInvalidRetentionModeValue
- }
- // Check if retain until date is in the future
- if retention.RetainUntilDate.Before(time.Now()) {
- return ErrRetentionDateMustBeFuture
- }
- return nil
- }
- // ValidateLegalHold validates legal hold configuration
- func ValidateLegalHold(legalHold *ObjectLegalHold) error {
- // Check if status is valid
- if legalHold.Status != s3_constants.LegalHoldOn && legalHold.Status != s3_constants.LegalHoldOff {
- return ErrInvalidLegalHoldStatus
- }
- return nil
- }
- // ValidateObjectLockConfiguration validates object lock configuration at bucket level
- func ValidateObjectLockConfiguration(config *ObjectLockConfiguration) error {
- // ObjectLockEnabled is required for bucket-level configuration
- if config.ObjectLockEnabled == "" {
- return ErrObjectLockConfigurationMissingEnabled
- }
- // Validate ObjectLockEnabled value
- if config.ObjectLockEnabled != s3_constants.ObjectLockEnabled {
- // ObjectLockEnabled can only be 'Enabled', any other value (including 'Disabled') is malformed XML
- return ErrInvalidObjectLockEnabledValue
- }
- // Validate Rule if present
- if config.Rule != nil {
- if config.Rule.DefaultRetention == nil {
- return ErrRuleMissingDefaultRetention
- }
- return validateDefaultRetention(config.Rule.DefaultRetention)
- }
- return nil
- }
- // validateDefaultRetention validates default retention configuration for bucket-level settings
- func validateDefaultRetention(retention *DefaultRetention) error {
- glog.V(2).Infof("validateDefaultRetention: Mode=%s, Days=%d (set=%v), Years=%d (set=%v)",
- retention.Mode, retention.Days, retention.DaysSet, retention.Years, retention.YearsSet)
- // Mode is required
- if retention.Mode == "" {
- return ErrDefaultRetentionMissingMode
- }
- // Mode must be valid
- if retention.Mode != s3_constants.RetentionModeGovernance && retention.Mode != s3_constants.RetentionModeCompliance {
- return ErrInvalidDefaultRetentionMode
- }
- // Check for invalid Years value (negative values are always invalid)
- if retention.YearsSet && retention.Years < 0 {
- return ErrInvalidRetentionPeriod
- }
- // Check for invalid Days value (negative values are invalid)
- if retention.DaysSet && retention.Days < 0 {
- return ErrInvalidRetentionPeriod
- }
- // Check for invalid Days value (zero is invalid when explicitly provided)
- if retention.DaysSet && retention.Days == 0 {
- return ErrInvalidRetentionPeriod
- }
- // Check for neither Days nor Years being specified
- if !retention.DaysSet && !retention.YearsSet {
- return ErrDefaultRetentionMissingPeriod
- }
- // Check for both Days and Years being specified
- if retention.DaysSet && retention.YearsSet {
- return ErrDefaultRetentionBothDaysAndYears
- }
- // Validate Days if specified
- if retention.DaysSet && retention.Days > 0 {
- if retention.Days > MaxRetentionDays {
- return ErrDefaultRetentionDaysOutOfRange
- }
- }
- // Validate Years if specified
- if retention.YearsSet && retention.Years > 0 {
- if retention.Years > MaxRetentionYears {
- return ErrDefaultRetentionYearsOutOfRange
- }
- }
- return nil
- }
|