+
+void RGWPutBucketObjectLock::pre_exec()
+{
+ rgw_bucket_object_pre_exec(s);
+}
+
+int RGWPutBucketObjectLock::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
+ if (has_s3_resource_tag)
+ rgw_iam_add_buckettags(this, s);
+
+ return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketObjectLockConfiguration);
+}
+
+void RGWPutBucketObjectLock::execute(optional_yield y)
+{
+ if (!s->bucket->get_info().obj_lock_enabled()) {
+ s->err.message = "object lock configuration can't be set if bucket object lock not enabled";
+ ldpp_dout(this, 4) << "ERROR: " << s->err.message << 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(y);
+ 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) {
+ ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+ if (obj_lock.has_rule() && !obj_lock.retention_period_valid()) {
+ s->err.message = "retention period must be a positive integer value";
+ ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
+ op_ret = -ERR_INVALID_RETENTION_PERIOD;
+ return;
+ }
+
+ op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+ if (op_ret < 0) {
+ ldpp_dout(this, 20) << __func__ << "forward_request_to_master returned ret=" << op_ret << dendl;
+ return;
+ }
+
+ op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
+ s->bucket->get_info().obj_lock = obj_lock;
+ op_ret = s->bucket->put_info(this, false, real_time());
+ return op_ret;
+ });
+ return;
+}
+
+void RGWGetBucketObjectLock::pre_exec()
+{
+ rgw_bucket_object_pre_exec(s);
+}
+
+int RGWGetBucketObjectLock::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
+ if (has_s3_resource_tag)
+ rgw_iam_add_buckettags(this, s);
+
+ return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketObjectLockConfiguration);
+}
+
+void RGWGetBucketObjectLock::execute(optional_yield y)
+{
+ if (!s->bucket->get_info().obj_lock_enabled()) {
+ op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
+ return;
+ }
+}
+
+int RGWPutObjRetention::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
+ if (has_s3_existing_tag || has_s3_resource_tag)
+ rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
+
+ if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectRetention)) {
+ return -EACCES;
+ }
+ op_ret = get_params(y);
+ 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(optional_yield y)
+{
+ if (!s->bucket->get_info().obj_lock_enabled()) {
+ s->err.message = "object retention can't be set if bucket object lock not configured";
+ ldpp_dout(this, 4) << "ERROR: " << s->err.message << 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;
+ }
+
+ 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 << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) < ceph_clock_now()) {
+ s->err.message = "the retain-until date must be in the future";
+ ldpp_dout(this, 0) << "ERROR: " << s->err.message << dendl;
+ op_ret = -EINVAL;
+ return;
+ }
+ bufferlist bl;
+ obj_retention.encode(bl);
+
+ //check old retention
+ op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "ERROR: get obj attr error"<< dendl;
+ return;
+ }
+ rgw::sal::Attrs attrs = s->object->get_attrs();
+ auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
+ if (aiter != attrs.end()) {
+ RGWObjectRetention old_obj_retention;
+ try {
+ decode(old_obj_retention, aiter->second);
+ } catch (buffer::error& err) {
+ ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
+ op_ret = -EIO;
+ return;
+ }
+ if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) < ceph::real_clock::to_time_t(old_obj_retention.get_retain_until_date())) {
+ if (old_obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
+ s->err.message = "proposed retain-until date shortens an existing retention period and governance bypass check failed";
+ op_ret = -EACCES;
+ return;
+ }
+ } else if (old_obj_retention.get_mode() == obj_retention.get_mode()) {
+ // ok if retention mode doesn't change
+ } else if (obj_retention.get_mode() == "GOVERNANCE") {
+ s->err.message = "can't change retention mode from COMPLIANCE to GOVERNANCE";
+ op_ret = -EACCES;
+ return;
+ } else if (!bypass_perm || !bypass_governance_mode) {
+ s->err.message = "can't change retention mode from GOVERNANCE without governance bypass";
+ op_ret = -EACCES;
+ return;
+ }
+ }
+
+ op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_OBJECT_RETENTION, bl, s->yield, this);
+
+ return;
+}
+
+int RGWGetObjRetention::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
+ if (has_s3_existing_tag || has_s3_resource_tag)
+ rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
+
+ 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(optional_yield y)
+{
+ if (!s->bucket->get_info().obj_lock_enabled()) {
+ s->err.message = "bucket object lock not configured";
+ ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
+ op_ret = -ERR_INVALID_REQUEST;
+ return;
+ }
+ op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
+ << " ret=" << op_ret << dendl;
+ return;
+ }
+ rgw::sal::Attrs attrs = s->object->get_attrs();
+ 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) {
+ ldpp_dout(this, 0) << __func__ << "decode object retention config failed" << dendl;
+ op_ret = -EIO;
+ return;
+ }
+ return;
+}
+
+int RGWPutObjLegalHold::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
+ if (has_s3_existing_tag || has_s3_resource_tag)
+ rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
+
+ 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(optional_yield y) {
+ if (!s->bucket->get_info().obj_lock_enabled()) {
+ s->err.message = "object legal hold can't be set if bucket object lock not enabled";
+ ldpp_dout(this, 4) << "ERROR: " << s->err.message << 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(y);
+ 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) {
+ ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+ bufferlist bl;
+ obj_legal_hold.encode(bl);
+ //if instance is empty, we should modify the latest object
+ op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_OBJECT_LEGAL_HOLD, bl, s->yield, this);
+ return;
+}
+
+int RGWGetObjLegalHold::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
+ if (has_s3_existing_tag || has_s3_resource_tag)
+ rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
+
+ 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(optional_yield y)
+{
+ if (!s->bucket->get_info().obj_lock_enabled()) {
+ s->err.message = "bucket object lock not configured";
+ ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
+ op_ret = -ERR_INVALID_REQUEST;
+ return;
+ }
+ map<string, bufferlist> attrs;
+ op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
+ << " ret=" << op_ret << dendl;
+ return;
+ }
+ auto aiter = s->object->get_attrs().find(RGW_ATTR_OBJECT_LEGAL_HOLD);
+ if (aiter == s->object->get_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) {
+ ldpp_dout(this, 0) << __func__ << "decode object legal hold config failed" << dendl;
+ op_ret = -EIO;
+ return;
+ }
+ return;
+}
+
+void RGWGetClusterStat::execute(optional_yield y)
+{
+ op_ret = store->cluster_stat(stats_op);
+}
+
+int RGWGetBucketPolicyStatus::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
+ if (has_s3_resource_tag)
+ rgw_iam_add_buckettags(this, s);
+
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicyStatus)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void RGWGetBucketPolicyStatus::execute(optional_yield y)
+{
+ isPublic = (s->iam_policy && rgw::IAM::is_public(*s->iam_policy)) || s->bucket_acl->is_public(this);
+}
+
+int RGWPutBucketPublicAccessBlock::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
+ if (has_s3_resource_tag)
+ rgw_iam_add_buckettags(this, s);
+
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+int RGWPutBucketPublicAccessBlock::get_params(optional_yield y)
+{
+ const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+ std::tie(op_ret, data) = read_all_input(s, max_size, false);
+ return op_ret;
+}
+
+void RGWPutBucketPublicAccessBlock::execute(optional_yield y)
+{
+ RGWXMLDecoder::XMLParser parser;
+ if (!parser.init()) {
+ ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+ op_ret = -EINVAL;
+ return;
+ }
+
+ op_ret = get_params(y);
+ if (op_ret < 0)
+ return;
+
+ if (!parser.parse(data.c_str(), data.length(), 1)) {
+ ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ try {
+ RGWXMLDecoder::decode_xml("PublicAccessBlockConfiguration", access_conf, &parser, true);
+ } catch (RGWXMLDecoder::err &err) {
+ ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
+ return;
+ }
+
+ bufferlist bl;
+ access_conf.encode(bl);
+ op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, &bl] {
+ rgw::sal::Attrs attrs(s->bucket_attrs);
+ attrs[RGW_ATTR_PUBLIC_ACCESS] = bl;
+ return s->bucket->merge_and_store_attrs(this, attrs, s->yield);
+ });
+
+}
+
+int RGWGetBucketPublicAccessBlock::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
+ if (has_s3_resource_tag)
+ rgw_iam_add_buckettags(this, s);
+
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicy)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void RGWGetBucketPublicAccessBlock::execute(optional_yield y)
+{
+ auto attrs = s->bucket_attrs;
+ if (auto aiter = attrs.find(RGW_ATTR_PUBLIC_ACCESS);
+ aiter == attrs.end()) {
+ ldpp_dout(this, 0) << "can't find bucket IAM POLICY attr bucket_name = "
+ << s->bucket_name << dendl;
+ // return the default;
+ return;
+ } else {
+ bufferlist::const_iterator iter{&aiter->second};
+ try {
+ access_conf.decode(iter);
+ } catch (const buffer::error& e) {
+ ldpp_dout(this, 0) << __func__ << "decode access_conf failed" << dendl;
+ op_ret = -EIO;
+ return;
+ }
+ }
+}
+
+
+void RGWDeleteBucketPublicAccessBlock::send_response()
+{
+ if (op_ret) {
+ set_req_state_err(s, op_ret);
+ }
+ dump_errno(s);
+ end_header(s);
+}
+
+int RGWDeleteBucketPublicAccessBlock::verify_permission(optional_yield y)
+{
+ auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
+ if (has_s3_resource_tag)
+ rgw_iam_add_buckettags(this, s);
+
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+void RGWDeleteBucketPublicAccessBlock::execute(optional_yield y)
+{
+ bufferlist data;
+ op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
+ return;
+ }
+
+ op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
+ rgw::sal::Attrs attrs(s->bucket_attrs);
+ attrs.erase(RGW_ATTR_PUBLIC_ACCESS);
+ op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
+ return op_ret;
+ });
+}
+
+int RGWPutBucketEncryption::get_params(optional_yield y)
+{
+ const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+ std::tie(op_ret, data) = read_all_input(s, max_size, false);
+ return op_ret;
+}
+
+int RGWPutBucketEncryption::verify_permission(optional_yield y)
+{
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
+ return -EACCES;
+ }
+ return 0;
+}
+
+void RGWPutBucketEncryption::execute(optional_yield y)
+{
+ RGWXMLDecoder::XMLParser parser;
+ if (!parser.init()) {
+ ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+ op_ret = -EINVAL;
+ return;
+ }
+ op_ret = get_params(y);
+ if (op_ret < 0) {
+ return;
+ }
+ if (!parser.parse(data.c_str(), data.length(), 1)) {
+ ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ try {
+ RGWXMLDecoder::decode_xml("ServerSideEncryptionConfiguration", bucket_encryption_conf, &parser, true);
+ } catch (RGWXMLDecoder::err& err) {
+ ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
+ op_ret = -ERR_MALFORMED_XML;
+ return;
+ }
+
+ op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+ if (op_ret < 0) {
+ ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
+ return;
+ }
+
+ bufferlist conf_bl;
+ bucket_encryption_conf.encode(conf_bl);
+ op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y, &conf_bl] {
+ rgw::sal::Attrs attrs = s->bucket->get_attrs();
+ attrs[RGW_ATTR_BUCKET_ENCRYPTION_POLICY] = conf_bl;
+ return s->bucket->merge_and_store_attrs(this, attrs, y);
+ });
+}
+
+int RGWGetBucketEncryption::verify_permission(optional_yield y)
+{
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketEncryption)) {
+ return -EACCES;
+ }
+ return 0;
+}
+
+void RGWGetBucketEncryption::execute(optional_yield y)
+{
+ const auto& attrs = s->bucket_attrs;
+ if (auto aiter = attrs.find(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
+ aiter == attrs.end()) {
+ ldpp_dout(this, 0) << "can't find BUCKET ENCRYPTION attr for bucket_name = " << s->bucket_name << dendl;
+ op_ret = -ENOENT;
+ s->err.message = "The server side encryption configuration was not found";
+ return;
+ } else {
+ bufferlist::const_iterator iter{&aiter->second};
+ try {
+ bucket_encryption_conf.decode(iter);
+ } catch (const buffer::error& e) {
+ ldpp_dout(this, 0) << __func__ << "decode bucket_encryption_conf failed" << dendl;
+ op_ret = -EIO;
+ return;
+ }
+ }
+}
+
+int RGWDeleteBucketEncryption::verify_permission(optional_yield y)
+{
+ if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
+ return -EACCES;
+ }
+ return 0;
+}
+
+void RGWDeleteBucketEncryption::execute(optional_yield y)
+{
+ bufferlist data;
+ op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+ if (op_ret < 0) {
+ ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
+ return;
+ }
+
+ op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y] {
+ rgw::sal::Attrs attrs = s->bucket->get_attrs();
+ attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
+ attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID);
+ op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
+ return op_ret;
+ });
+}
+
+void rgw_slo_entry::decode_json(JSONObj *obj)
+{
+ JSONDecoder::decode_json("path", path, obj);
+ JSONDecoder::decode_json("etag", etag, obj);
+ JSONDecoder::decode_json("size_bytes", size_bytes, obj);
+};
+