]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw: add object lock feature.
authorzhang Shaowen <zhangshaowen@cmss.chinamobile.com>
Mon, 11 Feb 2019 07:34:46 +0000 (15:34 +0800)
committerzhang Shaowen <zhangshaowen@cmss.chinamobile.com>
Wed, 12 Jun 2019 03:19:04 +0000 (11:19 +0800)
Signed-off-by: zhang Shaowen <zhangshaowen@cmss.chinamobile.com>
15 files changed:
src/rgw/CMakeLists.txt
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_iam_policy.cc
src/rgw/rgw_iam_policy.h
src/rgw/rgw_object_lock.cc [new file with mode: 0644]
src/rgw/rgw_object_lock.h [new file with mode: 0644]
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rados.cc
src/rgw/rgw_rest.cc
src/rgw/rgw_rest.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

index 10aee8d2a0502f7dd909efcf9adacf8adda04104..3a5db470d8c78953f4d08172b6fd0d5ee97251c7 100644 (file)
@@ -118,7 +118,8 @@ set(librgw_common_srcs
   rgw_sts.cc
   rgw_rest_sts.cc
   rgw_perf_counters.cc
-  rgw_rest_iam.cc)
+  rgw_rest_iam.cc
+  rgw_object_lock.cc)
 
 if(WITH_RADOSGW_AMQP_ENDPOINT)
   list(APPEND librgw_common_srcs rgw_amqp.cc)
index c87060a8e0c4005acd4cc94119f3f6cac060171b..0780101fb4aeab0652d6aab2c900ac634990224e 100644 (file)
@@ -47,7 +47,8 @@ static const auto signed_subresources = {
   "versionId",
   "versioning",
   "versions",
-  "website"
+  "website",
+  "object-lock"
 };
 
 /*
index 356e8e5b0fa200d729ca922d17bfa933b4160f9b..6245aa9ec44deacb3141f9eed87bb93183c58797 100644 (file)
@@ -97,6 +97,7 @@ rgw_http_errors rgw_http_s3_errors({
     { ERR_NO_SUCH_SUBUSER, {404, "NoSuchSubUser"}},
     { ERR_NO_SUCH_ENTITY, {404, "NoSuchEntity"}},
     { ERR_NO_SUCH_CORS_CONFIGURATION, {404, "NoSuchCORSConfiguration"}},
+    { ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION, {404, "NoSuchObjectLockConfiguration"}},
     { ERR_METHOD_NOT_ALLOWED, {405, "MethodNotAllowed" }},
     { ETIMEDOUT, {408, "RequestTimeout" }},
     { EEXIST, {409, "BucketAlreadyExists" }},
index fcb78de36c897facb6e47e00705cc8feb913026a..9c07f5e9e4bb1a5585468ba636ea46ca38031023 100644 (file)
@@ -30,6 +30,7 @@
 #include "rgw_string.h"
 #include "common/async/yield_context.h"
 #include "rgw_website.h"
+#include "rgw_object_lock.h"
 #include "cls/version/cls_version_types.h"
 #include "cls/user/cls_user_types.h"
 #include "cls/rgw/cls_rgw_types.h"
@@ -80,6 +81,14 @@ using ceph::crypto::MD5;
 #define RGW_ATTR_X_ROBOTS_TAG  RGW_ATTR_PREFIX "x-robots-tag"
 #define RGW_ATTR_STORAGE_CLASS  RGW_ATTR_PREFIX "storage_class"
 
+/* S3 Object Lock*/
+#define RGW_ATTR_OBJECT_LOCK        RGW_ATTR_PREFIX "object-lock"
+#define RGW_ATTR_OBJECT_RETENTION   RGW_ATTR_PREFIX "object-retention"
+#define RGW_ATTR_OBJECT_LEGAL_HOLD  RGW_ATTR_PREFIX "object-legal-hold"
+#define RGW_ATTR_OBJECT_LOCK_MODE   RGW_ATTR_PREFIX "object-lock-mode"
+#define RGW_ATTR_OBJECT_LOCK_UNTIL_DATE    RGW_ATTR_PREFIX "object-lock-until-date"
+
+
 #define RGW_ATTR_PG_VER        RGW_ATTR_PREFIX "pg_ver"
 #define RGW_ATTR_SOURCE_ZONE    RGW_ATTR_PREFIX "source_zone"
 #define RGW_ATTR_TAGS           RGW_ATTR_PREFIX RGW_AMZ_PREFIX "tagging"
@@ -209,6 +218,8 @@ using ceph::crypto::MD5;
 #define ERR_NO_SUCH_SUBUSER      2043
 #define ERR_MFA_REQUIRED         2044
 #define ERR_NO_SUCH_CORS_CONFIGURATION 2045
+#define ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION  2046
+#define ERR_INVALID_BUCKET_STATE 2047
 #define ERR_USER_SUSPENDED       2100
 #define ERR_INTERNAL_ERROR       2200
 #define ERR_NOT_IMPLEMENTED      2201
