object_lock_utils.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. package s3api
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "strconv"
  6. "time"
  7. "github.com/seaweedfs/seaweedfs/weed/glog"
  8. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  9. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  10. )
  11. // ObjectLockUtils provides shared utilities for Object Lock configuration
  12. // These functions are used by both Admin UI and S3 API handlers to ensure consistency
  13. // VersioningUtils provides shared utilities for bucket versioning configuration
  14. // These functions ensure Admin UI and S3 API use the same versioning keys
  15. // StoreVersioningInExtended stores versioning configuration in entry extended attributes
  16. func StoreVersioningInExtended(entry *filer_pb.Entry, enabled bool) error {
  17. if entry.Extended == nil {
  18. entry.Extended = make(map[string][]byte)
  19. }
  20. if enabled {
  21. entry.Extended[s3_constants.ExtVersioningKey] = []byte(s3_constants.VersioningEnabled)
  22. } else {
  23. entry.Extended[s3_constants.ExtVersioningKey] = []byte(s3_constants.VersioningSuspended)
  24. }
  25. return nil
  26. }
  27. // LoadVersioningFromExtended loads versioning configuration from entry extended attributes
  28. func LoadVersioningFromExtended(entry *filer_pb.Entry) (bool, bool) {
  29. if entry == nil || entry.Extended == nil {
  30. return false, false // not found, default to suspended
  31. }
  32. // Check for S3 API compatible key
  33. if versioningBytes, exists := entry.Extended[s3_constants.ExtVersioningKey]; exists {
  34. enabled := string(versioningBytes) == s3_constants.VersioningEnabled
  35. return enabled, true
  36. }
  37. return false, false // not found
  38. }
  39. // CreateObjectLockConfiguration creates a new ObjectLockConfiguration with the specified parameters
  40. func CreateObjectLockConfiguration(enabled bool, mode string, days int, years int) *ObjectLockConfiguration {
  41. if !enabled {
  42. return nil
  43. }
  44. config := &ObjectLockConfiguration{
  45. ObjectLockEnabled: s3_constants.ObjectLockEnabled,
  46. }
  47. // Add default retention rule if mode and period are specified
  48. if mode != "" && (days > 0 || years > 0) {
  49. config.Rule = &ObjectLockRule{
  50. DefaultRetention: &DefaultRetention{
  51. Mode: mode,
  52. Days: days,
  53. Years: years,
  54. DaysSet: days > 0,
  55. YearsSet: years > 0,
  56. },
  57. }
  58. }
  59. return config
  60. }
  61. // ObjectLockConfigurationToXML converts ObjectLockConfiguration to XML bytes
  62. func ObjectLockConfigurationToXML(config *ObjectLockConfiguration) ([]byte, error) {
  63. if config == nil {
  64. return nil, fmt.Errorf("object lock configuration is nil")
  65. }
  66. return xml.Marshal(config)
  67. }
  68. // StoreObjectLockConfigurationInExtended stores Object Lock configuration in entry extended attributes
  69. func StoreObjectLockConfigurationInExtended(entry *filer_pb.Entry, config *ObjectLockConfiguration) error {
  70. if entry.Extended == nil {
  71. entry.Extended = make(map[string][]byte)
  72. }
  73. if config == nil {
  74. // Remove Object Lock configuration
  75. delete(entry.Extended, s3_constants.ExtObjectLockEnabledKey)
  76. delete(entry.Extended, s3_constants.ExtObjectLockDefaultModeKey)
  77. delete(entry.Extended, s3_constants.ExtObjectLockDefaultDaysKey)
  78. delete(entry.Extended, s3_constants.ExtObjectLockDefaultYearsKey)
  79. return nil
  80. }
  81. // Store the enabled flag
  82. entry.Extended[s3_constants.ExtObjectLockEnabledKey] = []byte(config.ObjectLockEnabled)
  83. // Store default retention configuration if present
  84. if config.Rule != nil && config.Rule.DefaultRetention != nil {
  85. defaultRetention := config.Rule.DefaultRetention
  86. // Store mode
  87. if defaultRetention.Mode != "" {
  88. entry.Extended[s3_constants.ExtObjectLockDefaultModeKey] = []byte(defaultRetention.Mode)
  89. }
  90. // Store days
  91. if defaultRetention.DaysSet && defaultRetention.Days > 0 {
  92. entry.Extended[s3_constants.ExtObjectLockDefaultDaysKey] = []byte(strconv.Itoa(defaultRetention.Days))
  93. }
  94. // Store years
  95. if defaultRetention.YearsSet && defaultRetention.Years > 0 {
  96. entry.Extended[s3_constants.ExtObjectLockDefaultYearsKey] = []byte(strconv.Itoa(defaultRetention.Years))
  97. }
  98. } else {
  99. // Remove default retention if not present
  100. delete(entry.Extended, s3_constants.ExtObjectLockDefaultModeKey)
  101. delete(entry.Extended, s3_constants.ExtObjectLockDefaultDaysKey)
  102. delete(entry.Extended, s3_constants.ExtObjectLockDefaultYearsKey)
  103. }
  104. return nil
  105. }
  106. // LoadObjectLockConfigurationFromExtended loads Object Lock configuration from entry extended attributes
  107. func LoadObjectLockConfigurationFromExtended(entry *filer_pb.Entry) (*ObjectLockConfiguration, bool) {
  108. if entry == nil || entry.Extended == nil {
  109. return nil, false
  110. }
  111. // Check if Object Lock is enabled
  112. enabledBytes, exists := entry.Extended[s3_constants.ExtObjectLockEnabledKey]
  113. if !exists {
  114. return nil, false
  115. }
  116. enabled := string(enabledBytes)
  117. if enabled != s3_constants.ObjectLockEnabled && enabled != "true" {
  118. return nil, false
  119. }
  120. // Create basic configuration
  121. config := &ObjectLockConfiguration{
  122. ObjectLockEnabled: s3_constants.ObjectLockEnabled,
  123. }
  124. // Load default retention configuration if present
  125. if modeBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultModeKey]; exists {
  126. mode := string(modeBytes)
  127. // Parse days and years
  128. var days, years int
  129. if daysBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultDaysKey]; exists {
  130. if parsed, err := strconv.Atoi(string(daysBytes)); err == nil {
  131. days = parsed
  132. }
  133. }
  134. if yearsBytes, exists := entry.Extended[s3_constants.ExtObjectLockDefaultYearsKey]; exists {
  135. if parsed, err := strconv.Atoi(string(yearsBytes)); err == nil {
  136. years = parsed
  137. }
  138. }
  139. // Create rule if we have a mode and at least days or years
  140. if mode != "" && (days > 0 || years > 0) {
  141. config.Rule = &ObjectLockRule{
  142. DefaultRetention: &DefaultRetention{
  143. Mode: mode,
  144. Days: days,
  145. Years: years,
  146. DaysSet: days > 0,
  147. YearsSet: years > 0,
  148. },
  149. }
  150. }
  151. }
  152. return config, true
  153. }
  154. // ExtractObjectLockInfoFromConfig extracts basic Object Lock information from configuration
  155. // Returns: enabled, mode, duration (for UI display)
  156. func ExtractObjectLockInfoFromConfig(config *ObjectLockConfiguration) (bool, string, int32) {
  157. if config == nil || config.ObjectLockEnabled != s3_constants.ObjectLockEnabled {
  158. return false, "", 0
  159. }
  160. if config.Rule == nil || config.Rule.DefaultRetention == nil {
  161. return true, "", 0
  162. }
  163. defaultRetention := config.Rule.DefaultRetention
  164. // Convert years to days for consistent representation
  165. days := 0
  166. if defaultRetention.DaysSet {
  167. days = defaultRetention.Days
  168. }
  169. if defaultRetention.YearsSet && defaultRetention.Years > 0 {
  170. days += defaultRetention.Years * 365
  171. }
  172. return true, defaultRetention.Mode, int32(days)
  173. }
  174. // CreateObjectLockConfigurationFromParams creates ObjectLockConfiguration from individual parameters
  175. // This is a convenience function for Admin UI usage
  176. func CreateObjectLockConfigurationFromParams(enabled bool, mode string, duration int32) *ObjectLockConfiguration {
  177. if !enabled {
  178. return nil
  179. }
  180. return CreateObjectLockConfiguration(enabled, mode, int(duration), 0)
  181. }
  182. // ValidateObjectLockParameters validates Object Lock parameters before creating configuration
  183. func ValidateObjectLockParameters(enabled bool, mode string, duration int32) error {
  184. if !enabled {
  185. return nil
  186. }
  187. if mode != s3_constants.RetentionModeGovernance && mode != s3_constants.RetentionModeCompliance {
  188. return ErrInvalidObjectLockMode
  189. }
  190. if duration <= 0 {
  191. return ErrInvalidObjectLockDuration
  192. }
  193. if duration > MaxRetentionDays {
  194. return ErrObjectLockDurationExceeded
  195. }
  196. return nil
  197. }
  198. // ====================================================================
  199. // OBJECT LOCK VALIDATION FUNCTIONS
  200. // ====================================================================
  201. // These validation functions provide comprehensive validation for
  202. // all Object Lock related configurations and requests.
  203. // ValidateRetention validates retention configuration for object-level retention
  204. func ValidateRetention(retention *ObjectRetention) error {
  205. // Check if mode is specified
  206. if retention.Mode == "" {
  207. return ErrRetentionMissingMode
  208. }
  209. // Check if retain until date is specified
  210. if retention.RetainUntilDate == nil {
  211. return ErrRetentionMissingRetainUntilDate
  212. }
  213. // Check if mode is valid
  214. if retention.Mode != s3_constants.RetentionModeGovernance && retention.Mode != s3_constants.RetentionModeCompliance {
  215. return ErrInvalidRetentionModeValue
  216. }
  217. // Check if retain until date is in the future
  218. if retention.RetainUntilDate.Before(time.Now()) {
  219. return ErrRetentionDateMustBeFuture
  220. }
  221. return nil
  222. }
  223. // ValidateLegalHold validates legal hold configuration
  224. func ValidateLegalHold(legalHold *ObjectLegalHold) error {
  225. // Check if status is valid
  226. if legalHold.Status != s3_constants.LegalHoldOn && legalHold.Status != s3_constants.LegalHoldOff {
  227. return ErrInvalidLegalHoldStatus
  228. }
  229. return nil
  230. }
  231. // ValidateObjectLockConfiguration validates object lock configuration at bucket level
  232. func ValidateObjectLockConfiguration(config *ObjectLockConfiguration) error {
  233. // ObjectLockEnabled is required for bucket-level configuration
  234. if config.ObjectLockEnabled == "" {
  235. return ErrObjectLockConfigurationMissingEnabled
  236. }
  237. // Validate ObjectLockEnabled value
  238. if config.ObjectLockEnabled != s3_constants.ObjectLockEnabled {
  239. // ObjectLockEnabled can only be 'Enabled', any other value (including 'Disabled') is malformed XML
  240. return ErrInvalidObjectLockEnabledValue
  241. }
  242. // Validate Rule if present
  243. if config.Rule != nil {
  244. if config.Rule.DefaultRetention == nil {
  245. return ErrRuleMissingDefaultRetention
  246. }
  247. return validateDefaultRetention(config.Rule.DefaultRetention)
  248. }
  249. return nil
  250. }
  251. // validateDefaultRetention validates default retention configuration for bucket-level settings
  252. func validateDefaultRetention(retention *DefaultRetention) error {
  253. glog.V(2).Infof("validateDefaultRetention: Mode=%s, Days=%d (set=%v), Years=%d (set=%v)",
  254. retention.Mode, retention.Days, retention.DaysSet, retention.Years, retention.YearsSet)
  255. // Mode is required
  256. if retention.Mode == "" {
  257. return ErrDefaultRetentionMissingMode
  258. }
  259. // Mode must be valid
  260. if retention.Mode != s3_constants.RetentionModeGovernance && retention.Mode != s3_constants.RetentionModeCompliance {
  261. return ErrInvalidDefaultRetentionMode
  262. }
  263. // Check for invalid Years value (negative values are always invalid)
  264. if retention.YearsSet && retention.Years < 0 {
  265. return ErrInvalidRetentionPeriod
  266. }
  267. // Check for invalid Days value (negative values are invalid)
  268. if retention.DaysSet && retention.Days < 0 {
  269. return ErrInvalidRetentionPeriod
  270. }
  271. // Check for invalid Days value (zero is invalid when explicitly provided)
  272. if retention.DaysSet && retention.Days == 0 {
  273. return ErrInvalidRetentionPeriod
  274. }
  275. // Check for neither Days nor Years being specified
  276. if !retention.DaysSet && !retention.YearsSet {
  277. return ErrDefaultRetentionMissingPeriod
  278. }
  279. // Check for both Days and Years being specified
  280. if retention.DaysSet && retention.YearsSet {
  281. return ErrDefaultRetentionBothDaysAndYears
  282. }
  283. // Validate Days if specified
  284. if retention.DaysSet && retention.Days > 0 {
  285. if retention.Days > MaxRetentionDays {
  286. return ErrDefaultRetentionDaysOutOfRange
  287. }
  288. }
  289. // Validate Years if specified
  290. if retention.YearsSet && retention.Years > 0 {
  291. if retention.Years > MaxRetentionYears {
  292. return ErrDefaultRetentionYearsOutOfRange
  293. }
  294. }
  295. return nil
  296. }