1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
8 #include "common/ceph_crypto.h"
9 #include "common/Formatter.h"
10 #include "common/utf8.h"
11 #include "common/ceph_json.h"
12 #include "common/safe_io.h"
13 #include "common/backport14.h"
14 #include <boost/algorithm/string.hpp>
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/utility/string_view.hpp>
19 #include "rgw_rest_s3.h"
20 #include "rgw_rest_s3website.h"
21 #include "rgw_auth_s3.h"
23 #include "rgw_policy_s3.h"
26 #include "rgw_cors_s3.h"
27 #include "rgw_tag_s3.h"
29 #include "rgw_client_io.h"
31 #include "rgw_keystone.h"
32 #include "rgw_auth_keystone.h"
33 #include "rgw_auth_registry.h"
35 #include "rgw_es_query.h"
37 #include <typeinfo> // for 'typeid'
40 #include "rgw_token.h"
41 #include "rgw_rest_role.h"
42 #include "rgw_crypt.h"
43 #include "rgw_crypt_sanitize.h"
45 #include "include/assert.h"
47 #define dout_context g_ceph_context
48 #define dout_subsys ceph_subsys_rgw
51 using namespace ceph::crypto
;
55 void list_all_buckets_start(struct req_state
*s
)
57 s
->formatter
->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3
);
60 void list_all_buckets_end(struct req_state
*s
)
62 s
->formatter
->close_section();
65 void dump_bucket(struct req_state
*s
, RGWBucketEnt
& obj
)
67 s
->formatter
->open_object_section("Bucket");
68 s
->formatter
->dump_string("Name", obj
.bucket
.name
);
69 dump_time(s
, "CreationDate", &obj
.creation_time
);
70 s
->formatter
->close_section();
73 void rgw_get_errno_s3(rgw_http_error
*e
, int err_no
)
75 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
);
77 if (r
!= rgw_http_s3_errors
.end()) {
78 e
->http_ret
= r
->second
.first
;
79 e
->s3_code
= r
->second
.second
;
82 e
->s3_code
= "UnknownError";
86 struct response_attr_param
{
88 const char *http_attr
;
91 static struct response_attr_param resp_attr_params
[] = {
92 {"response-content-type", "Content-Type"},
93 {"response-content-language", "Content-Language"},
94 {"response-expires", "Expires"},
95 {"response-cache-control", "Cache-Control"},
96 {"response-content-disposition", "Content-Disposition"},
97 {"response-content-encoding", "Content-Encoding"},
101 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist
& bl
, off_t bl_ofs
, off_t bl_len
) {
102 map
<string
, bufferlist
>::iterator iter
;
103 iter
= attrs
.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION
);
104 if (iter
!= attrs
.end()) {
105 bufferlist
&bl
= iter
->second
;
106 s
->redirect
= string(bl
.c_str(), bl
.length());
107 s
->err
.http_ret
= 301;
108 ldout(s
->cct
, 20) << __CEPH_ASSERT_FUNCTION
<< " redirecting per x-amz-website-redirect-location=" << s
->redirect
<< dendl
;
109 op_ret
= -ERR_WEBSITE_REDIRECT
;
110 set_req_state_err(s
, op_ret
);
112 dump_content_length(s
, 0);
113 dump_redirect(s
, s
->redirect
);
117 return RGWGetObj_ObjStore_S3::send_response_data(bl
, bl_ofs
, bl_len
);
121 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
123 return RGWGetObj_ObjStore_S3::send_response_data_error();
126 int RGWGetObj_ObjStore_S3::get_params()
128 // for multisite sync requests, only read the slo manifest itself, rather than
129 // all of the data from its parts. the parts will sync as separate objects
130 skip_manifest
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"sync-manifest");
132 // multisite sync requests should fetch encrypted data, along with the
133 // attributes needed to support decryption on the other zone
134 if (s
->system_request
) {
135 skip_decrypt
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"skip-decrypt");
138 return RGWGetObj_ObjStore::get_params();
141 int RGWGetObj_ObjStore_S3::send_response_data_error()
144 return send_response_data(bl
, 0 , 0);
148 int decode_attr_bl_single_value(map
<string
, bufferlist
>& attrs
, const char *attr_name
, T
*result
, T def_val
)
150 map
<string
, bufferlist
>::iterator iter
= attrs
.find(attr_name
);
151 if (iter
== attrs
.end()) {
155 bufferlist
& bl
= iter
->second
;
156 if (bl
.length() == 0) {
160 bufferlist::iterator bliter
= bl
.begin();
162 ::decode(*result
, bliter
);
163 } catch (buffer::error
& err
) {
169 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist
& bl
, off_t bl_ofs
,
172 const char *content_type
= NULL
;
173 string content_type_str
;
174 map
<string
, string
> response_attrs
;
175 map
<string
, string
>::iterator riter
;
176 bufferlist metadata_bl
;
181 if (custom_http_ret
) {
182 set_req_state_err(s
, 0);
183 dump_errno(s
, custom_http_ret
);
185 set_req_state_err(s
, (partial_content
&& !op_ret
) ? STATUS_PARTIAL_CONTENT
194 dump_range(s
, start
, end
, s
->obj_size
);
196 if (s
->system_request
&&
197 s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"prepend-metadata")) {
199 dump_header(s
, "Rgwx-Object-Size", (long long)total_len
);
203 * in this case, we're not returning the object's content, only the prepended
209 /* JSON encode object metadata */
211 jf
.open_object_section("obj_metadata");
212 encode_json("attrs", attrs
, &jf
);
214 encode_json("mtime", ut
, &jf
);
218 metadata_bl
.append(ss
.str());
219 dump_header(s
, "Rgwx-Embedded-Metadata-Len", metadata_bl
.length());
220 total_len
+= metadata_bl
.length();
223 if (s
->system_request
&& !real_clock::is_zero(lastmod
)) {
224 /* we end up dumping mtime in two different methods, a bit redundant */
225 dump_epoch_header(s
, "Rgwx-Mtime", lastmod
);
227 int r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_PG_VER
, &pg_ver
, (uint64_t)0);
229 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
231 dump_header(s
, "Rgwx-Obj-PG-Ver", pg_ver
);
233 uint32_t source_zone_short_id
= 0;
234 r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_SOURCE_ZONE
, &source_zone_short_id
, (uint32_t)0);
236 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
238 if (source_zone_short_id
!= 0) {
239 dump_header(s
, "Rgwx-Source-Zone-Short-Id", source_zone_short_id
);
243 for (auto &it
: crypt_http_responses
)
244 dump_header(s
, it
.first
, it
.second
);
246 dump_content_length(s
, total_len
);
247 dump_last_modified(s
, lastmod
);
248 if (!version_id
.empty()) {
249 dump_header(s
, "x-amz-version-id", version_id
);
254 if (! lo_etag
.empty()) {
255 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
256 * legit to perform GET on them through S3 API. In such situation,
257 * a client should receive the composited content with corresponding
259 dump_etag(s
, lo_etag
);
261 auto iter
= attrs
.find(RGW_ATTR_ETAG
);
262 if (iter
!= attrs
.end()) {
263 dump_etag(s
, iter
->second
);
267 for (struct response_attr_param
*p
= resp_attr_params
; p
->param
; p
++) {
269 string val
= s
->info
.args
.get(p
->param
, &exists
);
271 if (strcmp(p
->param
, "response-content-type") != 0) {
272 response_attrs
[p
->http_attr
] = val
;
274 content_type_str
= val
;
275 content_type
= content_type_str
.c_str();
280 for (auto iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
281 const char *name
= iter
->first
.c_str();
282 map
<string
, string
>::iterator aiter
= rgw_to_http_attrs
.find(name
);
283 if (aiter
!= rgw_to_http_attrs
.end()) {
284 if (response_attrs
.count(aiter
->second
) == 0) {
285 /* Was not already overridden by a response param. */
286 response_attrs
[aiter
->second
] = iter
->second
.c_str();
288 } else if (iter
->first
.compare(RGW_ATTR_CONTENT_TYPE
) == 0) {
289 /* Special handling for content_type. */
291 content_type
= iter
->second
.c_str();
293 } else if (strcmp(name
, RGW_ATTR_SLO_UINDICATOR
) == 0) {
294 // this attr has an extra length prefix from ::encode() in prior versions
295 dump_header(s
, "X-Object-Meta-Static-Large-Object", "True");
296 } else if (strncmp(name
, RGW_ATTR_META_PREFIX
,
297 sizeof(RGW_ATTR_META_PREFIX
)-1) == 0) {
298 /* User custom metadata. */
299 name
+= sizeof(RGW_ATTR_PREFIX
) - 1;
300 dump_header(s
, name
, iter
->second
);
301 } else if (iter
->first
.compare(RGW_ATTR_TAGS
) == 0) {
304 bufferlist::iterator it
= iter
->second
.begin();
306 } catch (buffer::error
&err
) {
307 ldout(s
->cct
,0) << "Error caught buffer::error couldn't decode TagSet " << dendl
;
309 dump_header(s
, RGW_AMZ_TAG_COUNT
, obj_tags
.count());
315 for (riter
= response_attrs
.begin(); riter
!= response_attrs
.end();
317 dump_header(s
, riter
->first
, riter
->second
);
320 if (op_ret
== -ERR_NOT_MODIFIED
) {
324 content_type
= "binary/octet-stream";
326 end_header(s
, this, content_type
);
329 if (metadata_bl
.length()) {
330 dump_body(s
, metadata_bl
);
335 if (get_data
&& !op_ret
) {
336 int r
= dump_body(s
, bl
.c_str() + bl_ofs
, bl_len
);
344 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr
<RGWGetDataCB
> *filter
, RGWGetDataCB
* cb
, bufferlist
* manifest_bl
)
346 if (skip_decrypt
) { // bypass decryption for multisite sync requests
351 std::unique_ptr
<BlockCrypt
> block_crypt
;
352 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses
);
354 if (block_crypt
!= nullptr) {
355 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
356 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
358 if (manifest_bl
!= nullptr) {
359 res
= f
->read_manifest(*manifest_bl
);
361 *filter
= std::move(f
);
370 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist
& bl
)
373 end_header(s
, this, "application/xml");
376 s
->formatter
->open_object_section_in_ns("Tagging", XMLNS_AWS_S3
);
377 s
->formatter
->open_object_section("TagSet");
379 RGWObjTagSet_S3 tagset
;
380 bufferlist::iterator iter
= bl
.begin();
383 } catch (buffer::error
& err
) {
384 ldout(s
->cct
,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl
;
388 tagset
.dump_xml(s
->formatter
);
390 s
->formatter
->close_section();
391 s
->formatter
->close_section();
392 rgw_flush_formatter_and_reset(s
, s
->formatter
);
396 int RGWPutObjTags_ObjStore_S3::get_params()
398 RGWObjTagsXMLParser parser
;
407 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
408 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
413 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
415 if (!parser
.parse(data
, len
, 1)) {
416 return -ERR_MALFORMED_XML
;
419 RGWObjTagSet_S3
*obj_tags_s3
;
420 RGWObjTagging_S3
*tagging
;
422 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
423 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
425 return -ERR_MALFORMED_XML
;
429 r
= obj_tags_s3
->rebuild(obj_tags
);
433 obj_tags
.encode(tags_bl
);
434 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
439 void RGWPutObjTags_ObjStore_S3::send_response()
442 set_req_state_err(s
, op_ret
);
444 end_header(s
, this, "application/xml");
449 void RGWDeleteObjTags_ObjStore_S3::send_response()
455 r
= STATUS_NO_CONTENT
;
457 set_req_state_err(s
, r
);
462 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets
)
465 set_req_state_err(s
, op_ret
);
468 end_header(s
, NULL
, "application/xml");
471 list_all_buckets_start(s
);
472 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
473 s
->formatter
->open_array_section("Buckets");
478 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets
& buckets
)
483 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
484 map
<string
, RGWBucketEnt
>::iterator iter
;
486 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
487 RGWBucketEnt obj
= iter
->second
;
490 rgw_flush_formatter(s
, s
->formatter
);
493 void RGWListBuckets_ObjStore_S3::send_response_end()
496 s
->formatter
->close_section();
497 list_all_buckets_end(s
);
498 rgw_flush_formatter_and_reset(s
, s
->formatter
);
502 int RGWGetUsage_ObjStore_S3::get_params()
504 start_date
= s
->info
.args
.get("start-date");
505 end_date
= s
->info
.args
.get("end-date");
509 static void dump_usage_categories_info(Formatter
*formatter
, const rgw_usage_log_entry
& entry
, map
<string
, bool> *categories
)
511 formatter
->open_array_section("categories");
512 map
<string
, rgw_usage_data
>::const_iterator uiter
;
513 for (uiter
= entry
.usage_map
.begin(); uiter
!= entry
.usage_map
.end(); ++uiter
) {
514 if (categories
&& !categories
->empty() && !categories
->count(uiter
->first
))
516 const rgw_usage_data
& usage
= uiter
->second
;
517 formatter
->open_object_section("Entry");
518 formatter
->dump_string("Category", uiter
->first
);
519 formatter
->dump_int("BytesSent", usage
.bytes_sent
);
520 formatter
->dump_int("BytesReceived", usage
.bytes_received
);
521 formatter
->dump_int("Ops", usage
.ops
);
522 formatter
->dump_int("SuccessfulOps", usage
.successful_ops
);
523 formatter
->close_section(); // Entry
525 formatter
->close_section(); // Category
528 static void dump_usage_bucket_info(Formatter
*formatter
, const std::string
& name
, const cls_user_bucket_entry
& entry
)
530 formatter
->open_object_section("Entry");
531 formatter
->dump_string("Bucket", name
);
532 formatter
->dump_int("Bytes", entry
.size
);
533 formatter
->dump_int("Bytes_Rounded", entry
.size_rounded
);
534 formatter
->close_section(); // entry
537 void RGWGetUsage_ObjStore_S3::send_response()
540 set_req_state_err(s
, op_ret
);
543 end_header(s
, this, "application/xml");
548 Formatter
*formatter
= s
->formatter
;
550 bool user_section_open
= false;
552 formatter
->open_object_section("Usage");
553 if (show_log_entries
) {
554 formatter
->open_array_section("Entries");
556 map
<rgw_user_bucket
, rgw_usage_log_entry
>::iterator iter
;
557 for (iter
= usage
.begin(); iter
!= usage
.end(); ++iter
) {
558 const rgw_user_bucket
& ub
= iter
->first
;
559 const rgw_usage_log_entry
& entry
= iter
->second
;
561 if (show_log_entries
) {
562 if (ub
.user
.compare(last_owner
) != 0) {
563 if (user_section_open
) {
564 formatter
->close_section();
565 formatter
->close_section();
567 formatter
->open_object_section("User");
568 formatter
->dump_string("Owner", ub
.user
);
569 formatter
->open_array_section("Buckets");
570 user_section_open
= true;
571 last_owner
= ub
.user
;
573 formatter
->open_object_section("Bucket");
574 formatter
->dump_string("Bucket", ub
.bucket
);
575 utime_t
ut(entry
.epoch
, 0);
576 ut
.gmtime(formatter
->dump_stream("Time"));
577 formatter
->dump_int("Epoch", entry
.epoch
);
578 dump_usage_categories_info(formatter
, entry
, &categories
);
579 formatter
->close_section(); // bucket
582 summary_map
[ub
.user
].aggregate(entry
, &categories
);
585 if (show_log_entries
) {
586 if (user_section_open
) {
587 formatter
->close_section(); // buckets
588 formatter
->close_section(); //user
590 formatter
->close_section(); // entries
594 formatter
->open_array_section("Summary");
595 map
<string
, rgw_usage_log_entry
>::iterator siter
;
596 for (siter
= summary_map
.begin(); siter
!= summary_map
.end(); ++siter
) {
597 const rgw_usage_log_entry
& entry
= siter
->second
;
598 formatter
->open_object_section("User");
599 formatter
->dump_string("User", siter
->first
);
600 dump_usage_categories_info(formatter
, entry
, &categories
);
601 rgw_usage_data total_usage
;
602 entry
.sum(total_usage
, categories
);
603 formatter
->open_object_section("Total");
604 formatter
->dump_int("BytesSent", total_usage
.bytes_sent
);
605 formatter
->dump_int("BytesReceived", total_usage
.bytes_received
);
606 formatter
->dump_int("Ops", total_usage
.ops
);
607 formatter
->dump_int("SuccessfulOps", total_usage
.successful_ops
);
608 formatter
->close_section(); // total
609 formatter
->close_section(); // user
612 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
613 formatter
->open_object_section("Stats");
616 formatter
->dump_int("TotalBytes", header
.stats
.total_bytes
);
617 formatter
->dump_int("TotalBytesRounded", header
.stats
.total_bytes_rounded
);
618 formatter
->dump_int("TotalEntries", header
.stats
.total_entries
);
620 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
621 formatter
->close_section(); //Stats
624 formatter
->close_section(); // summary
627 formatter
->open_array_section("CapacityUsed");
628 formatter
->open_object_section("User");
629 formatter
->open_array_section("Buckets");
630 for (const auto& biter
: buckets_usage
) {
631 const cls_user_bucket_entry
& entry
= biter
.second
;
632 dump_usage_bucket_info(formatter
, biter
.first
, entry
);
634 formatter
->close_section(); // Buckets
635 formatter
->close_section(); // User
636 formatter
->close_section(); // CapacityUsed
638 formatter
->close_section(); // usage
639 rgw_flush_formatter_and_reset(s
, s
->formatter
);
642 int RGWListBucket_ObjStore_S3::get_params()
644 list_versions
= s
->info
.args
.exists("versions");
645 prefix
= s
->info
.args
.get("prefix");
646 if (!list_versions
) {
647 marker
= s
->info
.args
.get("marker");
649 marker
.name
= s
->info
.args
.get("key-marker");
650 marker
.instance
= s
->info
.args
.get("version-id-marker");
652 max_keys
= s
->info
.args
.get("max-keys");
653 op_ret
= parse_max_keys();
657 delimiter
= s
->info
.args
.get("delimiter");
658 encoding_type
= s
->info
.args
.get("encoding-type");
659 if (s
->system_request
) {
660 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
661 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
664 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
666 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
670 shard_id
= s
->bucket_instance_shard_id
;
676 void RGWListBucket_ObjStore_S3::send_versioned_response()
678 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
679 if (!s
->bucket_tenant
.empty())
680 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
681 s
->formatter
->dump_string("Name", s
->bucket_name
);
682 s
->formatter
->dump_string("Prefix", prefix
);
683 s
->formatter
->dump_string("KeyMarker", marker
.name
);
684 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
685 if (is_truncated
&& !next_marker
.empty()) {
686 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
687 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
689 s
->formatter
->dump_int("MaxKeys", max
);
690 if (!delimiter
.empty())
691 s
->formatter
->dump_string("Delimiter", delimiter
);
693 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
696 bool encode_key
= false;
697 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
698 s
->formatter
->dump_string("EncodingType", "url");
703 if (objs_container
) {
704 s
->formatter
->open_array_section("Entries");
707 vector
<rgw_bucket_dir_entry
>::iterator iter
;
708 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
709 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
711 s
->formatter
->open_object_section(section_name
);
712 if (objs_container
) {
713 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
715 rgw_obj_key
key(iter
->key
);
718 url_encode(key
.name
, key_name
);
719 s
->formatter
->dump_string("Key", key_name
);
721 s
->formatter
->dump_string("Key", key
.name
);
723 string version_id
= key
.instance
;
724 if (version_id
.empty()) {
727 if (s
->system_request
) {
728 if (iter
->versioned_epoch
> 0) {
729 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
731 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
732 utime_t
ut(iter
->meta
.mtime
);
733 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
735 s
->formatter
->dump_string("VersionId", version_id
);
736 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
737 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
738 if (!iter
->is_delete_marker()) {
739 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
740 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
741 s
->formatter
->dump_string("StorageClass", "STANDARD");
743 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
744 s
->formatter
->close_section();
746 if (objs_container
) {
747 s
->formatter
->close_section();
750 if (!common_prefixes
.empty()) {
751 map
<string
, bool>::iterator pref_iter
;
752 for (pref_iter
= common_prefixes
.begin();
753 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
754 s
->formatter
->open_array_section("CommonPrefixes");
755 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
756 s
->formatter
->close_section();
760 s
->formatter
->close_section();
761 rgw_flush_formatter_and_reset(s
, s
->formatter
);
764 void RGWListBucket_ObjStore_S3::send_response()
767 set_req_state_err(s
, op_ret
);
770 end_header(s
, this, "application/xml");
776 send_versioned_response();
780 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
781 if (!s
->bucket_tenant
.empty())
782 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
783 s
->formatter
->dump_string("Name", s
->bucket_name
);
784 s
->formatter
->dump_string("Prefix", prefix
);
785 s
->formatter
->dump_string("Marker", marker
.name
);
786 if (is_truncated
&& !next_marker
.empty())
787 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
788 s
->formatter
->dump_int("MaxKeys", max
);
789 if (!delimiter
.empty())
790 s
->formatter
->dump_string("Delimiter", delimiter
);
792 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
795 bool encode_key
= false;
796 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
797 s
->formatter
->dump_string("EncodingType", "url");
802 vector
<rgw_bucket_dir_entry
>::iterator iter
;
803 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
804 rgw_obj_key
key(iter
->key
);
805 s
->formatter
->open_array_section("Contents");
808 url_encode(key
.name
, key_name
);
809 s
->formatter
->dump_string("Key", key_name
);
811 s
->formatter
->dump_string("Key", key
.name
);
813 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
814 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
815 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
816 s
->formatter
->dump_string("StorageClass", "STANDARD");
817 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
818 if (s
->system_request
) {
819 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
821 s
->formatter
->close_section();
823 if (!common_prefixes
.empty()) {
824 map
<string
, bool>::iterator pref_iter
;
825 for (pref_iter
= common_prefixes
.begin();
826 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
827 s
->formatter
->open_array_section("CommonPrefixes");
828 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
829 s
->formatter
->close_section();
833 s
->formatter
->close_section();
834 rgw_flush_formatter_and_reset(s
, s
->formatter
);
837 void RGWGetBucketLogging_ObjStore_S3::send_response()
840 end_header(s
, this, "application/xml");
843 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
844 s
->formatter
->close_section();
845 rgw_flush_formatter_and_reset(s
, s
->formatter
);
848 void RGWGetBucketLocation_ObjStore_S3::send_response()
854 RGWZoneGroup zonegroup
;
857 int ret
= store
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
859 api_name
= zonegroup
.api_name
;
861 if (s
->bucket_info
.zonegroup
!= "default") {
862 api_name
= s
->bucket_info
.zonegroup
;
866 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
867 "%s", api_name
.c_str());
868 rgw_flush_formatter_and_reset(s
, s
->formatter
);
871 void RGWGetBucketVersioning_ObjStore_S3::send_response()
874 end_header(s
, this, "application/xml");
877 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
879 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
880 s
->formatter
->dump_string("Status", status
);
882 s
->formatter
->close_section();
883 rgw_flush_formatter_and_reset(s
, s
->formatter
);
886 class RGWSetBucketVersioningParser
: public RGWXMLParser
888 XMLObj
*alloc_obj(const char *el
) override
{
893 RGWSetBucketVersioningParser() {}
894 ~RGWSetBucketVersioningParser() override
{}
896 int get_versioning_status(bool *status
) {
897 XMLObj
*config
= find_first("VersioningConfiguration");
903 XMLObj
*field
= config
->find_first("Status");
907 string
& s
= field
->get_data();
909 if (stringcasecmp(s
, "Enabled") == 0) {
911 } else if (stringcasecmp(s
, "Suspended") != 0) {
919 int RGWSetBucketVersioning_ObjStore_S3::get_params()
921 char *data
= nullptr;
924 rgw_rest_read_all_input(s
, &data
, &len
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
929 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
931 r
= do_aws4_auth_completion();
936 RGWSetBucketVersioningParser parser
;
938 if (!parser
.init()) {
939 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
944 if (!parser
.parse(data
, len
, 1)) {
945 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
950 if (!store
->is_meta_master()) {
951 /* only need to keep this data around if we're not meta master */
952 in_data
.append(data
, len
);
955 r
= parser
.get_versioning_status(&enable_versioning
);
960 void RGWSetBucketVersioning_ObjStore_S3::send_response()
963 set_req_state_err(s
, op_ret
);
968 int RGWSetBucketWebsite_ObjStore_S3::get_params()
970 char *data
= nullptr;
972 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
973 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
979 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
981 r
= do_aws4_auth_completion();
986 bufferptr
in_ptr(data
, len
);
987 in_data
.append(in_ptr
);
989 RGWXMLDecoder::XMLParser parser
;
990 if (!parser
.init()) {
991 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
995 if (!parser
.parse(data
, len
, 1)) {
996 string
str(data
, len
);
997 ldout(s
->cct
, 5) << "failed to parse xml: " << str
<< dendl
;
1002 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
1003 } catch (RGWXMLDecoder::err
& err
) {
1004 string
str(data
, len
);
1005 ldout(s
->cct
, 5) << "unexpected xml: " << str
<< dendl
;
1012 void RGWSetBucketWebsite_ObjStore_S3::send_response()
1015 set_req_state_err(s
, op_ret
);
1020 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
1023 op_ret
= STATUS_NO_CONTENT
;
1025 set_req_state_err(s
, op_ret
);
1030 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1033 set_req_state_err(s
, op_ret
);
1035 end_header(s
, this, "application/xml");
1042 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
1044 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
1045 conf
.dump_xml(s
->formatter
);
1046 s
->formatter
->close_section(); // WebsiteConfiguration
1047 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1050 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
1052 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
1053 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
1056 void RGWStatBucket_ObjStore_S3::send_response()
1059 dump_bucket_metadata(s
, bucket
);
1062 set_req_state_err(s
, op_ret
);
1065 end_header(s
, this);
1069 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
1070 RGWAccessControlPolicy_S3
& s3policy
,
1073 if (s
->has_acl_header
) {
1074 if (!s
->canned_acl
.empty())
1075 return -ERR_INVALID_REQUEST
;
1077 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
1080 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
1083 class RGWLocationConstraint
: public XMLObj
1086 RGWLocationConstraint() {}
1087 ~RGWLocationConstraint() override
{}
1088 bool xml_end(const char *el
) override
{
1092 location_constraint
= get_data();
1097 string location_constraint
;
1100 class RGWCreateBucketConfig
: public XMLObj
1103 RGWCreateBucketConfig() {}
1104 ~RGWCreateBucketConfig() override
{}
1107 class RGWCreateBucketParser
: public RGWXMLParser
1109 XMLObj
*alloc_obj(const char *el
) override
{
1114 RGWCreateBucketParser() {}
1115 ~RGWCreateBucketParser() override
{}
1117 bool get_location_constraint(string
& zone_group
) {
1118 XMLObj
*config
= find_first("CreateBucketConfiguration");
1122 XMLObj
*constraint
= config
->find_first("LocationConstraint");
1126 zone_group
= constraint
->get_data();
1132 int RGWCreateBucket_ObjStore_S3::get_params()
1134 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1136 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1143 char *data
= nullptr;
1145 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1146 op_ret
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
1148 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1151 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
1153 const int auth_ret
= do_aws4_auth_completion();
1158 bufferptr
in_ptr(data
, len
);
1159 in_data
.append(in_ptr
);
1162 RGWCreateBucketParser parser
;
1164 if (!parser
.init()) {
1165 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1169 bool success
= parser
.parse(data
, len
, 1);
1170 ldout(s
->cct
, 20) << "create bucket input data=" << data
<< dendl
;
1173 ldout(s
->cct
, 0) << "failed to parse input: " << data
<< dendl
;
1177 if (!parser
.get_location_constraint(location_constraint
)) {
1178 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1182 ldout(s
->cct
, 10) << "create bucket location constraint: "
1183 << location_constraint
<< dendl
;
1186 size_t pos
= location_constraint
.find(':');
1187 if (pos
!= string::npos
) {
1188 placement_rule
= location_constraint
.substr(pos
+ 1);
1189 location_constraint
= location_constraint
.substr(0, pos
);
1195 void RGWCreateBucket_ObjStore_S3::send_response()
1197 if (op_ret
== -ERR_BUCKET_EXISTS
)
1200 set_req_state_err(s
, op_ret
);
1207 if (s
->system_request
) {
1208 JSONFormatter f
; /* use json formatter for system requests output */
1210 f
.open_object_section("info");
1211 encode_json("entry_point_object_ver", ep_objv
, &f
);
1212 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1213 encode_json("bucket_info", info
, &f
);
1215 rgw_flush_formatter_and_reset(s
, &f
);
1219 void RGWDeleteBucket_ObjStore_S3::send_response()
1223 r
= STATUS_NO_CONTENT
;
1225 set_req_state_err(s
, r
);
1227 end_header(s
, this);
1229 if (s
->system_request
) {
1230 JSONFormatter f
; /* use json formatter for system requests output */
1232 f
.open_object_section("info");
1233 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1235 rgw_flush_formatter_and_reset(s
, &f
);
1239 int RGWPutObj_ObjStore_S3::get_params()
1242 return -ERR_LENGTH_REQUIRED
;
1244 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
1245 map
<string
, bufferlist
> src_attrs
;
1249 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1250 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1256 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1257 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1258 copy_source
= url_decode(s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE", ""));
1259 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1261 /* handle x-amz-copy-source */
1262 boost::string_view
cs_view(copy_source
);
1263 if (! cs_view
.empty()) {
1264 if (cs_view
[0] == '/')
1265 cs_view
.remove_prefix(1);
1266 copy_source_bucket_name
= cs_view
.to_string();
1267 pos
= copy_source_bucket_name
.find("/");
1268 if (pos
== std::string::npos
) {
1270 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1273 copy_source_object_name
=
1274 copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1275 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1276 #define VERSION_ID_STR "?versionId="
1277 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1278 if (pos
== std::string::npos
) {
1279 copy_source_object_name
= url_decode(copy_source_object_name
);
1281 copy_source_version_id
=
1282 copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1283 copy_source_object_name
=
1284 url_decode(copy_source_object_name
.substr(0, pos
));
1286 pos
= copy_source_bucket_name
.find(":");
1287 if (pos
== std::string::npos
) {
1288 copy_source_tenant_name
= s
->src_tenant_name
;
1290 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1291 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1292 if (copy_source_bucket_name
.empty()) {
1294 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1298 ret
= store
->get_bucket_info(obj_ctx
,
1299 copy_source_tenant_name
,
1300 copy_source_bucket_name
,
1301 copy_source_bucket_info
,
1304 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1308 /* handle x-amz-copy-source-range */
1310 if (copy_source_range
) {
1311 string range
= copy_source_range
;
1312 pos
= range
.find("=");
1313 if (pos
== std::string::npos
) {
1315 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1318 range
= range
.substr(pos
+ 1);
1319 pos
= range
.find("-");
1320 if (pos
== std::string::npos
) {
1322 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1325 string first
= range
.substr(0, pos
);
1326 string last
= range
.substr(pos
+ 1);
1327 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1328 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1333 /* handle object tagging */
1334 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
1336 obj_tags
= ceph::make_unique
<RGWObjTags
>();
1337 ret
= obj_tags
->set_from_string(tag_str
);
1339 ldout(s
->cct
,0) << "setting obj tags failed with " << ret
<< dendl
;
1340 if (ret
== -ERR_INVALID_TAG
){
1341 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
1348 return RGWPutObj_ObjStore::get_params();
1351 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1353 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1355 const int ret_auth
= do_aws4_auth_completion();
1364 static int get_success_retcode(int code
)
1368 return STATUS_CREATED
;
1370 return STATUS_NO_CONTENT
;
1375 void RGWPutObj_ObjStore_S3::send_response()
1378 set_req_state_err(s
, op_ret
);
1381 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1382 op_ret
= get_success_retcode(
1383 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1384 set_req_state_err(s
, op_ret
);
1386 if (copy_source
.empty()) {
1389 dump_content_length(s
, 0);
1390 for (auto &it
: crypt_http_responses
)
1391 dump_header(s
, it
.first
, it
.second
);
1394 end_header(s
, this, "application/xml");
1398 time_t secs
= (time_t)ut
.sec();
1399 gmtime_r(&secs
, &tmp
);
1400 char buf
[TIME_BUF_SIZE
];
1401 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1402 "http://s3.amazonaws.com/doc/2006-03-01/");
1403 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1404 s
->formatter
->dump_string("LastModified", buf
);
1406 s
->formatter
->dump_string("ETag", etag
);
1407 s
->formatter
->close_section();
1408 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1412 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1413 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1415 end_header(s
, this);
1418 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1420 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1421 RGWRados::Object::Read
read_op(&op_target
);
1423 read_op
.params
.attrs
= &attrs
;
1425 return read_op
.prepare();
1428 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1432 attrs
.emplace(key
, std::move(bl
));
1435 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1439 attrs
.emplace(key
, std::move(bl
));
1442 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1443 std::unique_ptr
<RGWGetDataCB
>* filter
,
1445 map
<string
, bufferlist
>& attrs
,
1446 bufferlist
* manifest_bl
)
1448 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1451 std::unique_ptr
<BlockCrypt
> block_crypt
;
1452 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1454 if (block_crypt
!= nullptr) {
1455 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1456 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1458 if (manifest_bl
!= nullptr) {
1459 res
= f
->read_manifest(*manifest_bl
);
1461 *filter
= std::move(f
);
1470 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1471 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
,
1472 RGWPutObjDataProcessor
* cb
)
1475 RGWPutObjProcessor_Multipart
* multi_processor
=dynamic_cast<RGWPutObjProcessor_Multipart
*>(cb
);
1476 if (multi_processor
!= nullptr) {
1477 RGWMPObj
* mp
= nullptr;
1478 multi_processor
->get_mp(&mp
);
1479 if (mp
!= nullptr) {
1480 map
<string
, bufferlist
> xattrs
;
1482 meta_oid
= mp
->get_meta();
1485 obj
.init_ns(s
->bucket
, meta_oid
, RGW_OBJ_NS_MULTIPART
);
1486 obj
.set_in_extra_data(true);
1487 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1489 std::unique_ptr
<BlockCrypt
> block_crypt
;
1490 /* We are adding to existing object.
1491 * We use crypto mode that configured as if we were decrypting. */
1492 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
1493 if (res
== 0 && block_crypt
!= nullptr)
1494 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1495 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1498 /* it is ok, to not have encryption at all */
1502 std::unique_ptr
<BlockCrypt
> block_crypt
;
1503 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1504 if (res
== 0 && block_crypt
!= nullptr) {
1505 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1506 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1512 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1514 static string var
= "${filename}";
1515 int pos
= key
.find(var
);
1519 string new_key
= key
.substr(0, pos
);
1520 new_key
.append(filename
);
1521 new_key
.append(key
.substr(pos
+ var
.size()));
1526 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1528 return s
->object
.name
;
1531 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1533 return content_type
;
1536 int RGWPostObj_ObjStore_S3::get_params()
1538 op_ret
= RGWPostObj_ObjStore::get_params();
1543 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1545 env
.add_var("bucket", s
->bucket
.name
);
1549 struct post_form_part part
;
1550 int r
= read_form_part_header(&part
, done
);
1554 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 20)) {
1555 ldout(s
->cct
, 20) << "read part header -- part.name="
1556 << part
.name
<< dendl
;
1558 for (const auto& pair
: part
.fields
) {
1559 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1560 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1561 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1563 for (const auto& param_pair
: pair
.second
.params
) {
1564 ldout(s
->cct
, 20) << " " << param_pair
.first
1565 << " -> " << param_pair
.second
<< dendl
;
1570 if (done
) { /* unexpected here */
1571 err_msg
= "Malformed request";
1575 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1576 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1577 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1578 if (iter
!= field
.params
.end()) {
1579 filename
= iter
->second
;
1581 parts
[part
.name
] = part
;
1586 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1587 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1588 if (r
< 0 || !boundary
) {
1589 err_msg
= "Couldn't find boundary";
1592 parts
[part
.name
] = part
;
1593 string
part_str(part
.data
.c_str(), part
.data
.length());
1594 env
.add_var(part
.name
, part_str
);
1598 if (!part_str(parts
, "key", &object_str
)) {
1599 err_msg
= "Key not specified";
1603 s
->object
= rgw_obj_key(object_str
);
1605 rebuild_key(s
->object
.name
);
1607 if (s
->object
.empty()) {
1608 err_msg
= "Empty object name";
1612 env
.add_var("key", s
->object
.name
);
1614 part_str(parts
, "Content-Type", &content_type
);
1616 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
1617 if (! content_type
.empty()) {
1618 env
.add_var("Content-Type", content_type
);
1621 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1622 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1623 for (; piter
!= parts
.end(); ++piter
) {
1624 string n
= piter
->first
;
1625 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1626 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1629 string attr_name
= RGW_ATTR_PREFIX
;
1630 attr_name
.append(n
);
1632 /* need to null terminate it */
1633 bufferlist
& data
= piter
->second
.data
;
1634 string str
= string(data
.c_str(), data
.length());
1637 attr_bl
.append(str
.c_str(), str
.size() + 1);
1639 attrs
[attr_name
] = attr_bl
;
1641 // TODO: refactor this and the above loop to share code
1642 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1643 if (piter
!= parts
.end()) {
1644 string n
= piter
->first
;
1645 string attr_name
= RGW_ATTR_PREFIX
;
1646 attr_name
.append(n
);
1647 /* need to null terminate it */
1648 bufferlist
& data
= piter
->second
.data
;
1649 string str
= string(data
.c_str(), data
.length());
1652 attr_bl
.append(str
.c_str(), str
.size() + 1);
1654 attrs
[attr_name
] = attr_bl
;
1657 int r
= get_policy();
1666 min_len
= post_policy
.min_length
;
1667 max_len
= post_policy
.max_length
;
1674 int RGWPostObj_ObjStore_S3::get_tags()
1677 if (part_str(parts
, "tagging", &tags_str
)) {
1678 RGWObjTagsXMLParser parser
;
1679 if (!parser
.init()){
1680 ldout(s
->cct
, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
1681 err_msg
= "Server couldn't process the request";
1682 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
1684 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
1685 ldout(s
->cct
,0 ) << "Invalid Tagging XML" << dendl
;
1686 err_msg
= "Invalid Tagging XML";
1690 RGWObjTagSet_S3
*obj_tags_s3
;
1691 RGWObjTagging_S3
*tagging
;
1693 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
1694 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
1696 return -ERR_MALFORMED_XML
;
1699 RGWObjTags obj_tags
;
1700 int r
= obj_tags_s3
->rebuild(obj_tags
);
1705 obj_tags
.encode(tags_bl
);
1706 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
1707 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
1714 int RGWPostObj_ObjStore_S3::get_policy()
1716 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1717 bool aws4_auth
= false;
1719 /* x-amz-algorithm handling */
1720 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1721 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1722 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1723 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1726 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1729 // check that the signature matches the encoded policy
1733 /* x-amz-credential handling */
1734 if (!part_str(parts
, "x-amz-credential",
1735 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1736 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1737 err_msg
= "Missing aws4 credential";
1741 /* x-amz-signature handling */
1742 if (!part_str(parts
, "x-amz-signature",
1743 &s
->auth
.s3_postobj_creds
.signature
)) {
1744 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1745 err_msg
= "Missing aws4 signature";
1749 /* x-amz-date handling */
1750 std::string received_date_str
;
1751 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1752 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1753 err_msg
= "Missing aws4 date";
1759 // check that the signature matches the encoded policy
1760 if (!part_str(parts
, "AWSAccessKeyId",
1761 &s
->auth
.s3_postobj_creds
.access_key
)) {
1762 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1763 err_msg
= "Missing aws2 access key";
1767 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1768 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1769 err_msg
= "Missing aws2 signature";
1774 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1775 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1777 const int ret
= rgw::auth::Strategy::apply(auth_registry_ptr
->get_s3_post(), s
);
1781 /* Populate the owner info. */
1782 s
->owner
.set_id(s
->user
->user_id
);
1783 s
->owner
.set_name(s
->user
->display_name
);
1784 ldout(s
->cct
, 20) << "Successful Signature Verification!" << dendl
;
1787 ceph::bufferlist decoded_policy
;
1789 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1790 } catch (buffer::error
& err
) {
1791 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1792 err_msg
= "Could not decode policy";
1796 decoded_policy
.append('\0'); // NULL terminate
1797 ldout(s
->cct
, 20) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1800 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1802 if (err_msg
.empty()) {
1803 err_msg
= "Failed to parse policy";
1805 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1811 post_policy
.set_var_checked("x-amz-signature");
1814 post_policy
.set_var_checked("AWSAccessKeyId");
1815 post_policy
.set_var_checked("signature");
1817 post_policy
.set_var_checked("policy");
1819 r
= post_policy
.check(&env
, err_msg
);
1821 if (err_msg
.empty()) {
1822 err_msg
= "Policy check failed";
1824 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1829 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1833 part_str(parts
, "acl", &canned_acl
);
1835 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1836 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1837 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1838 err_msg
= "Bad canned ACLs";
1847 int RGWPostObj_ObjStore_S3::complete_get_params()
1851 struct post_form_part part
;
1852 int r
= read_form_part_header(&part
, done
);
1857 ceph::bufferlist part_data
;
1859 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1860 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1861 if (r
< 0 || !boundary
) {
1865 /* Just reading the data but not storing any results of that. */
1871 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1876 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1877 int r
= read_data(bl
, chunk_size
, boundary
, done
);
1884 /* Reached end of data, let's drain the rest of the params */
1885 r
= complete_get_params();
1896 void RGWPostObj_ObjStore_S3::send_response()
1898 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
1901 part_str(parts
, "success_action_redirect", &redirect
);
1906 string etag_str
= "\"";
1908 etag_str
.append(etag
);
1909 etag_str
.append("\"");
1913 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
1914 url_encode(s
->bucket_name
, bucket
);
1915 url_encode(s
->object
.name
, key
);
1916 url_encode(etag_str
, etag_url
);
1918 if (!s
->bucket_tenant
.empty()) {
1920 * What we really would like is to quaily the bucket name, so
1921 * that the client could simply copy it and paste into next request.
1922 * Unfortunately, in S3 we cannot know if the client will decide
1923 * to come through DNS, with "bucket.tenant" sytanx, or through
1924 * URL with "tenant\bucket" syntax. Therefore, we provide the
1925 * tenant separately.
1927 redirect
.append("?tenant=");
1928 redirect
.append(tenant
);
1929 redirect
.append("&bucket=");
1930 redirect
.append(bucket
);
1932 redirect
.append("?bucket=");
1933 redirect
.append(bucket
);
1935 redirect
.append("&key=");
1936 redirect
.append(key
);
1937 redirect
.append("&etag=");
1938 redirect
.append(etag_url
);
1940 int r
= check_utf8(redirect
.c_str(), redirect
.size());
1945 dump_redirect(s
, redirect
);
1946 op_ret
= STATUS_REDIRECT
;
1947 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
1948 string status_string
;
1949 uint32_t status_int
;
1951 part_str(parts
, "success_action_status", &status_string
);
1953 int r
= stringtoul(status_string
, &status_int
);
1959 switch (status_int
) {
1963 op_ret
= STATUS_CREATED
;
1966 op_ret
= STATUS_NO_CONTENT
;
1969 } else if (! op_ret
) {
1970 op_ret
= STATUS_NO_CONTENT
;
1974 if (op_ret
== STATUS_CREATED
) {
1975 for (auto &it
: crypt_http_responses
)
1976 dump_header(s
, it
.first
, it
.second
);
1977 s
->formatter
->open_object_section("PostResponse");
1978 if (g_conf
->rgw_dns_name
.length())
1979 s
->formatter
->dump_format("Location", "%s/%s",
1980 s
->info
.script_uri
.c_str(),
1981 s
->object
.name
.c_str());
1982 if (!s
->bucket_tenant
.empty())
1983 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1984 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
1985 s
->formatter
->dump_string("Key", s
->object
.name
);
1986 s
->formatter
->close_section();
1988 s
->err
.message
= err_msg
;
1989 set_req_state_err(s
, op_ret
);
1992 dump_content_length(s
, s
->formatter
->get_len());
1994 end_header(s
, this);
1995 if (op_ret
!= STATUS_CREATED
)
1998 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2001 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
2002 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
, RGWPutObjDataProcessor
* cb
)
2005 std::unique_ptr
<BlockCrypt
> block_crypt
;
2006 res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
, crypt_http_responses
);
2007 if (res
== 0 && block_crypt
!= nullptr) {
2008 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
2009 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
2016 int RGWDeleteObj_ObjStore_S3::get_params()
2018 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2020 if (s
->system_request
) {
2021 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
2025 std::string if_unmod_decoded
= url_decode(if_unmod
);
2028 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
2029 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
2032 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
2038 void RGWDeleteObj_ObjStore_S3::send_response()
2044 r
= STATUS_NO_CONTENT
;
2046 set_req_state_err(s
, r
);
2048 if (!version_id
.empty()) {
2049 dump_header(s
, "x-amz-version-id", version_id
);
2051 if (delete_marker
) {
2052 dump_header(s
, "x-amz-delete-marker", "true");
2054 end_header(s
, this);
2057 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2059 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2061 /* build a policy for the target object */
2062 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2066 dest_policy
= s3policy
;
2071 int RGWCopyObj_ObjStore_S3::get_params()
2073 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2074 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2075 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
2076 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2078 src_tenant_name
= s
->src_tenant_name
;
2079 src_bucket_name
= s
->src_bucket_name
;
2080 src_object
= s
->src_object
;
2081 dest_tenant_name
= s
->bucket
.tenant
;
2082 dest_bucket_name
= s
->bucket
.name
;
2083 dest_object
= s
->object
.name
;
2085 if (s
->system_request
) {
2086 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
2087 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
2088 if (!source_zone
.empty()) {
2089 client_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"client-id");
2090 op_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"op-id");
2092 if (client_id
.empty() || op_id
.empty()) {
2094 RGW_SYS_PARAM_PREFIX
"client-id or "
2095 RGW_SYS_PARAM_PREFIX
"op-id were not provided, "
2096 "required for intra-region copy"
2103 const char *md_directive
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2105 if (strcasecmp(md_directive
, "COPY") == 0) {
2106 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
2107 } else if (strcasecmp(md_directive
, "REPLACE") == 0) {
2108 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
2109 } else if (!source_zone
.empty()) {
2110 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
2112 ldout(s
->cct
, 0) << "invalid metadata directive" << dendl
;
2117 if (source_zone
.empty() &&
2118 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
2119 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
2120 (dest_object
.compare(src_object
.name
) == 0) &&
2121 src_object
.instance
.empty() &&
2122 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
2123 /* can only copy object into itself if replacing attrs */
2124 ldout(s
->cct
, 0) << "can't copy object into itself if not replacing attrs"
2126 return -ERR_INVALID_REQUEST
;
2131 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
2133 if (! sent_header
) {
2135 set_req_state_err(s
, op_ret
);
2138 end_header(s
, this, "application/xml");
2141 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
2145 /* Send progress field. Note that this diverge from the original S3
2146 * spec. We do this in order to keep connection alive.
2148 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
2150 rgw_flush_formatter(s
, s
->formatter
);
2153 void RGWCopyObj_ObjStore_S3::send_response()
2156 send_partial_response(0);
2159 dump_time(s
, "LastModified", &mtime
);
2160 std::string etag_str
= etag
.to_str();
2161 if (! etag_str
.empty()) {
2162 s
->formatter
->dump_string("ETag", std::move(etag_str
));
2164 s
->formatter
->close_section();
2165 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2169 void RGWGetACLs_ObjStore_S3::send_response()
2172 set_req_state_err(s
, op_ret
);
2174 end_header(s
, this, "application/xml");
2176 rgw_flush_formatter(s
, s
->formatter
);
2180 int RGWPutACLs_ObjStore_S3::get_params()
2182 int ret
= RGWPutACLs_ObjStore::get_params();
2184 const int ret_auth
= do_aws4_auth_completion();
2192 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
2193 struct req_state
*s
,
2196 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2198 // bucket-* canned acls do not apply to bucket
2199 if (s
->object
.empty()) {
2200 if (s
->canned_acl
.find("bucket") != string::npos
)
2201 s
->canned_acl
.clear();
2204 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2208 s3policy
.to_xml(ss
);
2213 void RGWPutACLs_ObjStore_S3::send_response()
2216 set_req_state_err(s
, op_ret
);
2218 end_header(s
, this, "application/xml");
2222 void RGWGetLC_ObjStore_S3::execute()
2224 config
.set_ctx(s
->cct
);
2226 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2227 if (aiter
== s
->bucket_attrs
.end()) {
2232 bufferlist::iterator
iter(&aiter
->second
);
2234 config
.decode(iter
);
2235 } catch (const buffer::error
& e
) {
2236 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2242 void RGWGetLC_ObjStore_S3::send_response()
2245 if (op_ret
== -ENOENT
) {
2246 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2248 set_req_state_err(s
, op_ret
);
2252 end_header(s
, this, "application/xml");
2258 config
.dump_xml(s
->formatter
);
2259 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2262 void RGWPutLC_ObjStore_S3::send_response()
2265 set_req_state_err(s
, op_ret
);
2267 end_header(s
, this, "application/xml");
2271 void RGWDeleteLC_ObjStore_S3::send_response()
2274 op_ret
= STATUS_NO_CONTENT
;
2276 set_req_state_err(s
, op_ret
);
2279 end_header(s
, this, "application/xml");
2283 void RGWGetCORS_ObjStore_S3::send_response()
2286 if (op_ret
== -ENOENT
)
2287 set_req_state_err(s
, ERR_NO_SUCH_CORS_CONFIGURATION
);
2289 set_req_state_err(s
, op_ret
);
2292 end_header(s
, NULL
, "application/xml");
2296 RGWCORSConfiguration_S3
*s3cors
=
2297 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2306 int RGWPutCORS_ObjStore_S3::get_params()
2309 char *data
= nullptr;
2311 RGWCORSXMLParser_S3
parser(s
->cct
);
2312 RGWCORSConfiguration_S3
*cors_config
;
2314 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2315 r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2320 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
2322 r
= do_aws4_auth_completion();
2327 if (!parser
.init()) {
2331 if (!data
|| !parser
.parse(data
, len
, 1)) {
2335 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2336 "CORSConfiguration"));
2341 // forward bucket cors requests to meta master zone
2342 if (!store
->is_meta_master()) {
2343 /* only need to keep this data around if we're not meta master */
2344 in_data
.append(data
, len
);
2347 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15)) {
2348 ldout(s
->cct
, 15) << "CORSConfiguration";
2349 cors_config
->to_xml(*_dout
);
2353 cors_config
->encode(cors_bl
);
2358 void RGWPutCORS_ObjStore_S3::send_response()
2361 set_req_state_err(s
, op_ret
);
2363 end_header(s
, NULL
, "application/xml");
2367 void RGWDeleteCORS_ObjStore_S3::send_response()
2370 if (!r
|| r
== -ENOENT
)
2371 r
= STATUS_NO_CONTENT
;
2373 set_req_state_err(s
, r
);
2375 end_header(s
, NULL
);
2378 void RGWOptionsCORS_ObjStore_S3::send_response()
2380 string hdrs
, exp_hdrs
;
2381 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2382 /*EACCES means, there is no CORS registered yet for the bucket
2383 *ENOENT means, there is no match of the Origin in the list of CORSRule
2385 if (op_ret
== -ENOENT
)
2388 set_req_state_err(s
, op_ret
);
2390 end_header(s
, NULL
);
2393 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2396 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2398 end_header(s
, NULL
);
2401 void RGWGetRequestPayment_ObjStore_S3::send_response()
2404 end_header(s
, this, "application/xml");
2407 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2408 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2409 s
->formatter
->dump_string("Payer", payer
);
2410 s
->formatter
->close_section();
2411 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2414 class RGWSetRequestPaymentParser
: public RGWXMLParser
2416 XMLObj
*alloc_obj(const char *el
) override
{
2421 RGWSetRequestPaymentParser() {}
2422 ~RGWSetRequestPaymentParser() override
{}
2424 int get_request_payment_payer(bool *requester_pays
) {
2425 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2429 *requester_pays
= false;
2431 XMLObj
*field
= config
->find_first("Payer");
2435 string
& s
= field
->get_data();
2437 if (stringcasecmp(s
, "Requester") == 0) {
2438 *requester_pays
= true;
2439 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2447 int RGWSetRequestPayment_ObjStore_S3::get_params()
2451 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2452 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2458 RGWSetRequestPaymentParser parser
;
2460 if (!parser
.init()) {
2461 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2466 if (!parser
.parse(data
, len
, 1)) {
2467 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
2472 r
= parser
.get_request_payment_payer(&requester_pays
);
2480 void RGWSetRequestPayment_ObjStore_S3::send_response()
2483 set_req_state_err(s
, op_ret
);
2488 int RGWInitMultipart_ObjStore_S3::get_params()
2490 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2491 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2500 void RGWInitMultipart_ObjStore_S3::send_response()
2503 set_req_state_err(s
, op_ret
);
2505 for (auto &it
: crypt_http_responses
)
2506 dump_header(s
, it
.first
, it
.second
);
2507 end_header(s
, this, "application/xml");
2510 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2511 if (!s
->bucket_tenant
.empty())
2512 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2513 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2514 s
->formatter
->dump_string("Key", s
->object
.name
);
2515 s
->formatter
->dump_string("UploadId", upload_id
);
2516 s
->formatter
->close_section();
2517 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2521 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2524 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2528 int RGWCompleteMultipart_ObjStore_S3::get_params()
2530 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2535 return do_aws4_auth_completion();
2538 void RGWCompleteMultipart_ObjStore_S3::send_response()
2541 set_req_state_err(s
, op_ret
);
2543 end_header(s
, this, "application/xml");
2546 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2547 std::string base_uri
= compute_domain_uri(s
);
2548 if (!s
->bucket_tenant
.empty()) {
2549 s
->formatter
->dump_format("Location", "%s/%s:%s/%s",
2551 s
->bucket_tenant
.c_str(),
2552 s
->bucket_name
.c_str(),
2553 s
->object
.name
.c_str()
2555 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2557 s
->formatter
->dump_format("Location", "%s/%s/%s",
2559 s
->bucket_name
.c_str(),
2560 s
->object
.name
.c_str()
2563 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2564 s
->formatter
->dump_string("Key", s
->object
.name
);
2565 s
->formatter
->dump_string("ETag", etag
);
2566 s
->formatter
->close_section();
2567 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2571 void RGWAbortMultipart_ObjStore_S3::send_response()
2575 r
= STATUS_NO_CONTENT
;
2577 set_req_state_err(s
, r
);
2579 end_header(s
, this);
2582 void RGWListMultipart_ObjStore_S3::send_response()
2585 set_req_state_err(s
, op_ret
);
2587 end_header(s
, this, "application/xml");
2591 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2592 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2593 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2596 iter
= parts
.begin();
2597 test_iter
= parts
.rbegin();
2598 if (test_iter
!= parts
.rend()) {
2599 cur_max
= test_iter
->first
;
2601 if (!s
->bucket_tenant
.empty())
2602 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2603 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2604 s
->formatter
->dump_string("Key", s
->object
.name
);
2605 s
->formatter
->dump_string("UploadId", upload_id
);
2606 s
->formatter
->dump_string("StorageClass", "STANDARD");
2607 s
->formatter
->dump_int("PartNumberMarker", marker
);
2608 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2609 s
->formatter
->dump_int("MaxParts", max_parts
);
2610 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2612 ACLOwner
& owner
= policy
.get_owner();
2613 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2615 for (; iter
!= parts
.end(); ++iter
) {
2616 RGWUploadPartInfo
& info
= iter
->second
;
2618 s
->formatter
->open_object_section("Part");
2620 dump_time(s
, "LastModified", &info
.modified
);
2622 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2623 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2624 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2625 s
->formatter
->close_section();
2627 s
->formatter
->close_section();
2628 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2632 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2635 set_req_state_err(s
, op_ret
);
2638 end_header(s
, this, "application/xml");
2643 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2644 if (!s
->bucket_tenant
.empty())
2645 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2646 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2647 if (!prefix
.empty())
2648 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2649 string
& key_marker
= marker
.get_key();
2650 if (!key_marker
.empty())
2651 s
->formatter
->dump_string("KeyMarker", key_marker
);
2652 string
& upload_id_marker
= marker
.get_upload_id();
2653 if (!upload_id_marker
.empty())
2654 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2655 string next_key
= next_marker
.mp
.get_key();
2656 if (!next_key
.empty())
2657 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2658 string next_upload_id
= next_marker
.mp
.get_upload_id();
2659 if (!next_upload_id
.empty())
2660 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2661 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2662 if (!delimiter
.empty())
2663 s
->formatter
->dump_string("Delimiter", delimiter
);
2664 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2667 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2668 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2669 RGWMPObj
& mp
= iter
->mp
;
2670 s
->formatter
->open_array_section("Upload");
2671 s
->formatter
->dump_string("Key", mp
.get_key());
2672 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2673 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2674 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2675 s
->formatter
->dump_string("StorageClass", "STANDARD");
2676 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2677 s
->formatter
->close_section();
2679 if (!common_prefixes
.empty()) {
2680 s
->formatter
->open_array_section("CommonPrefixes");
2681 map
<string
, bool>::iterator pref_iter
;
2682 for (pref_iter
= common_prefixes
.begin();
2683 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2684 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2686 s
->formatter
->close_section();
2689 s
->formatter
->close_section();
2690 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2693 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2695 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2700 return do_aws4_auth_completion();
2703 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2705 if (! status_dumped
) {
2707 set_req_state_err(s
, op_ret
);
2709 status_dumped
= true;
2713 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2716 if (!status_dumped
) {
2721 end_header(s
, this, "application/xml");
2722 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2724 rgw_flush_formatter(s
, s
->formatter
);
2727 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2729 const string
& marker_version_id
, int ret
)
2732 if (op_ret
== 0 && !quiet
) {
2733 s
->formatter
->open_object_section("Deleted");
2734 s
->formatter
->dump_string("Key", key
.name
);
2735 if (!key
.instance
.empty()) {
2736 s
->formatter
->dump_string("VersionId", key
.instance
);
2738 if (delete_marker
) {
2739 s
->formatter
->dump_bool("DeleteMarker", true);
2740 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2742 s
->formatter
->close_section();
2743 } else if (op_ret
< 0) {
2744 struct rgw_http_error r
;
2747 s
->formatter
->open_object_section("Error");
2750 rgw_get_errno_s3(&r
, err_no
);
2752 s
->formatter
->dump_string("Key", key
.name
);
2753 s
->formatter
->dump_string("VersionId", key
.instance
);
2754 s
->formatter
->dump_int("Code", r
.http_ret
);
2755 s
->formatter
->dump_string("Message", r
.s3_code
);
2756 s
->formatter
->close_section();
2759 rgw_flush_formatter(s
, s
->formatter
);
2763 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2766 s
->formatter
->close_section();
2767 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2770 void RGWGetObjLayout_ObjStore_S3::send_response()
2773 set_req_state_err(s
, op_ret
);
2775 end_header(s
, this, "application/json");
2783 f
.open_object_section("result");
2784 ::encode_json("head", head_obj
, &f
);
2785 ::encode_json("manifest", *manifest
, &f
);
2786 f
.open_array_section("data_location");
2787 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2788 f
.open_object_section("obj");
2789 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2790 ::encode_json("ofs", miter
.get_ofs(), &f
);
2791 ::encode_json("loc", raw_loc
, &f
);
2792 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2793 ::encode_json("loc_size", miter
.get_stripe_size(), &f
);
2795 rgw_flush_formatter(s
, &f
);
2799 rgw_flush_formatter(s
, &f
);
2802 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2804 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2805 if (iter
== s
->info
.x_meta_map
.end()) {
2806 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2807 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2811 list
<string
> expressions
;
2812 get_str_list(iter
->second
, ",", expressions
);
2814 for (auto& expression
: expressions
) {
2815 vector
<string
> args
;
2816 get_str_vec(expression
, ";", args
);
2819 s
->err
.message
= "invalid empty expression";
2820 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2823 if (args
.size() > 2) {
2824 s
->err
.message
= string("invalid expression: ") + expression
;
2825 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2829 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2831 if (args
.size() > 1) {
2832 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2835 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2836 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2837 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2841 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2843 ESEntityTypeMap::EntityType entity_type
;
2845 if (val
.empty() || val
== "str" || val
== "string") {
2846 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
2847 } else if (val
== "int" || val
== "integer") {
2848 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
2849 } else if (val
== "date" || val
== "datetime") {
2850 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
2852 s
->err
.message
= string("invalid entity type: ") + val
;
2853 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2857 mdsearch_config
[key
] = entity_type
;
2863 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2866 set_req_state_err(s
, op_ret
);
2868 end_header(s
, this);
2871 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2874 set_req_state_err(s
, op_ret
);
2876 end_header(s
, NULL
, "application/xml");
2878 Formatter
*f
= s
->formatter
;
2879 f
->open_array_section("GetBucketMetaSearchResult");
2880 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
2881 f
->open_object_section("Entry");
2882 string k
= string("x-amz-meta-") + e
.first
;
2883 f
->dump_string("Key", k
.c_str());
2886 case ESEntityTypeMap::ES_ENTITY_INT
:
2889 case ESEntityTypeMap::ES_ENTITY_DATE
:
2895 f
->dump_string("Type", type
);
2899 rgw_flush_formatter(s
, f
);
2902 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2905 set_req_state_err(s
, op_ret
);
2907 end_header(s
, this);
2911 RGWOp
*RGWHandler_REST_Service_S3::op_get()
2913 if (is_usage_op()) {
2914 return new RGWGetUsage_ObjStore_S3
;
2916 return new RGWListBuckets_ObjStore_S3
;
2920 RGWOp
*RGWHandler_REST_Service_S3::op_head()
2922 return new RGWListBuckets_ObjStore_S3
;
2925 RGWOp
*RGWHandler_REST_Service_S3::op_post()
2927 if (s
->info
.args
.exists("Action")) {
2928 string action
= s
->info
.args
.get("Action");
2929 if (action
.compare("CreateRole") == 0)
2930 return new RGWCreateRole
;
2931 if (action
.compare("DeleteRole") == 0)
2932 return new RGWDeleteRole
;
2933 if (action
.compare("GetRole") == 0)
2934 return new RGWGetRole
;
2935 if (action
.compare("UpdateAssumeRolePolicy") == 0)
2936 return new RGWModifyRole
;
2937 if (action
.compare("ListRoles") == 0)
2938 return new RGWListRoles
;
2939 if (action
.compare("PutRolePolicy") == 0)
2940 return new RGWPutRolePolicy
;
2941 if (action
.compare("GetRolePolicy") == 0)
2942 return new RGWGetRolePolicy
;
2943 if (action
.compare("ListRolePolicies") == 0)
2944 return new RGWListRolePolicies
;
2945 if (action
.compare("DeleteRolePolicy") == 0)
2946 return new RGWDeleteRolePolicy
;
2951 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
2955 return new RGWListBucket_ObjStore_S3
;
2957 return new RGWStatBucket_ObjStore_S3
;
2961 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
2963 if (s
->info
.args
.sub_resource_exists("logging"))
2964 return new RGWGetBucketLogging_ObjStore_S3
;
2966 if (s
->info
.args
.sub_resource_exists("location"))
2967 return new RGWGetBucketLocation_ObjStore_S3
;
2969 if (s
->info
.args
.sub_resource_exists("versioning"))
2970 return new RGWGetBucketVersioning_ObjStore_S3
;
2972 if (s
->info
.args
.sub_resource_exists("website")) {
2973 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2976 return new RGWGetBucketWebsite_ObjStore_S3
;
2979 if (s
->info
.args
.exists("mdsearch")) {
2980 return new RGWGetBucketMetaSearch_ObjStore_S3
;
2984 return new RGWGetACLs_ObjStore_S3
;
2985 } else if (is_cors_op()) {
2986 return new RGWGetCORS_ObjStore_S3
;
2987 } else if (is_request_payment_op()) {
2988 return new RGWGetRequestPayment_ObjStore_S3
;
2989 } else if (s
->info
.args
.exists("uploads")) {
2990 return new RGWListBucketMultiparts_ObjStore_S3
;
2991 } else if(is_lc_op()) {
2992 return new RGWGetLC_ObjStore_S3
;
2993 } else if(is_policy_op()) {
2994 return new RGWGetBucketPolicy
;
2996 return get_obj_op(true);
2999 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
3002 return new RGWGetACLs_ObjStore_S3
;
3003 } else if (s
->info
.args
.exists("uploads")) {
3004 return new RGWListBucketMultiparts_ObjStore_S3
;
3006 return get_obj_op(false);
3009 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
3011 if (s
->info
.args
.sub_resource_exists("logging"))
3013 if (s
->info
.args
.sub_resource_exists("versioning"))
3014 return new RGWSetBucketVersioning_ObjStore_S3
;
3015 if (s
->info
.args
.sub_resource_exists("website")) {
3016 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3019 return new RGWSetBucketWebsite_ObjStore_S3
;
3022 return new RGWPutACLs_ObjStore_S3
;
3023 } else if (is_cors_op()) {
3024 return new RGWPutCORS_ObjStore_S3
;
3025 } else if (is_request_payment_op()) {
3026 return new RGWSetRequestPayment_ObjStore_S3
;
3027 } else if(is_lc_op()) {
3028 return new RGWPutLC_ObjStore_S3
;
3029 } else if(is_policy_op()) {
3030 return new RGWPutBucketPolicy
;
3032 return new RGWCreateBucket_ObjStore_S3
;
3035 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
3038 return new RGWDeleteCORS_ObjStore_S3
;
3039 } else if(is_lc_op()) {
3040 return new RGWDeleteLC_ObjStore_S3
;
3041 } else if(is_policy_op()) {
3042 return new RGWDeleteBucketPolicy
;
3045 if (s
->info
.args
.sub_resource_exists("website")) {
3046 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3049 return new RGWDeleteBucketWebsite_ObjStore_S3
;
3052 if (s
->info
.args
.exists("mdsearch")) {
3053 return new RGWDelBucketMetaSearch_ObjStore_S3
;
3056 return new RGWDeleteBucket_ObjStore_S3
;
3059 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
3061 if (s
->info
.args
.exists("delete")) {
3062 return new RGWDeleteMultiObj_ObjStore_S3
;
3065 if (s
->info
.args
.exists("mdsearch")) {
3066 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
3069 return new RGWPostObj_ObjStore_S3
;
3072 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
3074 return new RGWOptionsCORS_ObjStore_S3
;
3077 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
3080 return new RGWGetACLs_ObjStore_S3
;
3082 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
3083 get_obj_op
->set_get_data(get_data
);
3087 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
3090 return new RGWGetACLs_ObjStore_S3
;
3091 } else if (s
->info
.args
.exists("uploadId")) {
3092 return new RGWListMultipart_ObjStore_S3
;
3093 } else if (s
->info
.args
.exists("layout")) {
3094 return new RGWGetObjLayout_ObjStore_S3
;
3095 } else if (is_tagging_op()) {
3096 return new RGWGetObjTags_ObjStore_S3
;
3098 return get_obj_op(true);
3101 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
3104 return new RGWGetACLs_ObjStore_S3
;
3105 } else if (s
->info
.args
.exists("uploadId")) {
3106 return new RGWListMultipart_ObjStore_S3
;
3108 return get_obj_op(false);
3111 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
3114 return new RGWPutACLs_ObjStore_S3
;
3115 } else if (is_tagging_op()) {
3116 return new RGWPutObjTags_ObjStore_S3
;
3119 if (s
->init_state
.src_bucket
.empty())
3120 return new RGWPutObj_ObjStore_S3
;
3122 return new RGWCopyObj_ObjStore_S3
;
3125 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
3127 if (is_tagging_op()) {
3128 return new RGWDeleteObjTags_ObjStore_S3
;
3130 string upload_id
= s
->info
.args
.get("uploadId");
3132 if (upload_id
.empty())
3133 return new RGWDeleteObj_ObjStore_S3
;
3135 return new RGWAbortMultipart_ObjStore_S3
;
3138 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
3140 if (s
->info
.args
.exists("uploadId"))
3141 return new RGWCompleteMultipart_ObjStore_S3
;
3143 if (s
->info
.args
.exists("uploads"))
3144 return new RGWInitMultipart_ObjStore_S3
;
3146 return new RGWPostObj_ObjStore_S3
;
3149 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
3151 return new RGWOptionsCORS_ObjStore_S3
;
3154 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
3155 int default_formatter
,
3156 bool configurable_format
)
3161 const char *req_name
= s
->relative_uri
.c_str();
3164 if (*req_name
== '?') {
3167 p
= s
->info
.request_params
.c_str();
3170 s
->info
.args
.set(p
);
3171 s
->info
.args
.parse();
3173 /* must be called after the args parsing */
3174 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
3178 if (*req_name
!= '/')
3187 int pos
= req
.find('/');
3189 first
= req
.substr(0, pos
);
3195 * XXX The intent of the check for empty is apparently to let the bucket
3196 * name from DNS to be set ahead. However, we currently take the DNS
3197 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3198 * So, this check is meaningless.
3200 * Rather than dropping this, the code needs to be changed into putting
3201 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3202 * into req_status.bucket_name directly.
3204 if (s
->init_state
.url_bucket
.empty()) {
3205 // Save bucket to tide us over until token is parsed.
3206 s
->init_state
.url_bucket
= first
;
3208 string encoded_obj_str
= req
.substr(pos
+1);
3209 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
3212 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3217 int RGWHandler_REST_S3::postauth_init()
3219 struct req_init_state
*t
= &s
->init_state
;
3220 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3222 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3223 s
->bucket_tenant
, s
->bucket_name
);
3225 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3226 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3229 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3232 if (!s
->bucket_name
.empty()) {
3233 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3236 ret
= validate_object_name(s
->object
.name
);
3241 if (!t
->src_bucket
.empty()) {
3242 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3243 s
->src_tenant_name
, s
->src_bucket_name
);
3244 ret
= rgw_validate_tenant_name(s
->src_tenant_name
);
3247 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3254 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3255 rgw::io::BasicClient
*cio
)
3261 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3264 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3265 if (!s
->bucket_name
.empty()) {
3266 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3269 ret
= validate_object_name(s
->object
.name
);
3274 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3276 s
->canned_acl
= cacl
;
3278 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3280 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3282 (! s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) &&
3283 (! s
->info
.args
.exists("uploadId"))) {
3285 ret
= RGWCopyObj::parse_copy_location(url_decode(copy_source
),
3286 s
->init_state
.src_bucket
,
3289 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3290 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3294 return RGWHandler_REST::init(store
, s
, cio
);
3297 enum class AwsVersion
{
3303 enum class AwsRoute
{
3309 static inline std::pair
<AwsVersion
, AwsRoute
>
3310 discover_aws_flavour(const req_info
& info
)
3312 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3314 AwsVersion version
= AwsVersion::UNKOWN
;
3315 AwsRoute route
= AwsRoute::UNKOWN
;
3317 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3318 if (http_auth
&& http_auth
[0]) {
3319 /* Authorization in Header */
3320 route
= AwsRoute::HEADERS
;
3322 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3323 strlen(AWS4_HMAC_SHA256_STR
))) {
3325 version
= AwsVersion::V4
;
3326 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3328 version
= AwsVersion::V2
;
3331 route
= AwsRoute::QUERY_STRING
;
3333 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3335 version
= AwsVersion::V4
;
3336 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3338 version
= AwsVersion::V2
;
3342 return std::make_pair(version
, route
);
3345 static void init_anon_user(struct req_state
*s
)
3347 rgw_get_anon_user(*(s
->user
));
3348 s
->perm_mask
= RGW_PERM_FULL_CONTROL
;
3352 * verify that a signed request comes from the keyholder
3353 * by checking the signature against our locally-computed version
3355 * it tries AWS v4 before AWS v2
3357 int RGW_Auth_S3::authorize(RGWRados
* const store
,
3358 const rgw::auth::StrategyRegistry
& auth_registry
,
3359 struct req_state
* const s
)
3362 /* neither keystone and rados enabled; warn and exit! */
3363 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3364 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3365 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3366 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3370 const auto ret
= rgw::auth::Strategy::apply(auth_registry
.get_s3_main(), s
);
3372 /* Populate the owner info. */
3373 s
->owner
.set_id(s
->user
->user_id
);
3374 s
->owner
.set_name(s
->user
->display_name
);
3379 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3380 rgw::io::BasicClient
*cio
)
3382 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3387 return RGWHandler_REST::init(store
, state
, cio
);
3390 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3391 const rgw::auth::StrategyRegistry
& auth_registry
,
3392 const std::string
& frontend_prefix
)
3394 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3396 RGWHandler_REST_S3::init_from_header(s
,
3397 is_s3website
? RGW_FORMAT_HTML
:
3398 RGW_FORMAT_XML
, true);
3402 RGWHandler_REST
* handler
;
3403 // TODO: Make this more readable
3405 if (s
->init_state
.url_bucket
.empty()) {
3406 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3407 } else if (s
->object
.empty()) {
3408 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3410 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3413 if (s
->init_state
.url_bucket
.empty()) {
3414 handler
= new RGWHandler_REST_Service_S3(auth_registry
);
3415 } else if (s
->object
.empty()) {
3416 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3418 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3422 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3427 bool RGWHandler_REST_S3Website::web_dir() const {
3428 std::string subdir_name
= url_decode(s
->object
.name
);
3430 if (subdir_name
.empty()) {
3432 } else if (subdir_name
.back() == '/') {
3433 subdir_name
.pop_back();
3436 rgw_obj
obj(s
->bucket
, subdir_name
);
3438 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3439 obj_ctx
.obj
.set_atomic(obj
);
3440 obj_ctx
.obj
.set_prefetch_data(obj
);
3442 RGWObjState
* state
= nullptr;
3443 if (store
->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false) < 0) {
3446 if (! state
->exists
) {
3449 return state
->exists
;
3452 int RGWHandler_REST_S3Website::init(RGWRados
*store
, req_state
*s
,
3453 rgw::io::BasicClient
* cio
)
3455 // save the original object name before retarget() replaces it with the
3456 // result of get_effective_key(). the error_handler() needs the original
3457 // object name for redirect handling
3458 original_object_name
= s
->object
.name
;
3460 return RGWHandler_REST_S3::init(store
, s
, cio
);
3463 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3465 ldout(s
->cct
, 10) << __func__
<< "Starting retarget" << dendl
;
3467 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3470 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3471 int ret
= store
->get_bucket_info(obj_ctx
, s
->bucket_tenant
,
3472 s
->bucket_name
, s
->bucket_info
, NULL
,
3475 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3476 return -ERR_NO_SUCH_BUCKET
;
3478 if (!s
->bucket_info
.has_website
) {
3479 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3480 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3483 rgw_obj_key new_obj
;
3484 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
3485 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3486 << new_obj
<< dendl
;
3488 RGWBWRoutingRule rrule
;
3489 bool should_redirect
=
3490 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3492 if (should_redirect
) {
3493 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3494 const string
& protocol
=
3495 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3496 int redirect_code
= 0;
3497 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3499 // APply a custom HTTP response code
3500 if (redirect_code
> 0)
3501 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3502 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3503 << " proto+host:" << protocol
<< "://" << hostname
3504 << " -> " << s
->redirect
<< dendl
;
3505 return -ERR_WEBSITE_REDIRECT
;
3509 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3510 * operation. Or remove this comment if it's not applicable anymore
3513 s
->object
= new_obj
;
3518 RGWOp
* RGWHandler_REST_S3Website::op_get()
3520 return get_obj_op(true);
3523 RGWOp
* RGWHandler_REST_S3Website::op_head()
3525 return get_obj_op(false);
3528 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3530 s
->formatter
->reset(); /* Try to throw it all away */
3532 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3533 if (getop
.get() == NULL
) {
3534 return -1; // Trigger double error handler
3536 getop
->init(store
, s
, this);
3537 getop
->range_str
= NULL
;
3538 getop
->if_mod
= NULL
;
3539 getop
->if_unmod
= NULL
;
3540 getop
->if_match
= NULL
;
3541 getop
->if_nomatch
= NULL
;
3542 s
->object
= errordoc_key
;
3544 ret
= init_permissions(getop
.get());
3546 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3547 return -1; // Trigger double error handler
3550 ret
= read_permissions(getop
.get());
3552 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3553 return -1; // Trigger double error handler
3557 getop
->set_custom_http_response(http_ret
);
3560 ret
= getop
->init_processing();
3562 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3563 return -1; // Trigger double error handler
3566 ret
= getop
->verify_op_mask();
3568 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3569 return -1; // Trigger double error handler
3572 ret
= getop
->verify_permission();
3574 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3575 return -1; // Trigger double error handler
3578 ret
= getop
->verify_params();
3580 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3581 return -1; // Trigger double error handler
3584 // No going back now
3587 * FIXME Missing headers:
3588 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3589 * x-amz-error-code: NoSuchKey
3590 * x-amz-error-message: The specified key does not exist.
3591 * x-amz-error-detail-Key: foo
3599 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3600 string
* error_content
) {
3601 int new_err_no
= -1;
3602 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3603 int http_error_code
= -1;
3605 if (r
!= rgw_http_s3_errors
.end()) {
3606 http_error_code
= r
->second
.first
;
3608 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3610 RGWBWRoutingRule rrule
;
3611 bool should_redirect
=
3612 s
->bucket_info
.website_conf
.should_redirect(original_object_name
,
3613 http_error_code
, &rrule
);
3615 if (should_redirect
) {
3616 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3617 const string
& protocol
=
3618 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3619 int redirect_code
= 0;
3620 rrule
.apply_rule(protocol
, hostname
, original_object_name
,
3621 &s
->redirect
, &redirect_code
);
3622 // Apply a custom HTTP response code
3623 if (redirect_code
> 0)
3624 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3625 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3626 << " proto+host:" << protocol
<< "://" << hostname
3627 << " -> " << s
->redirect
<< dendl
;
3628 return -ERR_WEBSITE_REDIRECT
;
3629 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3630 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3631 // Do NOT fire the ErrorDoc handler
3632 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3633 /* This serves an entire page!
3634 On success, it will return zero, and no further content should be sent to the socket
3635 On failure, we need the double-error handler
3637 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3638 if (new_err_no
&& new_err_no
!= -1) {
3639 err_no
= new_err_no
;
3642 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3648 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3650 /** If we are in website mode, then it is explicitly impossible to run GET or
3651 * HEAD on the actual directory. We must convert the request to run on the
3652 * suffix object instead!
3654 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3655 op
->set_get_data(get_data
);
3659 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3661 /** If we are in website mode, then it is explicitly impossible to run GET or
3662 * HEAD on the actual directory. We must convert the request to run on the
3663 * suffix object instead!
3665 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3666 op
->set_get_data(get_data
);
3670 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3672 /** If we are in website mode, then it is explicitly impossible to run GET or
3673 * HEAD on the actual directory. We must convert the request to run on the
3674 * suffix object instead!
3676 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3677 op
->set_get_data(get_data
);
3686 static rgw::auth::Completer::cmplptr_t
3687 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3693 AWSEngine::VersionAbstractor::auth_data_t
3694 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3698 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3700 if (version
== AwsVersion::V2
) {
3701 return get_auth_data_v2(s
);
3702 } else if (version
== AwsVersion::V4
) {
3703 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3705 /* FIXME(rzarzynski): handle anon user. */
3710 boost::optional
<std::string
>
3711 AWSGeneralAbstractor::get_v4_canonical_headers(
3712 const req_info
& info
,
3713 const boost::string_view
& signedheaders
,
3714 const bool using_qs
) const
3716 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3720 AWSEngine::VersionAbstractor::auth_data_t
3721 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3723 bool using_qs
) const
3725 boost::string_view access_key_id
;
3726 boost::string_view signed_hdrs
;
3728 boost::string_view date
;
3729 boost::string_view credential_scope
;
3730 boost::string_view client_signature
;
3732 int ret
= rgw::auth::s3::parse_credentials(s
->info
,
3743 /* craft canonical headers */
3744 boost::optional
<std::string
> canonical_headers
= \
3745 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3746 if (canonical_headers
) {
3747 ldout(s
->cct
, 10) << "canonical headers format = " << *canonical_headers
3753 /* Get the expected hash. */
3754 auto exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3756 /* Craft canonical URI. Using std::move later so let it be non-const. */
3757 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3759 /* Craft canonical query string. std::moving later so non-const here. */
3760 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3762 /* Craft canonical request. */
3763 auto canonical_req_hash
= \
3764 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3766 std::move(canonical_uri
),
3767 std::move(canonical_qs
),
3768 std::move(*canonical_headers
),
3772 auto string_to_sign
= \
3773 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3774 AWS4_HMAC_SHA256_STR
,
3777 std::move(canonical_req_hash
));
3779 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3781 std::placeholders::_1
,
3782 std::placeholders::_2
,
3783 std::placeholders::_3
);
3785 /* Requests authenticated with the Query Parameters are treated as unsigned.
3786 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3789 * You don't include a payload hash in the Canonical Request, because
3790 * when you create a presigned URL, you don't know the payload content
3791 * because the URL is used to upload an arbitrary payload. Instead, you
3792 * use a constant string UNSIGNED-PAYLOAD.
3794 * This means we have absolutely no business in spawning completer. Both
3795 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3796 * by default. We don't need to change that. */
3797 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
)) {
3801 std::move(string_to_sign
),
3803 null_completer_factory
3806 /* We're going to handle a signed payload. Be aware that even empty HTTP
3807 * body (no payload) requires verification:
3809 * The x-amz-content-sha256 header is required for all AWS Signature
3810 * Version 4 requests. It provides a hash of the request payload. If
3811 * there is no payload, you must provide the hash of an empty string. */
3812 if (!is_v4_payload_streamed(exp_payload_hash
)) {
3813 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
3815 /* payload in a single chunk */
3818 case RGW_OP_CREATE_BUCKET
:
3819 case RGW_OP_PUT_OBJ
:
3820 case RGW_OP_PUT_ACLS
:
3821 case RGW_OP_PUT_CORS
:
3822 case RGW_OP_INIT_MULTIPART
: // in case that Init Multipart uses CHUNK encoding
3823 case RGW_OP_COMPLETE_MULTIPART
:
3824 case RGW_OP_SET_BUCKET_VERSIONING
:
3825 case RGW_OP_DELETE_MULTI_OBJ
:
3826 case RGW_OP_ADMIN_SET_METADATA
:
3827 case RGW_OP_SET_BUCKET_WEBSITE
:
3828 case RGW_OP_PUT_BUCKET_POLICY
:
3829 case RGW_OP_PUT_OBJ_TAGGING
:
3833 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
3834 throw -ERR_NOT_IMPLEMENTED
;
3837 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
3839 std::placeholders::_1
);
3843 std::move(string_to_sign
),
3848 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3849 * it "chunked" but let's be coherent with Amazon's terminology. */
3851 dout(10) << "body content detected in multiple chunks" << dendl
;
3853 /* payload in multiple chunks */
3857 case RGW_OP_PUT_OBJ
:
3860 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
3861 throw -ERR_NOT_IMPLEMENTED
;
3864 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
3866 /* In the case of streamed payload client sets the x-amz-content-sha256
3867 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3868 * when constructing the Canonical Request. */
3870 /* In the case of single-chunk upload client set the header's value is
3871 * coherent with the one used for Canonical Request crafting. */
3873 /* In the case of query string-based authentication there should be no
3874 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3876 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
3881 std::placeholders::_1
);
3885 std::move(string_to_sign
),
3894 boost::optional
<std::string
>
3895 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3896 const req_info
& info
,
3897 const boost::string_view
& signedheaders
,
3898 const bool using_qs
) const
3900 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3905 AWSEngine::VersionAbstractor::auth_data_t
3906 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
3908 boost::string_view access_key_id
;
3909 boost::string_view signature
;
3912 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
3913 if (! http_auth
|| http_auth
[0] == '\0') {
3914 /* Credentials are provided in query string. We also need to verify
3915 * the "Expires" parameter now. */
3916 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
3917 signature
= s
->info
.args
.get("Signature");
3920 boost::string_view expires
= s
->info
.args
.get("Expires");
3921 if (expires
.empty()) {
3925 /* It looks we have the guarantee that expires is a null-terminated,
3926 * and thus string_view::data() can be safely used. */
3927 const time_t exp
= atoll(expires
.data());
3935 /* The "Authorization" HTTP header is being used. */
3936 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
3937 const size_t pos
= auth_str
.rfind(':');
3938 if (pos
!= boost::string_view::npos
) {
3939 access_key_id
= auth_str
.substr(0, pos
);
3940 signature
= auth_str
.substr(pos
+ 1);
3944 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3945 std::string string_to_sign
;
3946 utime_t header_time
;
3947 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
3949 ldout(cct
, 10) << "failed to create the canonized auth header\n"
3950 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3954 ldout(cct
, 10) << "string_to_sign:\n"
3955 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3957 if (!qsr
&& !is_time_skew_ok(header_time
)) {
3958 throw -ERR_REQUEST_TIME_SKEWED
;
3962 std::move(access_key_id
),
3963 std::move(signature
),
3964 std::move(string_to_sign
),
3965 rgw::auth::s3::get_v2_signature
,
3966 null_completer_factory
3971 AWSEngine::VersionAbstractor::auth_data_t
3972 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
3975 s
->auth
.s3_postobj_creds
.access_key
,
3976 s
->auth
.s3_postobj_creds
.signature
,
3977 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
3978 rgw::auth::s3::get_v2_signature
,
3979 null_completer_factory
3983 AWSEngine::VersionAbstractor::auth_data_t
3984 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
3986 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
3988 /* grab access key id */
3989 const size_t pos
= credential
.find("/");
3990 const boost::string_view access_key_id
= credential
.substr(0, pos
);
3991 dout(10) << "access key id = " << access_key_id
<< dendl
;
3993 /* grab credential scope */
3994 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
3995 dout(10) << "credential scope = " << credential_scope
<< dendl
;
3997 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3999 std::placeholders::_1
,
4000 std::placeholders::_2
,
4001 std::placeholders::_3
);
4005 s
->auth
.s3_postobj_creds
.signature
,
4006 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4008 null_completer_factory
4012 AWSEngine::VersionAbstractor::auth_data_t
4013 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
4015 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
4016 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
4017 << " (AWS4-HMAC-SHA256)" << dendl
;
4018 return get_auth_data_v4(s
);
4020 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
4021 return get_auth_data_v2(s
);
4027 AWSEngine::authenticate(const req_state
* const s
) const
4029 /* Small reminder: an ver_abstractor is allowed to throw! */
4030 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
4032 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
4033 return result_t::deny(-EINVAL
);
4035 return authenticate(auth_data
.access_key_id
,
4036 auth_data
.client_signature
,
4037 auth_data
.string_to_sign
,
4038 auth_data
.signature_factory
,
4039 auth_data
.completer_factory
,
4044 } /* namespace s3 */
4045 } /* namespace auth */
4046 } /* namespace rgw */
4048 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
4049 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
4051 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
4054 std::lock_guard
<std::mutex
> lck(mtx
);
4056 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
4057 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
4058 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
4059 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
4060 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
4061 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
4063 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
4064 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
4072 rgw::auth::RemoteApplier::acl_strategy_t
4073 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4075 //This is based on the assumption that the default acl strategy in
4076 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4080 rgw::auth::RemoteApplier::AuthInfo
4081 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
4083 /* The short form of "using" can't be used here -- we're aliasing a class'
4085 using acct_privilege_t
= \
4086 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4088 return rgw::auth::RemoteApplier::AuthInfo
{
4091 RGW_PERM_FULL_CONTROL
,
4092 acct_privilege_t::IS_PLAIN_ACCT
,
4097 rgw::auth::Engine::result_t
4098 rgw::auth::s3::LDAPEngine::authenticate(
4099 const boost::string_view
& access_key_id
,
4100 const boost::string_view
& signature
,
4101 const string_to_sign_t
& string_to_sign
,
4102 const signature_factory_t
&,
4103 const completer_factory_t
& completer_factory
,
4104 const req_state
* const s
) const
4106 /* boost filters and/or string_ref may throw on invalid input */
4107 rgw::RGWToken base64_token
;
4109 base64_token
= rgw::from_base64(access_key_id
);
4111 base64_token
= std::string("");
4114 if (! base64_token
.valid()) {
4115 return result_t::deny();
4118 //TODO: Uncomment, when we have a migration plan in place.
4119 //Check if a user of type other than 'ldap' is already present, if yes, then
4121 /*RGWUserInfo user_info;
4122 user_info.user_id = base64_token.id;
4123 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4124 if (user_info.type != TYPE_LDAP) {
4125 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4130 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
4131 return result_t::deny();
4134 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4135 get_creds_info(base64_token
));
4136 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4141 rgw::auth::Engine::result_t
4142 rgw::auth::s3::LocalEngine::authenticate(
4143 const boost::string_view
& _access_key_id
,
4144 const boost::string_view
& signature
,
4145 const string_to_sign_t
& string_to_sign
,
4146 const signature_factory_t
& signature_factory
,
4147 const completer_factory_t
& completer_factory
,
4148 const req_state
* const s
) const
4150 /* get the user info */
4151 RGWUserInfo user_info
;
4152 /* TODO(rzarzynski): we need to have string-view taking variant. */
4153 const std::string access_key_id
= _access_key_id
.to_string();
4154 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
4155 ldout(cct
, 5) << "error reading user info, uid=" << access_key_id
4156 << " can't authenticate" << dendl
;
4157 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
4159 //TODO: Uncomment, when we have a migration plan in place.
4161 if (s->user->type != TYPE_RGW) {
4162 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4163 << " is present" << dendl;
4168 const auto iter
= user_info
.access_keys
.find(access_key_id
);
4169 if (iter
== std::end(user_info
.access_keys
)) {
4170 ldout(cct
, 0) << "ERROR: access key not encoded in user info" << dendl
;
4171 return result_t::deny(-EPERM
);
4173 const RGWAccessKey
& k
= iter
->second
;
4175 const VersionAbstractor::server_signature_t server_signature
= \
4176 signature_factory(cct
, k
.key
, string_to_sign
);
4178 ldout(cct
, 15) << "string_to_sign="
4179 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4181 ldout(cct
, 15) << "server signature=" << server_signature
<< dendl
;
4182 ldout(cct
, 15) << "client signature=" << signature
<< dendl
;
4183 ldout(cct
, 15) << "compare=" << signature
.compare(server_signature
) << dendl
;
4185 if (static_cast<boost::string_view
>(server_signature
) != signature
) {
4186 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4189 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
);
4190 return result_t::grant(std::move(apl
), completer_factory(k
.key
));
4193 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4196 if (s
->op
== OP_OPTIONS
) {
4202 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
4204 return route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKOWN
;