@@ -492,6 +503,12 @@ enum RGWOpType {
   RGW_OP_GET_USER_POLICY,
   RGW_OP_LIST_USER_POLICIES,
   RGW_OP_DELETE_USER_POLICY,
+  RGW_OP_PUT_BUCKET_OBJ_LOCK,
+  RGW_OP_GET_BUCKET_OBJ_LOCK,
+  RGW_OP_PUT_OBJ_RETENTION,
+  RGW_OP_GET_OBJ_RETENTION,
+  RGW_OP_PUT_OBJ_LEGAL_HOLD,
+  RGW_OP_GET_OBJ_LEGAL_HOLD,
   /* rgw specific */
   RGW_OP_ADMIN_SET_METADATA,
   RGW_OP_GET_OBJ_LAYOUT,
@@ -1338,6 +1355,7 @@ enum RGWBucketFlags {
   BUCKET_VERSIONS_SUSPENDED = 0x4,
   BUCKET_DATASYNC_DISABLED = 0X8,
   BUCKET_MFA_ENABLED = 0X10,
+  BUCKET_OBJ_LOCK_ENABLED = 0X20,
 };
 
 enum RGWBucketIndexType {
@@ -1397,12 +1415,16 @@ struct RGWBucketInfo {
 
   map<string, uint32_t> mdsearch_config;
 
+
+
   /* resharding */
   uint8_t reshard_status;
   string new_bucket_instance_id;
 
+  RGWObjectLock obj_lock;
+
   void encode(bufferlist& bl) const {
-     ENCODE_START(19, 4, bl);
+     ENCODE_START(20, 4, bl);
      encode(bucket, bl);
      encode(owner.id, bl);
      encode(flags, bl);
@@ -1429,10 +1451,13 @@ struct RGWBucketInfo {
      encode(mdsearch_config, bl);
      encode(reshard_status, bl);
      encode(new_bucket_instance_id, bl);
+     if (obj_lock_enabled()) {
+       encode(obj_lock, bl);
+     }
      ENCODE_FINISH(bl);
   }
   void decode(bufferlist::const_iterator& bl) {
-    DECODE_START_LEGACY_COMPAT_LEN_32(19, 4, 4, bl);
+    DECODE_START_LEGACY_COMPAT_LEN_32(20, 4, 4, bl);
      decode(bucket, bl);
      if (struct_v >= 2) {
        string s;
@@ -1496,6 +1521,9 @@ struct RGWBucketInfo {
        decode(reshard_status, bl);
        decode(new_bucket_instance_id, bl);
      }
+     if (struct_v >= 20 && obj_lock_enabled()) {
+       decode(obj_lock, bl);
+     }
      DECODE_FINISH(bl);
   }
   void dump(Formatter *f) const;
@@ -1508,6 +1536,7 @@ struct RGWBucketInfo {
   bool versioning_enabled() const { return (versioning_status() & (BUCKET_VERSIONED | BUCKET_VERSIONS_SUSPENDED)) == BUCKET_VERSIONED; }
   bool mfa_enabled() const { return (versioning_status() & BUCKET_MFA_ENABLED) != 0; }
   bool datasync_flag_enabled() const { return (flags & BUCKET_DATASYNC_DISABLED) == 0; }
+  bool obj_lock_enabled() const { return (flags & BUCKET_OBJ_LOCK_ENABLED) != 0; }
 
   bool has_swift_versioning() const {
     /* A bucket may be versioned through one mechanism only. */
index d3e1cf05a6b29b3a4606ed7f72d11b97e3ccc663..94900de1216498b4ee41962db77bbfa33f675471 100644 (file)
@@ -95,6 +95,7 @@ static const actpair actpairs[] =
  { "s3:GetObjectTagging", s3GetObjectTagging },
  { "s3:GetObjectVersionTagging", s3GetObjectVersionTagging},
  { "s3:GetReplicationConfiguration", s3GetReplicationConfiguration },
+ { "s3:GetBucketObjectLockConfiguration", s3GetBucketObjectLockConfiguration},
  { "s3:ListAllMyBuckets", s3ListAllMyBuckets },
  { "s3:ListBucketMultipartUploads", s3ListBucketMultipartUploads },
  { "s3:ListBucket", s3ListBucket },
@@ -117,6 +118,7 @@ static const actpair actpairs[] =
  { "s3:PutObjectTagging", s3PutObjectTagging },
  { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
  { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
+ { "s3:PutBucketObjectLockConfiguration", s3PutBucketObjectLockConfiguration},
  { "s3:RestoreObject", s3RestoreObject },
  { "iam:PutUserPolicy", iamPutUserPolicy },
  { "iam:GetUserPolicy", iamGetUserPolicy },
@@ -1181,6 +1183,12 @@ const char* action_bit_string(uint64_t action) {
   case s3DeleteObjectVersionTagging:
     return "s3:DeleteObjectVersionTagging";
 
+  case s3PutBucketObjectLockConfiguration:
+    return "s3:PutBucketObjectLockConfiguration";
+
+  case s3GetBucketObjectLockConfiguration:
+    return "s3:GetBucketObjectLockConfiguration";
+
   case iamPutUserPolicy:
     return "iam:PutUserPolicy";
 
index e34aca0eea4aecb68ad646413c6ca74a617582d0..e2854bd9ebe9c869743aaa04321a4a7ac36325a3 100644 (file)
@@ -95,39 +95,46 @@ static constexpr std::uint64_t s3DeleteObjectTagging = 50;
 static constexpr std::uint64_t s3GetObjectVersionTagging = 51;
 static constexpr std::uint64_t s3PutObjectVersionTagging = 52;
 static constexpr std::uint64_t s3DeleteObjectVersionTagging = 53;
-static constexpr std::uint64_t s3All = 54;
-
-static constexpr std::uint64_t iamPutUserPolicy = 55;
-static constexpr std::uint64_t iamGetUserPolicy = 56;
-static constexpr std::uint64_t iamDeleteUserPolicy = 57;
-static constexpr std::uint64_t iamListUserPolicies = 58;
-static constexpr std::uint64_t iamCreateRole = 59;
-static constexpr std::uint64_t iamDeleteRole = 60;
-static constexpr std::uint64_t iamModifyRole = 61;
-static constexpr std::uint64_t iamGetRole = 62;
-static constexpr std::uint64_t iamListRoles = 63;
-static constexpr std::uint64_t iamPutRolePolicy = 64;
-static constexpr std::uint64_t iamGetRolePolicy = 65;
-static constexpr std::uint64_t iamListRolePolicies = 66;
-static constexpr std::uint64_t iamDeleteRolePolicy = 67;
-static constexpr std::uint64_t iamAll = 68;
-static constexpr std::uint64_t stsAssumeRole = 69;
-static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = 70;
-static constexpr std::uint64_t stsGetSessionToken = 71;
-static constexpr std::uint64_t stsAll = 72;
-
-static constexpr std::uint64_t s3Count = s3DeleteObjectVersionTagging + 1;
+static constexpr std::uint64_t s3PutBucketObjectLockConfiguration = 54;
+static constexpr std::uint64_t s3GetBucketObjectLockConfiguration = 55;
+static constexpr std::uint64_t s3PutObjectRetention = 56;
+static constexpr std::uint64_t s3GetObjectRetention = 57;
+static constexpr std::uint64_t s3PutObjectLegalHold = 58;
+static constexpr std::uint64_t s3GetObjectLegalHold = 59;
+static constexpr std::uint64_t s3BypassGovernanceRetention = 60;
+static constexpr std::uint64_t s3All = 61;
+
+static constexpr std::uint64_t iamPutUserPolicy = 62;
+static constexpr std::uint64_t iamGetUserPolicy = 63;
+static constexpr std::uint64_t iamDeleteUserPolicy = 64;
+static constexpr std::uint64_t iamListUserPolicies = 65;
+static constexpr std::uint64_t iamCreateRole = 66;
+static constexpr std::uint64_t iamDeleteRole = 67;
+static constexpr std::uint64_t iamModifyRole = 68;
+static constexpr std::uint64_t iamGetRole = 69;
+static constexpr std::uint64_t iamListRoles = 70;
+static constexpr std::uint64_t iamPutRolePolicy = 71;
+static constexpr std::uint64_t iamGetRolePolicy = 72;
+static constexpr std::uint64_t iamListRolePolicies = 73;
+static constexpr std::uint64_t iamDeleteRolePolicy = 74;
+static constexpr std::uint64_t iamAll = 75;
+static constexpr std::uint64_t stsAssumeRole = 76;
+static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = 77;
+static constexpr std::uint64_t stsGetSessionToken = 78;
+static constexpr std::uint64_t stsAll = 79;
+
+static constexpr std::uint64_t s3Count = s3BypassGovernanceRetention + 1;
 static constexpr std::uint64_t allCount = stsAll + 1;
 
 using Action_t = bitset<allCount>;
 using NotAction_t = Action_t;
 
 static const Action_t None(0);
-static const Action_t s3AllValue("111111111111111111111111111111111111111111111111111111");
-static const Action_t iamAllValue("11111111111110000000000000000000000000000000000000000000000000000000");
-static const Action_t stsAllValue("111000000000000000000000000000000000000000000000000000000000000000000000");
+static const Action_t s3AllValue("1111111111111111111111111111111111111111111111111111111111111");
+static const Action_t iamAllValue("111111111111100000000000000000000000000000000000000000000000000000000000000");
+static const Action_t stsAllValue("1110000000000000000000000000000000000000000000000000000000000000000000000000000");
 //Modify allValue if more Actions are added
-static const Action_t allValue("1111111111111111111111111111111111111111111111111111111111111111111111111");
+static const Action_t allValue("11111111111111111111111111111111111111111111111111111111111111111111111111111111");
 
 namespace {
 // Please update the table in doc/radosgw/s3/authentication.rst if you
@@ -140,6 +147,8 @@ inline int op_to_perm(std::uint64_t op) {
   case s3GetObjectVersionTorrent:
   case s3GetObjectTagging:
   case s3GetObjectVersionTagging:
+  case s3GetObjectRetention:
+  case s3GetObjectLegalHold:
   case s3ListAllMyBuckets:
   case s3ListBucket:
   case s3ListBucketMultipartUploads:
@@ -158,6 +167,8 @@ inline int op_to_perm(std::uint64_t op) {
   case s3DeleteObjectTagging:
   case s3DeleteObjectVersionTagging:
   case s3RestoreObject:
+  case s3PutObjectRetention:
+  case s3PutObjectLegalHold:
     return RGW_PERM_WRITE;
 
   case s3GetAccelerateConfiguration:
@@ -175,6 +186,7 @@ inline int op_to_perm(std::uint64_t op) {
   case s3GetObjectAcl:
   case s3GetObjectVersionAcl:
   case s3GetReplicationConfiguration:
+  case s3GetBucketObjectLockConfiguration:
     return RGW_PERM_READ_ACP;
 
   case s3DeleteBucketPolicy:
@@ -194,6 +206,7 @@ inline int op_to_perm(std::uint64_t op) {
   case s3PutObjectAcl:
   case s3PutObjectVersionAcl:
   case s3PutReplicationConfiguration:
+  case s3PutBucketObjectLockConfiguration:
     return RGW_PERM_WRITE_ACP;
 
   case s3All:
diff --git a/src/rgw/rgw_object_lock.cc b/src/rgw/rgw_object_lock.cc
new file mode 100644 (file)
index 0000000..69da888
--- /dev/null
@@ -0,0 +1,96 @@
+#include "rgw_object_lock.h"
+
+void DefaultRetention::decode_xml(XMLObj *obj) {
+  RGWXMLDecoder::decode_xml("Mode", mode, obj, true);
+  if (mode.compare("GOVERNANCE") != 0 && mode.compare("COMPLIANCE") != 0) {
+    throw RGWXMLDecoder::err("bad Mode in lock rule");
+  }
+  bool days_exist = RGWXMLDecoder::decode_xml("Days", days, obj);
+  bool years_exist = RGWXMLDecoder::decode_xml("Years", years, obj);
+  if ((days_exist && years_exist) || (!days_exist && !years_exist)) {
+    throw RGWXMLDecoder::err("either Days or Years must be specified, but not both");
+  }
+}
+
+void DefaultRetention::dump_xml(Formatter *f) const {
+  encode_xml("Mode", mode, f);
+  if (days > 0) {
+    encode_xml("Days", days, f);
+  } else {
+    encode_xml("Years", years, f);
+  }
+}
+
+void ObjectLockRule::decode_xml(XMLObj *obj) {
+  RGWXMLDecoder::decode_xml("DefaultRetention", defaultRetention, obj, true);
+}
+
+void ObjectLockRule::dump_xml(Formatter *f) const {
+  encode_xml("DefaultRetention", defaultRetention, f);
+}
+
+void RGWObjectLock::decode_xml(XMLObj *obj) {
+  string enabled_str;
+  RGWXMLDecoder::decode_xml("ObjectLockEnabled", enabled_str, obj, true);
+  if (enabled_str.compare("Enabled") != 0) {
+    throw RGWXMLDecoder::err("invalid ObjectLockEnabled value");
+  } else {
+    enabled = true;
+  }
+  rule_exist = RGWXMLDecoder::decode_xml("Rule", rule, obj);
+}
+
+void RGWObjectLock::dump_xml(Formatter *f) const {
+  if (enabled) {
+    encode_xml("ObjectLockEnabled", "Enabled", f);
+  }
+  if (rule_exist) {
+    encode_xml("Rule", rule, f);
+  }
+}
+
+ceph::real_time RGWObjectLock::get_lock_until_date(const ceph::real_time& mtime) const {
+  if (!rule_exist) {
+    return ceph::real_time();
+  }
+  int days = get_days();
+  if (days <= 0) {
+    days = get_years()*365;
+  }
+  return mtime + make_timespan(days*24*60*60);
+}
+
+void RGWObjectRetention::decode_xml(XMLObj *obj) {
+  RGWXMLDecoder::decode_xml("Mode", mode, obj, true);
+  if (mode.compare("GOVERNANCE") != 0 && mode.compare("COMPLIANCE") != 0) {
+    throw RGWXMLDecoder::err("bad Mode in retention");
+  }
+  string date_str;
+  RGWXMLDecoder::decode_xml("RetainUntilDate", date_str, obj, true);
+  boost::optional<ceph::real_time> date = ceph::from_iso_8601(date_str);
+  if (boost::none == date) {
+    throw RGWXMLDecoder::err("invalid RetainUntilDate value");
+  }
+  retain_until_date = *date;
+}
+
+void RGWObjectRetention::dump_xml(Formatter *f) const {
+  encode_xml("Mode", mode, f);
+  string date = ceph::to_iso_8601(retain_until_date);
+  encode_xml("RetainUntilDate", date, f);
+}
+
+void RGWObjectLegalHold::decode_xml(XMLObj *obj) {
+  RGWXMLDecoder::decode_xml("Status", status, obj, true);
+  if (status.compare("ON") != 0 && status.compare("OFF") != 0) {
+    throw RGWXMLDecoder::err("bad status in legal hold");
+  }
+}
+
+void RGWObjectLegalHold::dump_xml(Formatter *f) const {
+  encode_xml("Status", status, f);
+}
+
+bool RGWObjectLegalHold::is_enabled() const {
+  return status.compare("ON") == 0;
+}
diff --git a/src/rgw/rgw_object_lock.h b/src/rgw/rgw_object_lock.h
new file mode 100644 (file)
index 0000000..e743010
--- /dev/null
@@ -0,0 +1,217 @@
+#ifndef CEPH_RGW_OBJECT_LOCK_H
+#define CEPH_RGW_OBJECT_LOCK_H
+
+#include <string>
+#include "common/ceph_time.h"
+#include "common/iso_8601.h"
+#include "rgw_xml.h"
+using namespace std;
+
+class DefaultRetention
+{
+protected:
+  string mode;
+  int days;
+  int years;
+
+public:
+  DefaultRetention(): days(0), years(0) {};
+
+  int get_days() const {
+    return days;
+  }
+
+  int get_years() const {
+    return years;
+  }
+
+  string get_mode() const {
+    return mode;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(mode, bl);
+    encode(days, bl);
+    encode(years, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(mode, bl);
+    decode(days, bl);
+    decode(years, bl);
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(DefaultRetention)
+
+class ObjectLockRule
+{
+protected:
+  DefaultRetention defaultRetention;
+public:
+  int get_days() const {
+    return defaultRetention.get_days();
+  }
+
+  int get_years() const {
+    return defaultRetention.get_years();
+  }
+
+  string get_mode() const {
+    return defaultRetention.get_mode();
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(defaultRetention, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(defaultRetention, bl);
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(ObjectLockRule)
+
+class RGWObjectLock
+{
+protected:
+  bool enabled;
+  bool rule_exist;
+  ObjectLockRule rule;
+
+public:
+  RGWObjectLock():enabled(true), rule_exist(false) {}
+
+  int get_days() const {
+    return rule.get_days();
+  }
+
+  int get_years() const {
+    return rule.get_years();
+  }
+
+  string get_mode() const {
+    return rule.get_mode();
+  }
+
+  bool retention_period_valid() const {
+    return (get_years() > 0 || get_days() > 0);
+  }
+
+  bool has_rule() const {
+    return rule_exist;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(enabled, bl);
+    encode(rule_exist, bl);
+    if (rule_exist) {
+      encode(rule, bl);
+    }
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(enabled, bl);
+    decode(rule_exist, bl);
+    if (rule_exist) {
+      decode(rule, bl);
+    }
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+  ceph::real_time get_lock_until_date(const ceph::real_time& mtime) const;
+};
+WRITE_CLASS_ENCODER(RGWObjectLock)
+
+class RGWObjectRetention
+{
+protected:
+  string mode;
+  ceph::real_time retain_until_date;
+public:
+  RGWObjectRetention() {}
+  RGWObjectRetention(string _mode, ceph::real_time _date): mode(_mode), retain_until_date(_date) {}
+
+  void set_mode(string _mode) {
+    mode = _mode;
+  }
+
+  string get_mode() const {
+    return mode;
+  }
+
+  void set_retain_until_date(ceph::real_time _retain_until_date) {
+    retain_until_date = _retain_until_date;
+  }
+
+  ceph::real_time get_retain_until_date() const {
+    return retain_until_date;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(mode, bl);
+    encode(retain_until_date, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(mode, bl);
+    decode(retain_until_date, bl);
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(RGWObjectRetention)
+
+class RGWObjectLegalHold
+{
+protected:
+  string status;
+public:
+  void set_status(string _status) {
+    status = _status;
+  }
+
+  string get_status() const {
+    return status;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(status, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(status, bl);
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+  bool is_enabled() const;
+};
+WRITE_CLASS_ENCODER(RGWObjectLegalHold)
+#endif //CEPH_RGW_OBJECT_LOCK_H
index aede5202bd6cc9e4cf62c0ad66163d7c9e3b363f..3d3497c170a5bb136c05282baafa0fafd28386f0 100644 (file)
@@ -976,6 +976,11 @@ int RGWGetObj::verify_permission()
     return -EACCES;
   }
 
+  if (s->bucket_info.obj_lock_enabled()) {
+    get_retention = verify_object_permission(this, s, rgw::IAM::s3GetObjectRetention);
+    get_legal_hold = verify_object_permission(this, s, rgw::IAM::s3GetObjectLegalHold);
+  }
+
   return 0;
 }
 
@@ -2505,6 +2510,11 @@ void RGWSetBucketVersioning::execute()
   if (op_ret < 0)
     return;
 
+  if (s->bucket_info.obj_lock_enabled() && versioning_status != VersioningEnabled) {
+    op_ret = -ERR_INVALID_BUCKET_STATE;
+    return;
+  }
+
   bool cur_mfa_status = (s->bucket_info.flags & BUCKET_MFA_ENABLED) != 0;
 
   mfa_set_status &= (mfa_status != cur_mfa_status);
@@ -3181,6 +3191,10 @@ void RGWCreateBucket::execute()
     s->bucket_info.swift_ver_location = *swift_ver_location;
     s->bucket_info.swift_versioning = (! swift_ver_location->empty());
   }
+  if (obj_lock_enabled) {
+    info.flags = BUCKET_VERSIONED | BUCKET_OBJ_LOCK_ENABLED;
+  }
+
 
   op_ret = store->create_bucket(*(s->user), s->bucket, zonegroup_id,
                                 placement_rule, s->bucket_info.swift_ver_location,
@@ -4552,7 +4566,24 @@ int RGWDeleteObj::handle_slo_manifest(bufferlist& bl)
 
 int RGWDeleteObj::verify_permission()
 {
+  int op_ret = get_params();
+  if (op_ret) {
+    return op_ret;
+  }
   if (s->iam_policy || ! s->iam_user_policies.empty()) {
+    if (s->bucket_info.obj_lock_enabled() && bypass_governance_mode) {
+      auto r = eval_user_policies(s->iam_user_policies, s->env, boost::none,
+                                               rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket, s->object.name));
+      if (r == Effect::Allow) {
+        bypass_perm = true;
+      } else if (r == Effect::Pass && s->iam_policy) {
+        r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention,
+                                     ARN(s->bucket, s->object.name));
+        if (r == Effect::Allow) {
+          bypass_perm = true;
+        }
+      }
+    }
     auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
                                               boost::none,
                                               s->object.instance.empty() ?
@@ -4605,17 +4636,13 @@ void RGWDeleteObj::execute()
     return;
   }
 
-  op_ret = get_params();
-  if (op_ret < 0) {
-    return;
-  }
-
   rgw_obj obj(s->bucket, s->object);
   map<string, bufferlist> attrs;
 
+  bool check_obj_lock = obj.key.have_instance() && s->bucket_info.obj_lock_enabled();
 
   if (!s->object.empty()) {
-    if (need_object_expiration() || multipart_delete) {
+    if (need_object_expiration() || multipart_delete || check_obj_lock) {
       /* check if obj exists, read orig attrs */
       op_ret = get_obj_attrs(store, s, obj, attrs);
       if (op_ret < 0) {
@@ -4623,6 +4650,29 @@ void RGWDeleteObj::execute()
       }
     }
 
+    if (check_obj_lock) {
+      auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
+      if (aiter != attrs.end()) {
+        RGWObjectRetention obj_retention;
+        decode(obj_retention, aiter->second);
+        if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) {
+          if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
+            op_ret = -EACCES;
+            return;
+          }
+        }
+      }
+      aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD);
+      if (aiter != attrs.end()) {
+        RGWObjectLegalHold obj_legal_hold;
+        decode(obj_legal_hold, aiter->second);
+        if (obj_legal_hold.is_enabled()) {
+          op_ret = -EACCES;
+          return;
+        }
+      }
+    }
+
     if (multipart_delete) {
       const auto slo_attr = attrs.find(RGW_ATTR_SLO_MANIFEST);
 
@@ -7414,6 +7464,308 @@ void RGWDeleteBucketPolicy::execute()
     });
 }
 
+void RGWPutBucketObjectLock::pre_exec()
+{
+  rgw_bucket_object_pre_exec(s);
+}
+
+int RGWPutBucketObjectLock::verify_permission()
+{
+  return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketObjectLockConfiguration);
+}
+
+void RGWPutBucketObjectLock::execute()
+{
+  if (!s->bucket_info.obj_lock_enabled()) {
+    ldpp_dout(this, 0) << "ERROR: object Lock configuration cannot be enabled on existing buckets" << dendl;
+    op_ret = -ERR_INVALID_BUCKET_STATE;
+    return;
+  }
+
+  RGWXMLDecoder::XMLParser parser;
+  if (!parser.init()) {
+    ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
+
+  op_ret = get_params();
+  if (op_ret < 0)
+    return;
+
+  if (!parser.parse(data.c_str(), data.length(), 1)) {
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+
+  try {
+    RGWXMLDecoder::decode_xml("ObjectLockConfiguration", obj_lock, &parser, true);
+  } catch (RGWXMLDecoder::err& err) {
+    ldout(s->cct, 5) << "unexpected xml:" << err.message << dendl;
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+  if (obj_lock.has_rule() && !obj_lock.retention_period_valid()) {
+    ldpp_dout(this, 0) << "ERROR: retention period must be a positive integer value" << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
+
+  if (!store->svc.zone->is_meta_master()) {
+    op_ret = forward_request_to_master(s, NULL, store, data, nullptr);
+    if (op_ret < 0) {
+      ldout(s->cct, 20) << __func__ << "forward_request_to_master returned ret=" << op_ret << dendl;
+      return;
+    }
+  }
+
+  op_ret = retry_raced_bucket_write(store, s, [this] {
+    s->bucket_info.obj_lock = obj_lock;
+    op_ret = store->put_bucket_instance_info(s->bucket_info, false,
+                                             real_time(), &s->bucket_attrs);
+    return op_ret;
+  });
+  return;
+}
+
+void RGWGetBucketObjectLock::pre_exec()
+{
+  rgw_bucket_object_pre_exec(s);
+}
+
+int RGWGetBucketObjectLock::verify_permission()
+{
+  return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketObjectLockConfiguration);
+}
+
+void RGWGetBucketObjectLock::execute()
+{
+  if (!s->bucket_info.obj_lock_enabled()) {
+    op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
+    return;
+  }
+}
+
+int RGWPutObjRetention::verify_permission()
+{
+  if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectRetention)) {
+    return -EACCES;
+  }
+  op_ret = get_params();
+  if (op_ret) {
+    return op_ret;
+  }
+  if (bypass_governance_mode) {
+    bypass_perm = verify_object_permission(this, s, rgw::IAM::s3BypassGovernanceRetention);
+  }
+  return 0;
+}
+
+void RGWPutObjRetention::pre_exec()
+{
+  rgw_bucket_object_pre_exec(s);
+}
+
+void RGWPutObjRetention::execute()
+{
+  if (!s->bucket_info.obj_lock_enabled()) {
+    ldpp_dout(this, 0) << "ERROR: object retention can't be set if bucket object lock not configured" << dendl;
+    op_ret = -ERR_INVALID_REQUEST;
+    return;
+  }
+
+  RGWXMLDecoder::XMLParser parser;
+  if (!parser.init()) {
+    ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
+
+  op_ret = get_params();
+  if (op_ret < 0)
+    return;
+
+  if (!parser.parse(data.c_str(), data.length(), 1)) {
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+
+  try {
+    RGWXMLDecoder::decode_xml("Retention", obj_retention, &parser, true);
+  } catch (RGWXMLDecoder::err& err) {
+    ldpp_dout(this, 5) << "unexpected xml:" << err.message << dendl;
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+  bufferlist bl;
+  obj_retention.encode(bl);
+  rgw_obj obj(s->bucket, s->object);
+
+  //check old retention
+  map<string, bufferlist> attrs;
+  op_ret = get_obj_attrs(store, s, obj, attrs);
+  if (op_ret < 0) {
+    ldpp_dout(this, 0) << "ERROR: get obj attr error"<< dendl;
+    return;
+  }
+  auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
+  if (aiter != attrs.end()) {
+    RGWObjectRetention old_obj_retention;
+    decode(old_obj_retention, aiter->second);
+    if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
+      op_ret = -EACCES;
+      return;
+    }
+  }
+
+  store->set_atomic(s->obj_ctx, obj);
+  attrs[RGW_ATTR_OBJECT_RETENTION] = bl;
+  op_ret = store->set_attrs(s->obj_ctx, s->bucket_info, obj, attrs, NULL);
+  return;
+}
+
+int RGWGetObjRetention::verify_permission()
+{
+  if (!verify_object_permission(this, s, rgw::IAM::s3GetObjectRetention)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
+void RGWGetObjRetention::pre_exec()
+{
+  rgw_bucket_object_pre_exec(s);
+}
+
+void RGWGetObjRetention::execute()
+{
+  if (!s->bucket_info.obj_lock_enabled()) {
+    ldpp_dout(this, 0) << "ERROR: bucket object lock not configured" << dendl;
+    op_ret = -ERR_INVALID_REQUEST;
+    return;
+  }
+  rgw_obj obj(s->bucket, s->object);
+  map<string, bufferlist> attrs;
+  op_ret = get_obj_attrs(store, s, obj, attrs);
+  if (op_ret < 0) {
+    ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << obj
+                       << " ret=" << op_ret << dendl;
+    return;
+  }
+  auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
+  if (aiter == attrs.end()) {
+    op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
+    return;
+  }
+
+  bufferlist::const_iterator iter{&aiter->second};
+  try {
+    obj_retention.decode(iter);
+  } catch (const buffer::error& e) {
+    ldout(s->cct, 0) << __func__ <<  "decode object retention config failed" << dendl;
+    op_ret = -EIO;
+    return;
+  }
+  return;
+}
+
+int RGWPutObjLegalHold::verify_permission()
+{
+  if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectLegalHold)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
+void RGWPutObjLegalHold::pre_exec()
+{
+  rgw_bucket_object_pre_exec(s);
+}
+
+void RGWPutObjLegalHold::execute() {
+  if (!s->bucket_info.obj_lock_enabled()) {
+    ldpp_dout(this, 0) << "ERROR: object legal hold can't be set if bucket object lock not configured" << dendl;
+    op_ret = -ERR_INVALID_REQUEST;
+    return;
+  }
+
+  RGWXMLDecoder::XMLParser parser;
+  if (!parser.init()) {
+    ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
+
+  op_ret = get_params();
+  if (op_ret < 0)
+    return;
+
+  if (!parser.parse(data.c_str(), data.length(), 1)) {
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+
+  try {
+    RGWXMLDecoder::decode_xml("LegalHold", obj_legal_hold, &parser, true);
+  } catch (RGWXMLDecoder::err &err) {
+    ldout(s->cct, 5) << "unexpected xml:" << err.message << dendl;
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+  bufferlist bl;
+  obj_legal_hold.encode(bl);
+  rgw_obj obj(s->bucket, s->object);
+  store->set_atomic(s->obj_ctx, obj);
+  //if instance is empty, we should modify the latest object
+  op_ret = modify_obj_attr(store, s, obj, RGW_ATTR_OBJECT_LEGAL_HOLD, bl);
+  return;
+}
+
+int RGWGetObjLegalHold::verify_permission()
+{
+  if (!verify_object_permission(this, s, rgw::IAM::s3GetObjectLegalHold)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
+void RGWGetObjLegalHold::pre_exec()
+{
+  rgw_bucket_object_pre_exec(s);
+}
+
+void RGWGetObjLegalHold::execute()
+{
+  if (!s->bucket_info.obj_lock_enabled()) {
+    ldpp_dout(this, 0) << "ERROR: bucket object lock not configured" << dendl;
+    op_ret = -ERR_INVALID_REQUEST;
+    return;
+  }
+  rgw_obj obj(s->bucket, s->object);
+  map<string, bufferlist> attrs;
+  op_ret = get_obj_attrs(store, s, obj, attrs);
+  if (op_ret < 0) {
+    ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << obj
+                       << " ret=" << op_ret << dendl;
+    return;
+  }
+  auto aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD);
+  if (aiter == attrs.end()) {
+    op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
+    return;
+  }
+
+  bufferlist::const_iterator iter{&aiter->second};
+  try {
+    obj_legal_hold.decode(iter);
+  } catch (const buffer::error& e) {
+    ldout(s->cct, 0) << __func__ <<  "decode object legal hold config failed" << dendl;
+    op_ret = -EIO;
+    return;
+  }
+  return;
+}
+
 void RGWGetClusterStat::execute()
 {
   op_ret = this->store->get_rados_handle()->cluster_stat(stats_op);
index 49ed254a4ff6f21542eb48cc057c98ea39de88c6..1a4af91582dfade2f55b3d22af4ed852bb2fdd66 100644 (file)
@@ -45,6 +45,7 @@
 #include "rgw_lc.h"
 #include "rgw_torrent.h"
 #include "rgw_tag.h"
+#include "rgw_object_lock.h"
 #include "cls/lock/cls_lock_client.h"
 #include "cls/rgw/cls_rgw_client.h"
 
@@ -294,6 +295,9 @@ protected:
   bufferlist waiting;
   uint64_t action = 0;
 
+  bool get_retention;
+  bool get_legal_hold;
+
   int init_common();
 public:
   RGWGetObj() {
@@ -321,6 +325,8 @@ public:
     q_len = 0;
     first_data = true;
     cur_ofs = 0;
+    get_retention = false;
+    get_legal_hold = false;
  }
 
   bool prefetch_data() override;
@@ -956,6 +962,7 @@ protected:
   obj_version ep_objv;
   bool has_cors;
   bool relaxed_region_enforcement;
+  bool obj_lock_enabled;
   RGWCORSConfiguration cors_config;
   boost::optional<std::string> swift_ver_location;
   map<string, buffer::list> attrs;
@@ -966,7 +973,7 @@ protected:
   virtual bool need_metadata_upload() const { return false; }
 
 public:
-  RGWCreateBucket() : has_cors(false), relaxed_region_enforcement(false) {}
+  RGWCreateBucket() : has_cors(false), relaxed_region_enforcement(false), obj_lock_enabled(false) {}
 
   void emplace_attr(std::string&& key, buffer::list&& bl) {
     attrs.emplace(std::move(key), std::move(bl)); /* key and bl are r-value refs */
@@ -1321,13 +1328,17 @@ protected:
   ceph::real_time unmod_since; /* if unmodified since */
   bool no_precondition_error;
   std::unique_ptr<RGWBulkDelete::Deleter> deleter;
+  bool bypass_perm;
+  bool bypass_governance_mode;
 
 public:
   RGWDeleteObj()
     : delete_marker(false),
       multipart_delete(false),
       no_precondition_error(false),
-      deleter(nullptr) {
+      deleter(nullptr),
+      bypass_perm(false),
+      bypass_governance_mode(false) {
   }
 
   int verify_permission() override;
@@ -2178,6 +2189,93 @@ public:
   }
 };
 
+class RGWPutBucketObjectLock : public RGWOp {
+protected:
+  bufferlist data;
+  bufferlist obj_lock_bl;
+  RGWObjectLock obj_lock;
+public:
+  RGWPutBucketObjectLock() = default;
+  ~RGWPutBucketObjectLock() {}
+  int verify_permission() override;
+  void pre_exec() override;
+  void execute() override;
+  virtual void send_response() = 0;
+  virtual int get_params() = 0;
+  const char* name() const override { return "put_bucket_object_lock"; }
+  RGWOpType get_type() override { return RGW_OP_PUT_BUCKET_OBJ_LOCK; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+};
+
+class RGWGetBucketObjectLock : public RGWOp {
+public:
+  int verify_permission() override;
+  void pre_exec() override;
+  void execute() override;
+  virtual void send_response() = 0;
+  const char* name() const override {return "get_bucket_object_lock"; }
+  RGWOpType get_type() override { return RGW_OP_GET_BUCKET_OBJ_LOCK; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
+};
+
+class RGWPutObjRetention : public RGWOp {
+protected:
+  bufferlist data;
+  RGWObjectRetention obj_retention;
+  bool bypass_perm;
+  bool bypass_governance_mode;
+public:
+  int verify_permission() override;
+  void pre_exec() override;
+  void execute() override;
+  virtual void send_response() override = 0;
+  virtual int get_params() = 0;
+  const char* name() const override { return "put_obj_retention"; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+  RGWOpType get_type() override { return RGW_OP_PUT_OBJ_RETENTION; }
+};
+
+class RGWGetObjRetention : public RGWOp {
+protected:
+  RGWObjectRetention obj_retention;
+public:
+  int verify_permission() override;
+  void pre_exec() override;
+  void execute() override;
+  virtual void send_response() = 0;
+  const char* name() const override {return "get_obj_retention"; }
+  RGWOpType get_type() override { return RGW_OP_GET_OBJ_RETENTION; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
+};
+
+class RGWPutObjLegalHold : public RGWOp {
+protected:
+  bufferlist data;
+  RGWObjectLegalHold obj_legal_hold;
+public:
+  int verify_permission() override;
+  void pre_exec() override;
+  void execute() override;
+  virtual void send_response() override = 0;
+  virtual int get_params() = 0;
+  const char* name() const override { return "put_obj_legal_hold"; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+  RGWOpType get_type() override { return RGW_OP_PUT_OBJ_LEGAL_HOLD; }
+};
+
+class RGWGetObjLegalHold : public RGWOp {
+protected:
+  RGWObjectLegalHold obj_legal_hold;
+public:
+  int verify_permission() override;
+  void pre_exec() override;
+  void execute() override;
+  virtual void send_response() = 0;
+  const char* name() const override {return "get_obj_legal_hold"; }
+  RGWOpType get_type() override { return RGW_OP_GET_OBJ_LEGAL_HOLD; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
+};
+
 
 class RGWConfigBucketMetaSearch : public RGWOp {
 protected:
index 77fcb80bf14bcdbdbfe4188f1726b69f88aca18b..a53e403d8ddec5eb8ba58f4d04f162e1ef20c39a 100644 (file)
@@ -3588,6 +3588,15 @@ int RGWRados::Object::Write::_do_write_meta(uint64_t size, uint64_t accounted_si
     meta.set_mtime = real_clock::now();
   }
 
+  if (target->bucket_info.obj_lock_enabled() && target->bucket_info.obj_lock.has_rule() && meta.flags == PUT_OBJ_CREATE) {
+    real_time lock_until_date = target->bucket_info.obj_lock.get_lock_until_date(meta.set_mtime);
+    string mode = target->bucket_info.obj_lock.get_mode();
+    RGWObjectRetention obj_retention(mode, lock_until_date);
+    bufferlist bl;
+    obj_retention.encode(bl);
+    op.setxattr(RGW_ATTR_OBJECT_RETENTION, bl);
+  }
+
   if (state->is_olh) {
     op.setxattr(RGW_ATTR_OLH_ID_TAG, state->olh_tag);
   }
index d461f8ba079529f5797959748d41867b9ea9b223..b893e82c2c98e46079f876b227613ca850464900 100644 (file)
@@ -1447,6 +1447,21 @@ int RGWPutLC_ObjStore::get_params()
   return op_ret;
 }
 
+int RGWPutBucketObjectLock_ObjStore::get_params()
+{
+  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+  std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
+  return op_ret;
+}
+
+int RGWPutObjLegalHold_ObjStore::get_params()
+{
+  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+  std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
+  return op_ret;
+}
+
+
 static std::tuple<int, bufferlist> read_all_chunked_input(req_state *s, const uint64_t max_read)
 {
 #define READ_CHUNK 4096
index 0e5808c5059c4723494b2d3ff0ab0d37b0f56dda..6ba34d4eeb2c61988634259e3bbfd8d1f1c310b0 100644 (file)
@@ -485,6 +485,44 @@ public:
     ~RGWInfo_ObjStore() override = default;
 };
 
+class RGWPutBucketObjectLock_ObjStore : public RGWPutBucketObjectLock {
+public:
+  RGWPutBucketObjectLock_ObjStore() = default;
+  ~RGWPutBucketObjectLock_ObjStore() = default;
+  int get_params() override;
+};
+
+class RGWGetBucketObjectLock_ObjStore : public RGWGetBucketObjectLock {
+public:
+  RGWGetBucketObjectLock_ObjStore() = default;
+  ~RGWGetBucketObjectLock_ObjStore() override = default;
+};
+
+class RGWPutObjRetention_ObjStore : public RGWPutObjRetention {
+public:
+  RGWPutObjRetention_ObjStore() = default;
+  ~RGWPutObjRetention_ObjStore() override = default;
+};
+
+class RGWGetObjRetention_ObjStore : public RGWGetObjRetention {
+public:
+  RGWGetObjRetention_ObjStore() = default;
+  ~RGWGetObjRetention_ObjStore() = default;
+};
+
+class RGWPutObjLegalHold_ObjStore : public RGWPutObjLegalHold {
+public:
+  RGWPutObjLegalHold_ObjStore() = default;
+  ~RGWPutObjLegalHold_ObjStore() override = default;
+  int get_params() override;
+};
+
+class RGWGetObjLegalHold_ObjStore : public RGWGetObjLegalHold {
+public:
+  RGWGetObjLegalHold_ObjStore() = default;
+  ~RGWGetObjLegalHold_ObjStore() = default;
+};
+
 class RGWRESTOp : public RGWOp {
 protected:
   int http_ret;
index ae1065160ad482aaebfdab63efc6a1b82de58586..39b380c16fdd08d173af18d3476ddc0ac1b31156 100644 (file)
@@ -263,6 +263,7 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
   } else {
     dump_header(s, "x-rgw-object-type", "Normal");
   }
+
   if (! op_ret) {
     if (! lo_etag.empty()) {
       /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
@@ -328,6 +329,12 @@ int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
           ldpp_dout(this,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
         }
         dump_header(s, RGW_AMZ_TAG_COUNT, obj_tags.count());
+      } else if (iter->first.compare(RGW_ATTR_OBJECT_LOCK_MODE) == 0 && get_retention) {
+        dump_header(s, "x-amz-object-lock-mode", iter->second.to_str());
+      } else if (iter->first.compare(RGW_ATTR_OBJECT_LOCK_UNTIL_DATE) == 0 && get_retention) {
+        real_time lock_until_date;
+        decode(lock_until_date, iter->second);
+        dump_time_header(s, "x-amz-object-lock-retain-until-date", lock_until_date);
       }
     }
   }
@@ -1366,7 +1373,13 @@ int RGWCreateBucket_ObjStore_S3::get_params()
   } else {
     placement_rule.storage_class = s->info.storage_class;
   }
-
+  auto iter = s->info.x_meta_map.find("x-amz-bucket-object-lock-enabled");
+  if (iter != s->info.x_meta_map.end()) {
+    if ((iter->second.compare("true") != 0 && (iter->second.compare("false") != 0))) {
+      return -EINVAL;
+    }
+    obj_lock_enabled = iter->second.compare("true") == 0;
+  }
   return 0;
 }
 
@@ -2257,6 +2270,12 @@ int RGWDeleteObj_ObjStore_S3::get_params()
     unmod_since = utime_t(epoch, nsec).to_real_time();
   }
 
+  const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
+  if (bypass_gov_header) {
+    std::string bypass_gov_decoded = url_decode(bypass_gov_header);
+    bypass_governance_mode = bypass_gov_decoded.compare("true") == 0;
+  }
+
   return 0;
 }
 
@@ -3157,6 +3176,95 @@ void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
   end_header(s, this);
 }
 
+void RGWPutBucketObjectLock_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s);
+}
+
+void RGWGetBucketObjectLock_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s, this, "application/xml");
+  dump_start(s);
+
+  if (op_ret) {
+    return;
+  }
+  encode_xml("ObjectLockConfiguration", s->bucket_info.obj_lock, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
+}
+
+
+int RGWPutObjRetention_ObjStore_S3::get_params()
+{
+  const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
+  if (bypass_gov_header) {
+    std::string bypass_gov_decoded = url_decode(bypass_gov_header);
+    bypass_governance_mode = bypass_gov_decoded.compare("true") == 0;
+  }
+
+  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+  std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
+  return op_ret;
+}
+
+void RGWPutObjRetention_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s);
+}
+
+void RGWGetObjRetention_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s, this, "application/xml");
+  dump_start(s);
+
+  if (op_ret) {
+    return;
+  }
+  encode_xml("Retention", obj_retention, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
+}
+
+void RGWPutObjLegalHold_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s);
+}
+
+void RGWGetObjLegalHold_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s, this, "application/xml");
+  dump_start(s);
+
+  if (op_ret) {
+    return;
+  }
+  encode_xml("LegalHold", obj_legal_hold, s->formatter);
+  rgw_flush_formatter_and_reset(s, s->formatter);
+}
+
 
 RGWOp *RGWHandler_REST_Service_S3::op_get()
 {
@@ -3247,7 +3355,9 @@ RGWOp *RGWHandler_REST_Bucket_S3::op_get()
     return new RGWGetBucketPolicy;
   } else if (is_tagging_op()) {
     return new RGWGetBucketTags_ObjStore_S3;
-  } 
+  } else if (is_object_lock_op()) {
+    return new RGWGetBucketObjectLock_ObjStore_S3;
+  }
   return get_obj_op(true);
 }
 
@@ -3286,6 +3396,10 @@ RGWOp *RGWHandler_REST_Bucket_S3::op_put()
     return new RGWPutLC_ObjStore_S3;
   } else if(is_policy_op()) {
     return new RGWPutBucketPolicy;
+  } else if (is_tagging_op()) {
+    return nullptr;
+  } else if (is_object_lock_op()) {
+    return new RGWPutBucketObjectLock_ObjStore_S3;
   }
   return new RGWCreateBucket_ObjStore_S3;
 }
@@ -3351,6 +3465,10 @@ RGWOp *RGWHandler_REST_Obj_S3::op_get()
     return new RGWGetObjLayout_ObjStore_S3;
   } else if (is_tagging_op()) {
     return new RGWGetObjTags_ObjStore_S3;
+  } else if (is_obj_retention_op()) {
+    return new RGWGetObjRetention_ObjStore_S3;
+  } else if (is_obj_legal_hold_op()) {
+    return new RGWGetObjLegalHold_ObjStore_S3;
   }
   return get_obj_op(true);
 }
@@ -3371,6 +3489,10 @@ RGWOp *RGWHandler_REST_Obj_S3::op_put()
     return new RGWPutACLs_ObjStore_S3;
   } else if (is_tagging_op()) {
     return new RGWPutObjTags_ObjStore_S3;
+  } else if (is_obj_retention_op()) {
+    return new RGWPutObjRetention_ObjStore_S3;
+  } else if (is_obj_legal_hold_op()) {
+    return new RGWPutObjLegalHold_ObjStore_S3;
   }
 
   if (s->init_state.src_bucket.empty())
@@ -4162,6 +4284,11 @@ AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
         case RGW_OP_PUT_LC:
         case RGW_OP_SET_REQUEST_PAYMENT:
         case RGW_OP_PUBSUB_NOTIF_CREATE:
+        case RGW_OP_PUT_BUCKET_OBJ_LOCK:
+        case RGW_OP_PUT_OBJ_RETENTION:
+        case RGW_OP_PUT_OBJ_LEGAL_HOLD:
+        case RGW_STS_GET_SESSION_TOKEN:
+        case RGW_STS_ASSUME_ROLE:
           break;
         default:
           dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
index 023a9f0d68d2245049f3d9bf9f31e3c2d1726c3a..e782460e673287b2e94c4b68b4c9c4399ded4dce 100644 (file)
@@ -453,6 +453,49 @@ public:
   void end_response() override;
 };
 
+class RGWPutBucketObjectLock_ObjStore_S3 : public RGWPutBucketObjectLock_ObjStore {
+public:
+  RGWPutBucketObjectLock_ObjStore_S3() {}
+  ~RGWPutBucketObjectLock_ObjStore_S3() override {}
+  void send_response() override;
+};
+
+class RGWGetBucketObjectLock_ObjStore_S3 : public RGWGetBucketObjectLock_ObjStore {
+public:
+  RGWGetBucketObjectLock_ObjStore_S3() {}
+  ~RGWGetBucketObjectLock_ObjStore_S3() {}
+  void send_response() override;
+};
+
+class RGWPutObjRetention_ObjStore_S3 : public RGWPutObjRetention_ObjStore {
+public:
+  RGWPutObjRetention_ObjStore_S3() {}
+  ~RGWPutObjRetention_ObjStore_S3() {}
+  int get_params() override;
+  void send_response() override;
+};
+
+class RGWGetObjRetention_ObjStore_S3 : public RGWGetObjRetention_ObjStore {
+public:
+  RGWGetObjRetention_ObjStore_S3() {}
+  ~RGWGetObjRetention_ObjStore_S3() {}
+  void send_response() override;
+};
+
+class RGWPutObjLegalHold_ObjStore_S3 : public RGWPutObjLegalHold_ObjStore {
+public:
+  RGWPutObjLegalHold_ObjStore_S3() {}
+  ~RGWPutObjLegalHold_ObjStore_S3() {}
+  void send_response() override;
+};
+
+class RGWGetObjLegalHold_ObjStore_S3 : public RGWGetObjLegalHold_ObjStore {
+public:
+  RGWGetObjLegalHold_ObjStore_S3() {}
+  ~RGWGetObjLegalHold_ObjStore_S3() {}
+  void send_response() override;
+};
+
 class RGWGetObjLayout_ObjStore_S3 : public RGWGetObjLayout {
 public:
   RGWGetObjLayout_ObjStore_S3() {}
@@ -581,7 +624,10 @@ protected:
     return s->info.args.exists("policy");
   }
   bool is_tagging_op() const {
-    return s->info.args.exists("tagging");
+      return s->info.args.exists("tagging");
+  }
+  bool is_object_lock_op() {
+    return s->info.args.exists("object-lock");
   }
   RGWOp *get_obj_op(bool get_data);
 
@@ -604,8 +650,15 @@ protected:
   bool is_tagging_op() {
     return s->info.args.exists("tagging");
   }
+  bool is_obj_retention_op() {
+    return s->info.args.exists("retention");
+  }
+  bool is_obj_legal_hold_op() {
+    return s->info.args.exists("legal-hold");
+  }
+
   bool is_obj_update_op() override {
-    return is_acl_op() || is_tagging_op() ;
+    return is_acl_op() || is_tagging_op() || is_obj_retention_op() || is_obj_legal_hold_op();
   }
   RGWOp *get_obj_op(bool get_data);