From 0c24ce5581db6518ce2ebae4d51037e5ab61f405 Mon Sep 17 00:00:00 2001 From: zhang Shaowen Date: Mon, 11 Feb 2019 15:34:46 +0800 Subject: [PATCH] rgw: add object lock feature. Signed-off-by: zhang Shaowen --- src/rgw/CMakeLists.txt | 3 +- src/rgw/rgw_auth_s3.cc | 3 +- src/rgw/rgw_common.cc | 1 + src/rgw/rgw_common.h | 33 +++- src/rgw/rgw_iam_policy.cc | 8 + src/rgw/rgw_iam_policy.h | 65 ++++--- src/rgw/rgw_object_lock.cc | 96 ++++++++++ src/rgw/rgw_object_lock.h | 217 ++++++++++++++++++++++ src/rgw/rgw_op.cc | 364 ++++++++++++++++++++++++++++++++++++- src/rgw/rgw_op.h | 102 ++++++++++- src/rgw/rgw_rados.cc | 9 + src/rgw/rgw_rest.cc | 15 ++ src/rgw/rgw_rest.h | 38 ++++ src/rgw/rgw_rest_s3.cc | 131 ++++++++++++- src/rgw/rgw_rest_s3.h | 57 +++++- 15 files changed, 1100 insertions(+), 42 deletions(-) create mode 100644 src/rgw/rgw_object_lock.cc create mode 100644 src/rgw/rgw_object_lock.h diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 10aee8d2a05..3a5db470d8c 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -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) diff --git a/src/rgw/rgw_auth_s3.cc b/src/rgw/rgw_auth_s3.cc index c87060a8e0c..0780101fb4a 100644 --- a/src/rgw/rgw_auth_s3.cc +++ b/src/rgw/rgw_auth_s3.cc @@ -47,7 +47,8 @@ static const auto signed_subresources = { "versionId", "versioning", "versions", - "website" + "website", + "object-lock" }; /* diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 356e8e5b0fa..6245aa9ec44 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -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" }}, diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index fcb78de36c8..9c07f5e9e4b 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -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 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. */ diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc index d3e1cf05a6b..94900de1216 100644 --- a/src/rgw/rgw_iam_policy.cc +++ b/src/rgw/rgw_iam_policy.cc @@ -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"; diff --git a/src/rgw/rgw_iam_policy.h b/src/rgw/rgw_iam_policy.h index e34aca0eea4..e2854bd9ebe 100644 --- a/src/rgw/rgw_iam_policy.h +++ b/src/rgw/rgw_iam_policy.h @@ -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; 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 index 00000000000..69da8881998 --- /dev/null +++ b/src/rgw/rgw_object_lock.cc @@ -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 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 index 00000000000..e7430105d98 --- /dev/null +++ b/src/rgw/rgw_object_lock.h @@ -0,0 +1,217 @@ +#ifndef CEPH_RGW_OBJECT_LOCK_H +#define CEPH_RGW_OBJECT_LOCK_H + +#include +#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 diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index aede5202bd6..3d3497c170a 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -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 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 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 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 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); diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 49ed254a4ff..1a4af91582d 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -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 swift_ver_location; map 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 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: diff --git a/src/rgw/rgw_rados.cc b/src/rgw/rgw_rados.cc index 77fcb80bf14..a53e403d8dd 100644 --- a/src/rgw/rgw_rados.cc +++ b/src/rgw/rgw_rados.cc @@ -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); } diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index d461f8ba079..b893e82c2c9 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -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 read_all_chunked_input(req_state *s, const uint64_t max_read) { #define READ_CHUNK 4096 diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index 0e5808c5059..6ba34d4eeb2 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -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; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index ae1065160ad..39b380c16fd 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -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; diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index 023a9f0d68d..e782460e673 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -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); -- 2.39.5