multipart_upload_info upload_info;
upload_info.dest_placement = dest_placement;
+
+ if (obj_legal_hold) {
+ upload_info.obj_legal_hold_exist = true;
+ upload_info.obj_legal_hold = (*obj_legal_hold);
+ }
+ if (obj_retention) {
+ upload_info.obj_retention_exist = true;
+ upload_info.obj_retention = (*obj_retention);
+ }
bufferlist bl;
encode(upload_info, bl);
attrs[RGW_ATTR_ETAG] = etag_bl;
+ rgw_placement_rule* ru;
+ ru = &placement;
+ ret = RadosMultipartUpload::get_info(dpp, y, &ru, &attrs);
+ if (upload_information.obj_retention_exist) {
+ bufferlist obj_retention_bl;
+ upload_information.obj_retention.encode(obj_retention_bl);
+ attrs[RGW_ATTR_OBJECT_RETENTION] = std::move(obj_retention_bl);
+ }
+ if (upload_information.obj_legal_hold_exist) {
+ bufferlist obj_legal_hold_bl;
+ upload_information.obj_legal_hold.encode(obj_legal_hold_bl);
+ attrs[RGW_ATTR_OBJECT_LEGAL_HOLD] = std::move(obj_legal_hold_bl);
+ }
+
if (compressed) {
// write compression attribute to full object
bufferlist tmp;
return -EIO;
}
placement = upload_info.dest_placement;
+ upload_information = upload_info;
*rule = &placement;
return 0;
ceph::real_time mtime;
rgw_placement_rule placement;
RGWObjManifest manifest;
+ multipart_upload_info upload_information;
public:
RadosMultipartUpload(RadosStore* _store, Bucket* _bucket, const std::string& oid,
struct multipart_upload_info
{
rgw_placement_rule dest_placement;
+ //object lock
+ bool obj_retention_exist{false};
+ bool obj_legal_hold_exist{false};
+ RGWObjectRetention obj_retention;
+ RGWObjectLegalHold obj_legal_hold;
void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
+ ENCODE_START(2, 1, bl);
encode(dest_placement, bl);
+ encode(obj_retention_exist, bl);
+ encode(obj_legal_hold_exist, bl);
+ encode(obj_retention, bl);
+ encode(obj_legal_hold, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
- DECODE_START(1, bl);
+ DECODE_START(2, bl);
decode(dest_placement, bl);
+ if (struct_v >= 2) {
+ decode(obj_retention_exist, bl);
+ decode(obj_legal_hold_exist, bl);
+ decode(obj_retention, bl);
+ decode(obj_legal_hold, bl);
+ } else {
+ obj_retention_exist = false;
+ obj_legal_hold_exist = false;
+ }
DECODE_FINISH(bl);
}
{
RGWObjState* astate = nullptr;
bool check_obj_lock = s->object->have_instance() && s->bucket->get_info().obj_lock_enabled();
-
op_ret = s->object->get_obj_state(this, &astate, s->yield, true);
if (op_ret < 0) {
if (need_object_expiration() || multipart_delete) {
// ignore return value from get_obj_attrs in all other cases
op_ret = 0;
-
if (check_obj_lock) {
ceph_assert(astate);
int object_lock_response = verify_object_lock(this, astate->attrset, bypass_perm, bypass_governance_mode);
std::unique_ptr<rgw::sal::MultipartUpload> upload;
upload = s->bucket->get_multipart_upload(s->object->get_name(),
upload_id);
+ upload->obj_legal_hold = obj_legal_hold;
+ upload->obj_retention = obj_retention;
op_ret = upload->init(this, s->yield, s->owner, s->dest_placement, attrs);
if (op_ret == 0) {
rgw_iam_add_crypt_attrs(s->env, s->info.crypt_attribute_map);
if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
+ if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) {
+ auto r = eval_identity_or_session_policies(this, s->iam_user_policies, s->env,
+ rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name()));
+ if (r == Effect::Deny) {
+ bypass_perm = false;
+ } else if (r == Effect::Pass && s->iam_policy) {
+ ARN arn(s->bucket->get_key(), s->object->get_name());
+ r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention, arn);
+ if (r == Effect::Deny) {
+ bypass_perm = false;
+ }
+ } else if (r == Effect::Pass && !s->session_policies.empty()) {
+ r = eval_identity_or_session_policies(this, s->session_policies, s->env,
+ rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name()));
+ if (r == Effect::Deny) {
+ bypass_perm = false;
+ }
+ } else if (r == Effect::Pass) {
+ bypass_perm = false;
+ }
+ bypass_governance_mode &= bypass_perm;
+ }
auto identity_policy_res = eval_identity_or_session_policies(this, s->iam_user_policies, s->env,
rgw::IAM::s3PutObject,
s->object->get_obj());
RGWAccessControlPolicy policy;
ceph::real_time mtime;
jspan_ptr multipart_trace;
+ //object lock
+ std::optional<RGWObjectRetention> obj_retention = std::nullopt;
+ std::optional<RGWObjectLegalHold> obj_legal_hold = std::nullopt;
public:
RGWInitMultipart() {}
std::unique_ptr<rgw::sal::Notification> res;
std::unique_ptr<rgw::sal::Object> meta_obj;
off_t ofs = 0;
+ //object lock
+ bool bypass_perm = true;
+ bool bypass_governance_mode = false;
public:
RGWCompleteMultipart() {}
std::tie(op_ret, data) = read_all_input(s, max_size);
if (op_ret < 0)
return op_ret;
+
+ 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 = boost::algorithm::iequals(bypass_gov_decoded, "true");
+ }
return 0;
}
return ret;
}
- return create_s3_policy(s, driver, policy, s->owner);
+ ret = create_s3_policy(s, driver, policy, s->owner);
+ if (ret < 0)
+ return ret;
+
+ //handle object lock
+ auto obj_lock_mode_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_MODE");
+ auto obj_lock_date_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE");
+ auto obj_legal_hold_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_LEGAL_HOLD");
+ if (obj_lock_mode_str && obj_lock_date_str) {
+ boost::optional<ceph::real_time> date = ceph::from_iso_8601(obj_lock_date_str);
+ if (boost::none == date || ceph::real_clock::to_time_t(*date) <= ceph_clock_now()) {
+ ldpp_dout(this,0) << "invalid x-amz-object-lock-retain-until-date value" << dendl;
+ return -EINVAL;;
+ }
+ if (strcmp(obj_lock_mode_str, "GOVERNANCE") != 0 && strcmp(obj_lock_mode_str, "COMPLIANCE") != 0) {
+ ldpp_dout(this,0) << "invalid x-amz-object-lock-mode value" << dendl;
+ return -EINVAL;
+ }
+ obj_retention = RGWObjectRetention(obj_lock_mode_str, *date);
+ } else if ((obj_lock_mode_str && !obj_lock_date_str) || (!obj_lock_mode_str && obj_lock_date_str)) {
+ ldpp_dout(this,0) << "need both x-amz-object-lock-mode and x-amz-object-lock-retain-until-date " << dendl;
+ return -EINVAL;
+ }
+ if (obj_legal_hold_str) {
+ if (strcmp(obj_legal_hold_str, "ON") != 0 && strcmp(obj_legal_hold_str, "OFF") != 0) {
+ ldpp_dout(this,0) << "invalid x-amz-object-lock-legal-hold value" << dendl;
+ return -EINVAL;
+ }
+ obj_legal_hold = RGWObjectLegalHold(obj_legal_hold_str);
+ }
+ if (!s->bucket->get_info().obj_lock_enabled() && (obj_retention || obj_legal_hold)) {
+ ldpp_dout(this, 0) << "ERROR: object retention or legal hold can't be set if bucket object lock not configured" << dendl;
+ return -ERR_INVALID_REQUEST;
+ }
+
+ return 0;
}
void RGWInitMultipart_ObjStore_S3::send_response()
*/
class MultipartUpload {
public:
+ //object lock
+ std::optional<RGWObjectRetention> obj_retention = std::nullopt;
+ std::optional<RGWObjectLegalHold> obj_legal_hold = std::nullopt;
+
MultipartUpload() = default;
virtual ~MultipartUpload() = default;