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 "auth/Crypto.h"
14 #include <boost/algorithm/string.hpp>
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/utility/string_view.hpp>
17 #include <boost/tokenizer.hpp>
19 #include <liboath/oath.h>
22 #include "rgw_rest_s3.h"
23 #include "rgw_rest_s3website.h"
24 #include "rgw_auth_s3.h"
26 #include "rgw_policy_s3.h"
29 #include "rgw_cors_s3.h"
30 #include "rgw_tag_s3.h"
32 #include "rgw_client_io.h"
34 #include "rgw_keystone.h"
35 #include "rgw_auth_keystone.h"
36 #include "rgw_auth_registry.h"
38 #include "rgw_es_query.h"
40 #include <typeinfo> // for 'typeid'
43 #include "rgw_token.h"
44 #include "rgw_rest_role.h"
45 #include "rgw_crypt.h"
46 #include "rgw_crypt_sanitize.h"
47 #include "rgw_rest_user_policy.h"
50 #include "services/svc_zone.h"
52 #include "include/ceph_assert.h"
54 #include "rgw_rest_sts.h"
57 #define dout_context g_ceph_context
58 #define dout_subsys ceph_subsys_rgw
61 using namespace ceph::crypto
;
65 void list_all_buckets_start(struct req_state
*s
)
67 s
->formatter
->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3
);
70 void list_all_buckets_end(struct req_state
*s
)
72 s
->formatter
->close_section();
75 void dump_bucket(struct req_state
*s
, RGWBucketEnt
& obj
)
77 s
->formatter
->open_object_section("Bucket");
78 s
->formatter
->dump_string("Name", obj
.bucket
.name
);
79 dump_time(s
, "CreationDate", &obj
.creation_time
);
80 s
->formatter
->close_section();
83 void rgw_get_errno_s3(rgw_http_error
*e
, int err_no
)
85 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
);
87 if (r
!= rgw_http_s3_errors
.end()) {
88 e
->http_ret
= r
->second
.first
;
89 e
->s3_code
= r
->second
.second
;
92 e
->s3_code
= "UnknownError";
96 struct response_attr_param
{
98 const char *http_attr
;
101 static struct response_attr_param resp_attr_params
[] = {
102 {"response-content-type", "Content-Type"},
103 {"response-content-language", "Content-Language"},
104 {"response-expires", "Expires"},
105 {"response-cache-control", "Cache-Control"},
106 {"response-content-disposition", "Content-Disposition"},
107 {"response-content-encoding", "Content-Encoding"},
111 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist
& bl
, off_t bl_ofs
, off_t bl_len
) {
112 map
<string
, bufferlist
>::iterator iter
;
113 iter
= attrs
.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION
);
114 if (iter
!= attrs
.end()) {
115 bufferlist
&bl
= iter
->second
;
116 s
->redirect
= bl
.c_str();
117 s
->err
.http_ret
= 301;
118 ldout(s
->cct
, 20) << __CEPH_ASSERT_FUNCTION
<< " redirecting per x-amz-website-redirect-location=" << s
->redirect
<< dendl
;
119 op_ret
= -ERR_WEBSITE_REDIRECT
;
120 set_req_state_err(s
, op_ret
);
122 dump_content_length(s
, 0);
123 dump_redirect(s
, s
->redirect
);
127 return RGWGetObj_ObjStore_S3::send_response_data(bl
, bl_ofs
, bl_len
);
131 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
133 return RGWGetObj_ObjStore_S3::send_response_data_error();
136 int RGWGetObj_ObjStore_S3::get_params()
138 // for multisite sync requests, only read the slo manifest itself, rather than
139 // all of the data from its parts. the parts will sync as separate objects
140 skip_manifest
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"sync-manifest");
142 // multisite sync requests should fetch encrypted data, along with the
143 // attributes needed to support decryption on the other zone
144 if (s
->system_request
) {
145 skip_decrypt
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"skip-decrypt");
148 return RGWGetObj_ObjStore::get_params();
151 int RGWGetObj_ObjStore_S3::send_response_data_error()
154 return send_response_data(bl
, 0 , 0);
158 int decode_attr_bl_single_value(map
<string
, bufferlist
>& attrs
, const char *attr_name
, T
*result
, T def_val
)
160 map
<string
, bufferlist
>::iterator iter
= attrs
.find(attr_name
);
161 if (iter
== attrs
.end()) {
165 bufferlist
& bl
= iter
->second
;
166 if (bl
.length() == 0) {
170 auto bliter
= bl
.cbegin();
172 decode(*result
, bliter
);
173 } catch (buffer::error
& err
) {
179 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist
& bl
, off_t bl_ofs
,
182 const char *content_type
= NULL
;
183 string content_type_str
;
184 map
<string
, string
> response_attrs
;
185 map
<string
, string
>::iterator riter
;
186 bufferlist metadata_bl
;
191 if (custom_http_ret
) {
192 set_req_state_err(s
, 0);
193 dump_errno(s
, custom_http_ret
);
195 set_req_state_err(s
, (partial_content
&& !op_ret
) ? STATUS_PARTIAL_CONTENT
204 dump_range(s
, start
, end
, s
->obj_size
);
206 if (s
->system_request
&&
207 s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"prepend-metadata")) {
209 dump_header(s
, "Rgwx-Object-Size", (long long)total_len
);
213 * in this case, we're not returning the object's content, only the prepended
219 /* JSON encode object metadata */
221 jf
.open_object_section("obj_metadata");
222 encode_json("attrs", attrs
, &jf
);
224 encode_json("mtime", ut
, &jf
);
228 metadata_bl
.append(ss
.str());
229 dump_header(s
, "Rgwx-Embedded-Metadata-Len", metadata_bl
.length());
230 total_len
+= metadata_bl
.length();
233 if (s
->system_request
&& !real_clock::is_zero(lastmod
)) {
234 /* we end up dumping mtime in two different methods, a bit redundant */
235 dump_epoch_header(s
, "Rgwx-Mtime", lastmod
);
237 int r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_PG_VER
, &pg_ver
, (uint64_t)0);
239 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
241 dump_header(s
, "Rgwx-Obj-PG-Ver", pg_ver
);
243 uint32_t source_zone_short_id
= 0;
244 r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_SOURCE_ZONE
, &source_zone_short_id
, (uint32_t)0);
246 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
248 if (source_zone_short_id
!= 0) {
249 dump_header(s
, "Rgwx-Source-Zone-Short-Id", source_zone_short_id
);
253 for (auto &it
: crypt_http_responses
)
254 dump_header(s
, it
.first
, it
.second
);
256 dump_content_length(s
, total_len
);
257 dump_last_modified(s
, lastmod
);
258 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
259 if (attrs
.find(RGW_ATTR_APPEND_PART_NUM
) != attrs
.end()) {
260 dump_header(s
, "x-rgw-object-type", "Appendable");
261 dump_header(s
, "x-rgw-next-append-position", s
->obj_size
);
263 dump_header(s
, "x-rgw-object-type", "Normal");
266 if (! lo_etag
.empty()) {
267 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
268 * legit to perform GET on them through S3 API. In such situation,
269 * a client should receive the composited content with corresponding
271 dump_etag(s
, lo_etag
);
273 auto iter
= attrs
.find(RGW_ATTR_ETAG
);
274 if (iter
!= attrs
.end()) {
275 dump_etag(s
, iter
->second
.to_str());
279 for (struct response_attr_param
*p
= resp_attr_params
; p
->param
; p
++) {
281 string val
= s
->info
.args
.get(p
->param
, &exists
);
283 if (strcmp(p
->param
, "response-content-type") != 0) {
284 response_attrs
[p
->http_attr
] = val
;
286 content_type_str
= val
;
287 content_type
= content_type_str
.c_str();
292 for (auto iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
293 const char *name
= iter
->first
.c_str();
294 map
<string
, string
>::iterator aiter
= rgw_to_http_attrs
.find(name
);
295 if (aiter
!= rgw_to_http_attrs
.end()) {
296 if (response_attrs
.count(aiter
->second
) == 0) {
297 /* Was not already overridden by a response param. */
299 size_t len
= iter
->second
.length();
300 string
s(iter
->second
.c_str(), len
);
301 while (len
&& !s
[len
- 1]) {
305 response_attrs
[aiter
->second
] = s
;
307 } else if (iter
->first
.compare(RGW_ATTR_CONTENT_TYPE
) == 0) {
308 /* Special handling for content_type. */
310 content_type_str
= rgw_bl_str(iter
->second
);
311 content_type
= content_type_str
.c_str();
313 } else if (strcmp(name
, RGW_ATTR_SLO_UINDICATOR
) == 0) {
314 // this attr has an extra length prefix from encode() in prior versions
315 dump_header(s
, "X-Object-Meta-Static-Large-Object", "True");
316 } else if (strncmp(name
, RGW_ATTR_META_PREFIX
,
317 sizeof(RGW_ATTR_META_PREFIX
)-1) == 0) {
318 /* User custom metadata. */
319 name
+= sizeof(RGW_ATTR_PREFIX
) - 1;
320 dump_header(s
, name
, iter
->second
);
321 } else if (iter
->first
.compare(RGW_ATTR_TAGS
) == 0) {
324 auto it
= iter
->second
.cbegin();
326 } catch (buffer::error
&err
) {
327 ldout(s
->cct
,0) << "Error caught buffer::error couldn't decode TagSet " << dendl
;
329 dump_header(s
, RGW_AMZ_TAG_COUNT
, obj_tags
.count());
335 for (riter
= response_attrs
.begin(); riter
!= response_attrs
.end();
337 dump_header(s
, riter
->first
, riter
->second
);
340 if (op_ret
== -ERR_NOT_MODIFIED
) {
344 content_type
= "binary/octet-stream";
346 end_header(s
, this, content_type
);
349 if (metadata_bl
.length()) {
350 dump_body(s
, metadata_bl
);
355 if (get_data
&& !op_ret
) {
356 int r
= dump_body(s
, bl
.c_str() + bl_ofs
, bl_len
);
364 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr
<RGWGetObj_Filter
> *filter
, RGWGetObj_Filter
* cb
, bufferlist
* manifest_bl
)
366 if (skip_decrypt
) { // bypass decryption for multisite sync requests
371 std::unique_ptr
<BlockCrypt
> block_crypt
;
372 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses
);
374 if (block_crypt
!= nullptr) {
375 auto f
= std::make_unique
<RGWGetObj_BlockDecrypt
>(s
->cct
, cb
, std::move(block_crypt
));
376 if (manifest_bl
!= nullptr) {
377 res
= f
->read_manifest(*manifest_bl
);
379 *filter
= std::move(f
);
387 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist
& bl
)
390 end_header(s
, this, "application/xml");
393 s
->formatter
->open_object_section_in_ns("Tagging", XMLNS_AWS_S3
);
394 s
->formatter
->open_object_section("TagSet");
396 RGWObjTagSet_S3 tagset
;
397 auto iter
= bl
.cbegin();
400 } catch (buffer::error
& err
) {
401 ldout(s
->cct
,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl
;
405 tagset
.dump_xml(s
->formatter
);
407 s
->formatter
->close_section();
408 s
->formatter
->close_section();
409 rgw_flush_formatter_and_reset(s
, s
->formatter
);
413 int RGWPutObjTags_ObjStore_S3::get_params()
421 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
425 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
430 if (!parser
.parse(data
.c_str(), data
.length(), 1)) {
431 return -ERR_MALFORMED_XML
;
434 RGWObjTagging_S3 tagging
;
437 RGWXMLDecoder::decode_xml("Tagging", tagging
, &parser
);
438 } catch (RGWXMLDecoder::err
& err
) {
439 ldout(s
->cct
, 5) << "Malformed tagging request: " << err
<< dendl
;
440 return -ERR_MALFORMED_XML
;
444 r
= tagging
.rebuild(obj_tags
);
448 obj_tags
.encode(tags_bl
);
449 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
454 void RGWPutObjTags_ObjStore_S3::send_response()
457 set_req_state_err(s
, op_ret
);
459 end_header(s
, this, "application/xml");
464 void RGWDeleteObjTags_ObjStore_S3::send_response()
470 r
= STATUS_NO_CONTENT
;
472 set_req_state_err(s
, r
);
477 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets
)
480 set_req_state_err(s
, op_ret
);
483 // Explicitly use chunked transfer encoding so that we can stream the result
484 // to the user without having to wait for the full length of it.
485 end_header(s
, NULL
, "application/xml", CHUNKED_TRANSFER_ENCODING
);
488 list_all_buckets_start(s
);
489 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
490 s
->formatter
->open_array_section("Buckets");
495 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets
& buckets
)
500 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
501 map
<string
, RGWBucketEnt
>::iterator iter
;
503 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
504 RGWBucketEnt obj
= iter
->second
;
507 rgw_flush_formatter(s
, s
->formatter
);
510 void RGWListBuckets_ObjStore_S3::send_response_end()
513 s
->formatter
->close_section();
514 list_all_buckets_end(s
);
515 rgw_flush_formatter_and_reset(s
, s
->formatter
);
519 int RGWGetUsage_ObjStore_S3::get_params()
521 start_date
= s
->info
.args
.get("start-date");
522 end_date
= s
->info
.args
.get("end-date");
526 static void dump_usage_categories_info(Formatter
*formatter
, const rgw_usage_log_entry
& entry
, map
<string
, bool> *categories
)
528 formatter
->open_array_section("categories");
529 map
<string
, rgw_usage_data
>::const_iterator uiter
;
530 for (uiter
= entry
.usage_map
.begin(); uiter
!= entry
.usage_map
.end(); ++uiter
) {
531 if (categories
&& !categories
->empty() && !categories
->count(uiter
->first
))
533 const rgw_usage_data
& usage
= uiter
->second
;
534 formatter
->open_object_section("Entry");
535 formatter
->dump_string("Category", uiter
->first
);
536 formatter
->dump_int("BytesSent", usage
.bytes_sent
);
537 formatter
->dump_int("BytesReceived", usage
.bytes_received
);
538 formatter
->dump_int("Ops", usage
.ops
);
539 formatter
->dump_int("SuccessfulOps", usage
.successful_ops
);
540 formatter
->close_section(); // Entry
542 formatter
->close_section(); // Category
545 static void dump_usage_bucket_info(Formatter
*formatter
, const std::string
& name
, const cls_user_bucket_entry
& entry
)
547 formatter
->open_object_section("Entry");
548 formatter
->dump_string("Bucket", name
);
549 formatter
->dump_int("Bytes", entry
.size
);
550 formatter
->dump_int("Bytes_Rounded", entry
.size_rounded
);
551 formatter
->close_section(); // entry
554 void RGWGetUsage_ObjStore_S3::send_response()
557 set_req_state_err(s
, op_ret
);
560 // Explicitly use chunked transfer encoding so that we can stream the result
561 // to the user without having to wait for the full length of it.
562 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
567 Formatter
*formatter
= s
->formatter
;
569 bool user_section_open
= false;
571 formatter
->open_object_section("Usage");
572 if (show_log_entries
) {
573 formatter
->open_array_section("Entries");
575 map
<rgw_user_bucket
, rgw_usage_log_entry
>::iterator iter
;
576 for (iter
= usage
.begin(); iter
!= usage
.end(); ++iter
) {
577 const rgw_user_bucket
& ub
= iter
->first
;
578 const rgw_usage_log_entry
& entry
= iter
->second
;
580 if (show_log_entries
) {
581 if (ub
.user
.compare(last_owner
) != 0) {
582 if (user_section_open
) {
583 formatter
->close_section();
584 formatter
->close_section();
586 formatter
->open_object_section("User");
587 formatter
->dump_string("Owner", ub
.user
);
588 formatter
->open_array_section("Buckets");
589 user_section_open
= true;
590 last_owner
= ub
.user
;
592 formatter
->open_object_section("Bucket");
593 formatter
->dump_string("Bucket", ub
.bucket
);
594 utime_t
ut(entry
.epoch
, 0);
595 ut
.gmtime(formatter
->dump_stream("Time"));
596 formatter
->dump_int("Epoch", entry
.epoch
);
597 dump_usage_categories_info(formatter
, entry
, &categories
);
598 formatter
->close_section(); // bucket
601 summary_map
[ub
.user
].aggregate(entry
, &categories
);
604 if (show_log_entries
) {
605 if (user_section_open
) {
606 formatter
->close_section(); // buckets
607 formatter
->close_section(); //user
609 formatter
->close_section(); // entries
613 formatter
->open_array_section("Summary");
614 map
<string
, rgw_usage_log_entry
>::iterator siter
;
615 for (siter
= summary_map
.begin(); siter
!= summary_map
.end(); ++siter
) {
616 const rgw_usage_log_entry
& entry
= siter
->second
;
617 formatter
->open_object_section("User");
618 formatter
->dump_string("User", siter
->first
);
619 dump_usage_categories_info(formatter
, entry
, &categories
);
620 rgw_usage_data total_usage
;
621 entry
.sum(total_usage
, categories
);
622 formatter
->open_object_section("Total");
623 formatter
->dump_int("BytesSent", total_usage
.bytes_sent
);
624 formatter
->dump_int("BytesReceived", total_usage
.bytes_received
);
625 formatter
->dump_int("Ops", total_usage
.ops
);
626 formatter
->dump_int("SuccessfulOps", total_usage
.successful_ops
);
627 formatter
->close_section(); // total
628 formatter
->close_section(); // user
631 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
632 formatter
->open_object_section("Stats");
635 formatter
->dump_int("TotalBytes", header
.stats
.total_bytes
);
636 formatter
->dump_int("TotalBytesRounded", header
.stats
.total_bytes_rounded
);
637 formatter
->dump_int("TotalEntries", header
.stats
.total_entries
);
639 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
640 formatter
->close_section(); //Stats
643 formatter
->close_section(); // summary
646 formatter
->open_array_section("CapacityUsed");
647 formatter
->open_object_section("User");
648 formatter
->open_array_section("Buckets");
649 for (const auto& biter
: buckets_usage
) {
650 const cls_user_bucket_entry
& entry
= biter
.second
;
651 dump_usage_bucket_info(formatter
, biter
.first
, entry
);
653 formatter
->close_section(); // Buckets
654 formatter
->close_section(); // User
655 formatter
->close_section(); // CapacityUsed
657 formatter
->close_section(); // usage
658 rgw_flush_formatter_and_reset(s
, s
->formatter
);
661 int RGWListBucket_ObjStore_S3::get_params()
663 list_versions
= s
->info
.args
.exists("versions");
664 prefix
= s
->info
.args
.get("prefix");
665 if (!list_versions
) {
666 marker
= s
->info
.args
.get("marker");
668 marker
.name
= s
->info
.args
.get("key-marker");
669 marker
.instance
= s
->info
.args
.get("version-id-marker");
673 s
->info
.args
.get_bool("allow-unordered", &allow_unordered
, false);
675 delimiter
= s
->info
.args
.get("delimiter");
677 max_keys
= s
->info
.args
.get("max-keys");
678 op_ret
= parse_max_keys();
683 encoding_type
= s
->info
.args
.get("encoding-type");
684 if (s
->system_request
) {
685 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
686 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
689 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
691 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
695 shard_id
= s
->bucket_instance_shard_id
;
702 void RGWListBucket_ObjStore_S3::send_versioned_response()
704 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
705 if (!s
->bucket_tenant
.empty())
706 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
707 s
->formatter
->dump_string("Name", s
->bucket_name
);
708 s
->formatter
->dump_string("Prefix", prefix
);
709 s
->formatter
->dump_string("KeyMarker", marker
.name
);
710 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
711 if (is_truncated
&& !next_marker
.empty()) {
712 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
713 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
715 s
->formatter
->dump_int("MaxKeys", max
);
716 if (!delimiter
.empty())
717 s
->formatter
->dump_string("Delimiter", delimiter
);
719 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
722 bool encode_key
= false;
723 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
724 s
->formatter
->dump_string("EncodingType", "url");
729 if (objs_container
) {
730 s
->formatter
->open_array_section("Entries");
733 vector
<rgw_bucket_dir_entry
>::iterator iter
;
734 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
735 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
737 s
->formatter
->open_object_section(section_name
);
738 if (objs_container
) {
739 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
741 rgw_obj_key
key(iter
->key
);
744 url_encode(key
.name
, key_name
);
745 s
->formatter
->dump_string("Key", key_name
);
747 s
->formatter
->dump_string("Key", key
.name
);
749 string version_id
= key
.instance
;
750 if (version_id
.empty()) {
753 if (s
->system_request
) {
754 if (iter
->versioned_epoch
> 0) {
755 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
757 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
758 utime_t
ut(iter
->meta
.mtime
);
759 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
761 s
->formatter
->dump_string("VersionId", version_id
);
762 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
763 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
764 if (!iter
->is_delete_marker()) {
765 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
766 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
767 auto& storage_class
= rgw_placement_rule::get_canonical_storage_class(iter
->meta
.storage_class
);
768 s
->formatter
->dump_string("StorageClass", storage_class
.c_str());
770 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
771 if (iter
->meta
.appendable
) {
772 s
->formatter
->dump_string("Type", "Appendable");
774 s
->formatter
->dump_string("Type", "Normal");
776 s
->formatter
->close_section();
778 if (objs_container
) {
779 s
->formatter
->close_section();
782 if (!common_prefixes
.empty()) {
783 map
<string
, bool>::iterator pref_iter
;
784 for (pref_iter
= common_prefixes
.begin();
785 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
786 s
->formatter
->open_array_section("CommonPrefixes");
787 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
788 s
->formatter
->close_section();
792 s
->formatter
->close_section();
793 rgw_flush_formatter_and_reset(s
, s
->formatter
);
796 void RGWListBucket_ObjStore_S3::send_response()
799 set_req_state_err(s
, op_ret
);
802 // Explicitly use chunked transfer encoding so that we can stream the result
803 // to the user without having to wait for the full length of it.
804 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
810 send_versioned_response();
814 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
815 if (!s
->bucket_tenant
.empty())
816 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
817 s
->formatter
->dump_string("Name", s
->bucket_name
);
818 s
->formatter
->dump_string("Prefix", prefix
);
819 s
->formatter
->dump_string("Marker", marker
.name
);
820 if (is_truncated
&& !next_marker
.empty())
821 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
822 s
->formatter
->dump_int("MaxKeys", max
);
823 if (!delimiter
.empty())
824 s
->formatter
->dump_string("Delimiter", delimiter
);
826 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
829 bool encode_key
= false;
830 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
831 s
->formatter
->dump_string("EncodingType", "url");
836 vector
<rgw_bucket_dir_entry
>::iterator iter
;
837 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
838 rgw_obj_key
key(iter
->key
);
839 s
->formatter
->open_array_section("Contents");
842 url_encode(key
.name
, key_name
);
843 s
->formatter
->dump_string("Key", key_name
);
845 s
->formatter
->dump_string("Key", key
.name
);
847 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
848 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
849 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
850 auto& storage_class
= rgw_placement_rule::get_canonical_storage_class(iter
->meta
.storage_class
);
851 s
->formatter
->dump_string("StorageClass", storage_class
.c_str());
852 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
853 if (s
->system_request
) {
854 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
856 if (iter
->meta
.appendable
) {
857 s
->formatter
->dump_string("Type", "Appendable");
859 s
->formatter
->dump_string("Type", "Normal");
861 s
->formatter
->close_section();
863 if (!common_prefixes
.empty()) {
864 map
<string
, bool>::iterator pref_iter
;
865 for (pref_iter
= common_prefixes
.begin();
866 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
867 s
->formatter
->open_array_section("CommonPrefixes");
868 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
869 s
->formatter
->close_section();
873 s
->formatter
->close_section();
874 rgw_flush_formatter_and_reset(s
, s
->formatter
);
877 void RGWGetBucketLogging_ObjStore_S3::send_response()
880 end_header(s
, this, "application/xml");
883 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
884 s
->formatter
->close_section();
885 rgw_flush_formatter_and_reset(s
, s
->formatter
);
888 void RGWGetBucketLocation_ObjStore_S3::send_response()
894 RGWZoneGroup zonegroup
;
897 int ret
= store
->svc
.zone
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
899 api_name
= zonegroup
.api_name
;
901 if (s
->bucket_info
.zonegroup
!= "default") {
902 api_name
= s
->bucket_info
.zonegroup
;
906 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
907 "%s", api_name
.c_str());
908 rgw_flush_formatter_and_reset(s
, s
->formatter
);
911 void RGWGetBucketVersioning_ObjStore_S3::send_response()
914 end_header(s
, this, "application/xml");
917 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
919 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
920 s
->formatter
->dump_string("Status", status
);
921 const char *mfa_status
= (mfa_enabled
? "Enabled" : "Disabled");
922 s
->formatter
->dump_string("MfaDelete", mfa_status
);
924 s
->formatter
->close_section();
925 rgw_flush_formatter_and_reset(s
, s
->formatter
);
928 struct ver_config_status
{
929 int status
{VersioningSuspended
};
935 } mfa_status
{MFA_UNKNOWN
};
938 void decode_xml(XMLObj
*obj
) {
941 RGWXMLDecoder::decode_xml("Status", status_str
, obj
);
942 if (status_str
== "Enabled") {
943 status
= VersioningEnabled
;
944 } else if (status_str
!= "Suspended") {
945 status
= VersioningStatusInvalid
;
949 if (RGWXMLDecoder::decode_xml("MfaDelete", mfa_str
, obj
)) {
950 if (mfa_str
== "Enabled") {
951 mfa_status
= MFA_ENABLED
;
952 } else if (mfa_str
== "Disabled") {
953 mfa_status
= MFA_DISABLED
;
961 int RGWSetBucketVersioning_ObjStore_S3::get_params()
966 rgw_rest_read_all_input(s
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
971 r
= do_aws4_auth_completion();
976 RGWXMLDecoder::XMLParser parser
;
977 if (!parser
.init()) {
978 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
982 char* buf
= data
.c_str();
983 if (!parser
.parse(buf
, data
.length(), 1)) {
984 ldout(s
->cct
, 10) << "NOTICE: failed to parse data: " << buf
<< dendl
;
989 ver_config_status status_conf
;
991 if (!RGWXMLDecoder::decode_xml("VersioningConfiguration", status_conf
, &parser
)) {
992 ldout(s
->cct
, 10) << "NOTICE: bad versioning config input" << dendl
;
996 if (!store
->svc
.zone
->is_meta_master()) {
997 /* only need to keep this data around if we're not meta master */
998 in_data
.append(data
);
1001 versioning_status
= status_conf
.status
;
1002 if (versioning_status
== VersioningStatusInvalid
) {
1006 if (status_conf
.mfa_status
!= ver_config_status::MFA_UNKNOWN
) {
1007 mfa_set_status
= true;
1008 switch (status_conf
.mfa_status
) {
1009 case ver_config_status::MFA_DISABLED
:
1012 case ver_config_status::MFA_ENABLED
:
1016 ldout(s
->cct
, 0) << "ERROR: RGWSetBucketVersioning_ObjStore_S3::get_params(): unexpected switch case mfa_status=" << status_conf
.mfa_status
<< dendl
;
1019 } else if (status_conf
.retcode
< 0) {
1020 r
= status_conf
.retcode
;
1025 void RGWSetBucketVersioning_ObjStore_S3::send_response()
1028 set_req_state_err(s
, op_ret
);
1030 end_header(s
, this, "application/xml");
1033 int RGWSetBucketWebsite_ObjStore_S3::get_params()
1035 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1039 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
1045 r
= do_aws4_auth_completion();
1050 in_data
.append(data
);
1052 RGWXMLDecoder::XMLParser parser
;
1053 if (!parser
.init()) {
1054 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1058 char* buf
= data
.c_str();
1059 if (!parser
.parse(buf
, data
.length(), 1)) {
1060 ldout(s
->cct
, 5) << "failed to parse xml: " << buf
<< dendl
;
1065 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
1066 } catch (RGWXMLDecoder::err
& err
) {
1067 ldout(s
->cct
, 5) << "unexpected xml: " << buf
<< dendl
;
1071 #define WEBSITE_ROUTING_RULES_MAX_NUM 50
1072 int max_num
= s
->cct
->_conf
->rgw_website_routing_rules_max_num
;
1074 max_num
= WEBSITE_ROUTING_RULES_MAX_NUM
;
1076 int routing_rules_num
= website_conf
.routing_rules
.rules
.size();
1077 if (routing_rules_num
> max_num
) {
1078 ldout(s
->cct
, 4) << "An website routing config can have up to "
1080 << " rules, request website routing rules num: "
1081 << routing_rules_num
<< dendl
;
1082 op_ret
= -ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR
;
1083 s
->err
.message
= std::to_string(routing_rules_num
) +" routing rules provided, the number of routing rules in a website configuration is limited to "
1084 + std::to_string(max_num
)
1086 return -ERR_INVALID_REQUEST
;
1092 void RGWSetBucketWebsite_ObjStore_S3::send_response()
1095 set_req_state_err(s
, op_ret
);
1097 end_header(s
, this, "application/xml");
1100 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
1103 op_ret
= STATUS_NO_CONTENT
;
1105 set_req_state_err(s
, op_ret
);
1107 end_header(s
, this, "application/xml");
1110 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1113 set_req_state_err(s
, op_ret
);
1115 end_header(s
, this, "application/xml");
1122 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
1124 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
1125 conf
.dump_xml(s
->formatter
);
1126 s
->formatter
->close_section(); // WebsiteConfiguration
1127 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1130 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
1132 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
1133 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
1136 void RGWStatBucket_ObjStore_S3::send_response()
1139 dump_bucket_metadata(s
, bucket
);
1142 set_req_state_err(s
, op_ret
);
1145 end_header(s
, this);
1149 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
1150 RGWAccessControlPolicy_S3
& s3policy
,
1153 if (s
->has_acl_header
) {
1154 if (!s
->canned_acl
.empty())
1155 return -ERR_INVALID_REQUEST
;
1157 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
1160 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
1163 class RGWLocationConstraint
: public XMLObj
1166 RGWLocationConstraint() {}
1167 ~RGWLocationConstraint() override
{}
1168 bool xml_end(const char *el
) override
{
1172 location_constraint
= get_data();
1177 string location_constraint
;
1180 class RGWCreateBucketConfig
: public XMLObj
1183 RGWCreateBucketConfig() {}
1184 ~RGWCreateBucketConfig() override
{}
1187 class RGWCreateBucketParser
: public RGWXMLParser
1189 XMLObj
*alloc_obj(const char *el
) override
{
1194 RGWCreateBucketParser() {}
1195 ~RGWCreateBucketParser() override
{}
1197 bool get_location_constraint(string
& zone_group
) {
1198 XMLObj
*config
= find_first("CreateBucketConfiguration");
1202 XMLObj
*constraint
= config
->find_first("LocationConstraint");
1206 zone_group
= constraint
->get_data();
1212 int RGWCreateBucket_ObjStore_S3::get_params()
1214 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1216 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1222 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1226 std::tie(op_ret
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
1228 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1231 const int auth_ret
= do_aws4_auth_completion();
1236 in_data
.append(data
);
1238 if (data
.length()) {
1239 RGWCreateBucketParser parser
;
1241 if (!parser
.init()) {
1242 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1246 char* buf
= data
.c_str();
1247 bool success
= parser
.parse(buf
, data
.length(), 1);
1248 ldout(s
->cct
, 20) << "create bucket input data=" << buf
<< dendl
;
1251 ldout(s
->cct
, 0) << "failed to parse input: " << buf
<< dendl
;
1255 if (!parser
.get_location_constraint(location_constraint
)) {
1256 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1260 ldout(s
->cct
, 10) << "create bucket location constraint: "
1261 << location_constraint
<< dendl
;
1264 size_t pos
= location_constraint
.find(':');
1265 if (pos
!= string::npos
) {
1266 placement_rule
.init(location_constraint
.substr(pos
+ 1), s
->info
.storage_class
);
1267 location_constraint
= location_constraint
.substr(0, pos
);
1269 placement_rule
.storage_class
= s
->info
.storage_class
;
1275 void RGWCreateBucket_ObjStore_S3::send_response()
1277 if (op_ret
== -ERR_BUCKET_EXISTS
)
1280 set_req_state_err(s
, op_ret
);
1287 if (s
->system_request
) {
1288 JSONFormatter f
; /* use json formatter for system requests output */
1290 f
.open_object_section("info");
1291 encode_json("entry_point_object_ver", ep_objv
, &f
);
1292 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1293 encode_json("bucket_info", info
, &f
);
1295 rgw_flush_formatter_and_reset(s
, &f
);
1299 void RGWDeleteBucket_ObjStore_S3::send_response()
1303 r
= STATUS_NO_CONTENT
;
1305 set_req_state_err(s
, r
);
1307 end_header(s
, this);
1309 if (s
->system_request
) {
1310 JSONFormatter f
; /* use json formatter for system requests output */
1312 f
.open_object_section("info");
1313 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1315 rgw_flush_formatter_and_reset(s
, &f
);
1319 static inline void map_qs_metadata(struct req_state
* s
)
1321 /* merge S3 valid user metadata from the query-string into
1322 * x_meta_map, which maps them to attributes */
1323 const auto& params
= const_cast<RGWHTTPArgs
&>(s
->info
.args
).get_params();
1324 for (const auto& elt
: params
) {
1325 std::string k
= boost::algorithm::to_lower_copy(elt
.first
);
1326 if (k
.find("x-amz-meta-") == /* offset */ 0) {
1327 add_amz_meta_header(s
->info
.x_meta_map
, k
, elt
.second
);
1332 int RGWPutObj_ObjStore_S3::get_params()
1335 return -ERR_LENGTH_REQUIRED
;
1337 map
<string
, bufferlist
> src_attrs
;
1343 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1344 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1350 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1351 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1352 copy_source
= url_decode(s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE", ""));
1353 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1355 /* handle x-amz-copy-source */
1356 boost::string_view
cs_view(copy_source
);
1357 if (! cs_view
.empty()) {
1358 if (cs_view
[0] == '/')
1359 cs_view
.remove_prefix(1);
1360 copy_source_bucket_name
= cs_view
.to_string();
1361 pos
= copy_source_bucket_name
.find("/");
1362 if (pos
== std::string::npos
) {
1364 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1367 copy_source_object_name
=
1368 copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1369 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1370 #define VERSION_ID_STR "?versionId="
1371 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1372 if (pos
== std::string::npos
) {
1373 copy_source_object_name
= url_decode(copy_source_object_name
);
1375 copy_source_version_id
=
1376 copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1377 copy_source_object_name
=
1378 url_decode(copy_source_object_name
.substr(0, pos
));
1380 pos
= copy_source_bucket_name
.find(":");
1381 if (pos
== std::string::npos
) {
1382 copy_source_tenant_name
= s
->src_tenant_name
;
1384 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1385 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1386 if (copy_source_bucket_name
.empty()) {
1388 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1392 ret
= store
->get_bucket_info(*s
->sysobj_ctx
,
1393 copy_source_tenant_name
,
1394 copy_source_bucket_name
,
1395 copy_source_bucket_info
,
1398 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1402 /* handle x-amz-copy-source-range */
1404 if (copy_source_range
) {
1405 string range
= copy_source_range
;
1406 pos
= range
.find("=");
1407 if (pos
== std::string::npos
) {
1409 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1412 range
= range
.substr(pos
+ 1);
1413 pos
= range
.find("-");
1414 if (pos
== std::string::npos
) {
1416 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1419 string first
= range
.substr(0, pos
);
1420 string last
= range
.substr(pos
+ 1);
1421 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1422 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1427 /* handle object tagging */
1428 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
1430 obj_tags
= std::make_unique
<RGWObjTags
>();
1431 ret
= obj_tags
->set_from_string(tag_str
);
1433 ldout(s
->cct
,0) << "setting obj tags failed with " << ret
<< dendl
;
1434 if (ret
== -ERR_INVALID_TAG
){
1435 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
1442 multipart_upload_id
= s
->info
.args
.get("uploadId");
1443 multipart_part_str
= s
->info
.args
.get("partNumber");
1444 if (!multipart_part_str
.empty()) {
1446 multipart_part_num
= strict_strtol(multipart_part_str
.c_str(), 10, &err
);
1448 ldpp_dout(s
, 10) << "bad part number: " << multipart_part_str
<< ": " << err
<< dendl
;
1451 } else if (!multipart_upload_id
.empty()) {
1452 ldpp_dout(s
, 10) << "part number with no multipart upload id" << dendl
;
1456 append
= s
->info
.args
.exists("append");
1458 string pos_str
= s
->info
.args
.get("position");
1459 if (pos_str
.empty()) {
1462 position
= strtoull(pos_str
.c_str(), NULL
, 10);
1466 return RGWPutObj_ObjStore::get_params();
1469 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1471 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1473 const int ret_auth
= do_aws4_auth_completion();
1482 static int get_success_retcode(int code
)
1486 return STATUS_CREATED
;
1488 return STATUS_NO_CONTENT
;
1493 void RGWPutObj_ObjStore_S3::send_response()
1496 set_req_state_err(s
, op_ret
);
1499 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1500 op_ret
= get_success_retcode(
1501 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1502 set_req_state_err(s
, op_ret
);
1504 if (copy_source
.empty()) {
1507 dump_content_length(s
, 0);
1508 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
1509 for (auto &it
: crypt_http_responses
)
1510 dump_header(s
, it
.first
, it
.second
);
1513 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
1514 end_header(s
, this, "application/xml");
1518 time_t secs
= (time_t)ut
.sec();
1519 gmtime_r(&secs
, &tmp
);
1520 char buf
[TIME_BUF_SIZE
];
1521 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1522 "http://s3.amazonaws.com/doc/2006-03-01/");
1523 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1524 s
->formatter
->dump_string("LastModified", buf
);
1526 s
->formatter
->dump_string("ETag", etag
);
1527 s
->formatter
->close_section();
1528 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1533 if (op_ret
== 0 || op_ret
== -ERR_POSITION_NOT_EQUAL_TO_LENGTH
) {
1534 dump_header(s
, "x-rgw-next-append-position", cur_accounted_size
);
1537 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1538 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1540 end_header(s
, this);
1543 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1545 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1546 RGWRados::Object::Read
read_op(&op_target
);
1548 read_op
.params
.attrs
= &attrs
;
1550 return read_op
.prepare();
1553 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1557 attrs
.emplace(key
, std::move(bl
));
1560 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1564 attrs
.emplace(key
, std::move(bl
));
1567 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1568 std::unique_ptr
<RGWGetObj_Filter
>* filter
,
1569 RGWGetObj_Filter
* cb
,
1570 map
<string
, bufferlist
>& attrs
,
1571 bufferlist
* manifest_bl
)
1573 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1576 std::unique_ptr
<BlockCrypt
> block_crypt
;
1577 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1579 if (block_crypt
!= nullptr) {
1580 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1581 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1583 if (manifest_bl
!= nullptr) {
1584 res
= f
->read_manifest(*manifest_bl
);
1586 *filter
= std::move(f
);
1595 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1596 std::unique_ptr
<rgw::putobj::DataProcessor
> *filter
,
1597 rgw::putobj::DataProcessor
*cb
)
1600 if (!multipart_upload_id
.empty()) {
1601 RGWMPObj
mp(s
->object
.name
, multipart_upload_id
);
1603 obj
.init_ns(s
->bucket
, mp
.get_meta(), RGW_OBJ_NS_MULTIPART
);
1604 obj
.set_in_extra_data(true);
1605 map
<string
, bufferlist
> xattrs
;
1606 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1608 std::unique_ptr
<BlockCrypt
> block_crypt
;
1609 /* We are adding to existing object.
1610 * We use crypto mode that configured as if we were decrypting. */
1611 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
1612 if (res
== 0 && block_crypt
!= nullptr)
1613 filter
->reset(new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1615 /* it is ok, to not have encryption at all */
1619 std::unique_ptr
<BlockCrypt
> block_crypt
;
1620 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1621 if (res
== 0 && block_crypt
!= nullptr) {
1622 filter
->reset(new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1628 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1630 static string var
= "${filename}";
1631 int pos
= key
.find(var
);
1635 string new_key
= key
.substr(0, pos
);
1636 new_key
.append(filename
);
1637 new_key
.append(key
.substr(pos
+ var
.size()));
1642 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1644 return s
->object
.name
;
1647 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1649 return content_type
;
1652 int RGWPostObj_ObjStore_S3::get_params()
1654 op_ret
= RGWPostObj_ObjStore::get_params();
1661 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1663 env
.add_var("bucket", s
->bucket
.name
);
1667 struct post_form_part part
;
1668 int r
= read_form_part_header(&part
, done
);
1672 if (s
->cct
->_conf
->subsys
.should_gather
<ceph_subsys_rgw
, 20>()) {
1673 ldout(s
->cct
, 20) << "read part header -- part.name="
1674 << part
.name
<< dendl
;
1676 for (const auto& pair
: part
.fields
) {
1677 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1678 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1679 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1681 for (const auto& param_pair
: pair
.second
.params
) {
1682 ldout(s
->cct
, 20) << " " << param_pair
.first
1683 << " -> " << param_pair
.second
<< dendl
;
1688 if (done
) { /* unexpected here */
1689 err_msg
= "Malformed request";
1693 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1694 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1695 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1696 if (iter
!= field
.params
.end()) {
1697 filename
= iter
->second
;
1699 parts
[part
.name
] = part
;
1704 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1705 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1706 if (r
< 0 || !boundary
) {
1707 err_msg
= "Couldn't find boundary";
1710 parts
[part
.name
] = part
;
1711 string
part_str(part
.data
.c_str(), part
.data
.length());
1712 env
.add_var(part
.name
, part_str
);
1716 if (!part_str(parts
, "key", &object_str
)) {
1717 err_msg
= "Key not specified";
1721 s
->object
= rgw_obj_key(object_str
);
1723 rebuild_key(s
->object
.name
);
1725 if (s
->object
.empty()) {
1726 err_msg
= "Empty object name";
1730 env
.add_var("key", s
->object
.name
);
1732 part_str(parts
, "Content-Type", &content_type
);
1734 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
1735 if (! content_type
.empty()) {
1736 env
.add_var("Content-Type", content_type
);
1739 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1740 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1741 for (; piter
!= parts
.end(); ++piter
) {
1742 string n
= piter
->first
;
1743 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1744 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1747 string attr_name
= RGW_ATTR_PREFIX
;
1748 attr_name
.append(n
);
1750 /* need to null terminate it */
1751 bufferlist
& data
= piter
->second
.data
;
1752 string str
= string(data
.c_str(), data
.length());
1755 attr_bl
.append(str
.c_str(), str
.size() + 1);
1757 attrs
[attr_name
] = attr_bl
;
1759 // TODO: refactor this and the above loop to share code
1760 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1761 if (piter
!= parts
.end()) {
1762 string n
= piter
->first
;
1763 string attr_name
= RGW_ATTR_PREFIX
;
1764 attr_name
.append(n
);
1765 /* need to null terminate it */
1766 bufferlist
& data
= piter
->second
.data
;
1767 string str
= string(data
.c_str(), data
.length());
1770 attr_bl
.append(str
.c_str(), str
.size() + 1);
1772 attrs
[attr_name
] = attr_bl
;
1775 int r
= get_policy();
1784 min_len
= post_policy
.min_length
;
1785 max_len
= post_policy
.max_length
;
1792 int RGWPostObj_ObjStore_S3::get_tags()
1795 if (part_str(parts
, "tagging", &tags_str
)) {
1796 RGWXMLParser parser
;
1797 if (!parser
.init()){
1798 ldout(s
->cct
, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
1799 err_msg
= "Server couldn't process the request";
1800 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
1802 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
1803 ldout(s
->cct
,0 ) << "Invalid Tagging XML" << dendl
;
1804 err_msg
= "Invalid Tagging XML";
1808 RGWObjTagging_S3 tagging
;
1811 RGWXMLDecoder::decode_xml("Tagging", tagging
, &parser
);
1812 } catch (RGWXMLDecoder::err
& err
) {
1813 ldout(s
->cct
, 5) << "Malformed tagging request: " << err
<< dendl
;
1817 RGWObjTags obj_tags
;
1818 int r
= tagging
.rebuild(obj_tags
);
1823 obj_tags
.encode(tags_bl
);
1824 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
1825 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
1832 int RGWPostObj_ObjStore_S3::get_policy()
1834 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1835 bool aws4_auth
= false;
1837 /* x-amz-algorithm handling */
1838 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1839 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1840 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1841 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1844 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1847 // check that the signature matches the encoded policy
1851 /* x-amz-credential handling */
1852 if (!part_str(parts
, "x-amz-credential",
1853 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1854 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1855 err_msg
= "Missing aws4 credential";
1859 /* x-amz-signature handling */
1860 if (!part_str(parts
, "x-amz-signature",
1861 &s
->auth
.s3_postobj_creds
.signature
)) {
1862 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1863 err_msg
= "Missing aws4 signature";
1867 /* x-amz-date handling */
1868 std::string received_date_str
;
1869 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1870 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1871 err_msg
= "Missing aws4 date";
1877 // check that the signature matches the encoded policy
1878 if (!part_str(parts
, "AWSAccessKeyId",
1879 &s
->auth
.s3_postobj_creds
.access_key
)) {
1880 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1881 err_msg
= "Missing aws2 access key";
1885 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1886 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1887 err_msg
= "Missing aws2 signature";
1892 part_str(parts
, "x-amz-security-token", &s
->auth
.s3_postobj_creds
.x_amz_security_token
);
1894 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1895 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1897 const int ret
= rgw::auth::Strategy::apply(this, auth_registry_ptr
->get_s3_post(), s
);
1901 /* Populate the owner info. */
1902 s
->owner
.set_id(s
->user
->user_id
);
1903 s
->owner
.set_name(s
->user
->display_name
);
1904 ldout(s
->cct
, 20) << "Successful Signature Verification!" << dendl
;
1907 ceph::bufferlist decoded_policy
;
1909 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1910 } catch (buffer::error
& err
) {
1911 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1912 err_msg
= "Could not decode policy";
1916 decoded_policy
.append('\0'); // NULL terminate
1917 ldout(s
->cct
, 20) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1920 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1922 if (err_msg
.empty()) {
1923 err_msg
= "Failed to parse policy";
1925 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1931 post_policy
.set_var_checked("x-amz-signature");
1934 post_policy
.set_var_checked("AWSAccessKeyId");
1935 post_policy
.set_var_checked("signature");
1937 post_policy
.set_var_checked("policy");
1939 r
= post_policy
.check(&env
, err_msg
);
1941 if (err_msg
.empty()) {
1942 err_msg
= "Policy check failed";
1944 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1949 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1953 part_str(parts
, "acl", &canned_acl
);
1955 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1956 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1957 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1958 err_msg
= "Bad canned ACLs";
1967 int RGWPostObj_ObjStore_S3::complete_get_params()
1971 struct post_form_part part
;
1972 int r
= read_form_part_header(&part
, done
);
1977 ceph::bufferlist part_data
;
1979 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1980 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1981 if (r
< 0 || !boundary
) {
1985 /* Just reading the data but not storing any results of that. */
1991 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1996 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1997 int r
= read_data(bl
, chunk_size
, boundary
, done
);
2004 /* Reached end of data, let's drain the rest of the params */
2005 r
= complete_get_params();
2016 void RGWPostObj_ObjStore_S3::send_response()
2018 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
2021 part_str(parts
, "success_action_redirect", &redirect
);
2026 string etag_str
= "\"";
2028 etag_str
.append(etag
);
2029 etag_str
.append("\"");
2033 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
2034 url_encode(s
->bucket_name
, bucket
);
2035 url_encode(s
->object
.name
, key
);
2036 url_encode(etag_str
, etag_url
);
2038 if (!s
->bucket_tenant
.empty()) {
2040 * What we really would like is to quaily the bucket name, so
2041 * that the client could simply copy it and paste into next request.
2042 * Unfortunately, in S3 we cannot know if the client will decide
2043 * to come through DNS, with "bucket.tenant" sytanx, or through
2044 * URL with "tenant\bucket" syntax. Therefore, we provide the
2045 * tenant separately.
2047 redirect
.append("?tenant=");
2048 redirect
.append(tenant
);
2049 redirect
.append("&bucket=");
2050 redirect
.append(bucket
);
2052 redirect
.append("?bucket=");
2053 redirect
.append(bucket
);
2055 redirect
.append("&key=");
2056 redirect
.append(key
);
2057 redirect
.append("&etag=");
2058 redirect
.append(etag_url
);
2060 int r
= check_utf8(redirect
.c_str(), redirect
.size());
2065 dump_redirect(s
, redirect
);
2066 op_ret
= STATUS_REDIRECT
;
2067 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
2068 string status_string
;
2069 uint32_t status_int
;
2071 part_str(parts
, "success_action_status", &status_string
);
2073 int r
= stringtoul(status_string
, &status_int
);
2079 switch (status_int
) {
2083 op_ret
= STATUS_CREATED
;
2086 op_ret
= STATUS_NO_CONTENT
;
2089 } else if (! op_ret
) {
2090 op_ret
= STATUS_NO_CONTENT
;
2094 if (op_ret
== STATUS_CREATED
) {
2095 for (auto &it
: crypt_http_responses
)
2096 dump_header(s
, it
.first
, it
.second
);
2097 s
->formatter
->open_object_section("PostResponse");
2098 std::string base_uri
= compute_domain_uri(s
);
2099 if (!s
->bucket_tenant
.empty()){
2100 s
->formatter
->dump_format("Location", "%s/%s:%s/%s",
2102 url_encode(s
->bucket_tenant
).c_str(),
2103 url_encode(s
->bucket_name
).c_str(),
2104 url_encode(s
->object
.name
).c_str());
2105 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2107 s
->formatter
->dump_format("Location", "%s/%s/%s",
2109 url_encode(s
->bucket_name
).c_str(),
2110 url_encode(s
->object
.name
).c_str());
2112 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2113 s
->formatter
->dump_string("Key", s
->object
.name
);
2114 s
->formatter
->dump_string("ETag", etag
);
2115 s
->formatter
->close_section();
2117 s
->err
.message
= err_msg
;
2118 set_req_state_err(s
, op_ret
);
2121 dump_content_length(s
, s
->formatter
->get_len());
2123 end_header(s
, this);
2124 if (op_ret
!= STATUS_CREATED
)
2127 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2130 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
2131 std::unique_ptr
<rgw::putobj::DataProcessor
> *filter
,
2132 rgw::putobj::DataProcessor
*cb
)
2134 std::unique_ptr
<BlockCrypt
> block_crypt
;
2135 int res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
,
2136 crypt_http_responses
);
2137 if (res
== 0 && block_crypt
!= nullptr) {
2138 filter
->reset(new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
2143 int RGWDeleteObj_ObjStore_S3::get_params()
2145 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2147 if (s
->system_request
) {
2148 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
2152 std::string if_unmod_decoded
= url_decode(if_unmod
);
2155 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
2156 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
2159 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
2165 void RGWDeleteObj_ObjStore_S3::send_response()
2171 r
= STATUS_NO_CONTENT
;
2173 set_req_state_err(s
, r
);
2175 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
2176 if (delete_marker
) {
2177 dump_header(s
, "x-amz-delete-marker", "true");
2179 end_header(s
, this);
2182 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2184 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2186 /* build a policy for the target object */
2187 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2191 dest_policy
= s3policy
;
2196 int RGWCopyObj_ObjStore_S3::get_params()
2198 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2199 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2200 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
2201 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2203 src_tenant_name
= s
->src_tenant_name
;
2204 src_bucket_name
= s
->src_bucket_name
;
2205 src_object
= s
->src_object
;
2206 dest_tenant_name
= s
->bucket
.tenant
;
2207 dest_bucket_name
= s
->bucket
.name
;
2208 dest_object
= s
->object
.name
;
2210 if (s
->system_request
) {
2211 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
2212 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
2215 copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
2216 auto tmp_md_d
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2218 if (strcasecmp(tmp_md_d
, "COPY") == 0) {
2219 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
2220 } else if (strcasecmp(tmp_md_d
, "REPLACE") == 0) {
2221 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
2222 } else if (!source_zone
.empty()) {
2223 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
2225 s
->err
.message
= "Unknown metadata directive.";
2226 ldout(s
->cct
, 0) << s
->err
.message
<< dendl
;
2229 md_directive
= tmp_md_d
;
2232 if (source_zone
.empty() &&
2233 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
2234 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
2235 (dest_object
.compare(src_object
.name
) == 0) &&
2236 src_object
.instance
.empty() &&
2237 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
2238 need_to_check_storage_class
= true;
2244 int RGWCopyObj_ObjStore_S3::check_storage_class(const rgw_placement_rule
& src_placement
)
2246 if (src_placement
== s
->dest_placement
) {
2247 /* can only copy object into itself if replacing attrs */
2248 s
->err
.message
= "This copy request is illegal because it is trying to copy "
2249 "an object to itself without changing the object's metadata, "
2250 "storage class, website redirect location or encryption attributes.";
2251 ldout(s
->cct
, 0) << s
->err
.message
<< dendl
;
2252 return -ERR_INVALID_REQUEST
;
2257 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
2259 if (! sent_header
) {
2261 set_req_state_err(s
, op_ret
);
2264 // Explicitly use chunked transfer encoding so that we can stream the result
2265 // to the user without having to wait for the full length of it.
2266 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
2269 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
2273 /* Send progress field. Note that this diverge from the original S3
2274 * spec. We do this in order to keep connection alive.
2276 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
2278 rgw_flush_formatter(s
, s
->formatter
);
2281 void RGWCopyObj_ObjStore_S3::send_response()
2284 send_partial_response(0);
2287 dump_time(s
, "LastModified", &mtime
);
2288 if (!etag
.empty()) {
2289 s
->formatter
->dump_string("ETag", std::move(etag
));
2291 s
->formatter
->close_section();
2292 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2296 void RGWGetACLs_ObjStore_S3::send_response()
2299 set_req_state_err(s
, op_ret
);
2301 end_header(s
, this, "application/xml");
2303 rgw_flush_formatter(s
, s
->formatter
);
2307 int RGWPutACLs_ObjStore_S3::get_params()
2309 int ret
= RGWPutACLs_ObjStore::get_params();
2311 const int ret_auth
= do_aws4_auth_completion();
2319 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
2320 struct req_state
*s
,
2323 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2325 // bucket-* canned acls do not apply to bucket
2326 if (s
->object
.empty()) {
2327 if (s
->canned_acl
.find("bucket") != string::npos
)
2328 s
->canned_acl
.clear();
2331 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2335 s3policy
.to_xml(ss
);
2340 void RGWPutACLs_ObjStore_S3::send_response()
2343 set_req_state_err(s
, op_ret
);
2345 end_header(s
, this, "application/xml");
2349 void RGWGetLC_ObjStore_S3::execute()
2351 config
.set_ctx(s
->cct
);
2353 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2354 if (aiter
== s
->bucket_attrs
.end()) {
2359 bufferlist::const_iterator iter
{&aiter
->second
};
2361 config
.decode(iter
);
2362 } catch (const buffer::error
& e
) {
2363 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2369 void RGWGetLC_ObjStore_S3::send_response()
2372 if (op_ret
== -ENOENT
) {
2373 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2375 set_req_state_err(s
, op_ret
);
2379 end_header(s
, this, "application/xml");
2385 encode_xml("LifecycleConfiguration", XMLNS_AWS_S3
, config
, s
->formatter
);
2386 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2389 void RGWPutLC_ObjStore_S3::send_response()
2392 set_req_state_err(s
, op_ret
);
2394 end_header(s
, this, "application/xml");
2398 void RGWDeleteLC_ObjStore_S3::send_response()
2401 op_ret
= STATUS_NO_CONTENT
;
2403 set_req_state_err(s
, op_ret
);
2406 end_header(s
, this, "application/xml");
2410 void RGWGetCORS_ObjStore_S3::send_response()
2413 if (op_ret
== -ENOENT
)
2414 set_req_state_err(s
, ERR_NO_SUCH_CORS_CONFIGURATION
);
2416 set_req_state_err(s
, op_ret
);
2419 end_header(s
, NULL
, "application/xml");
2423 RGWCORSConfiguration_S3
*s3cors
=
2424 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2433 int RGWPutCORS_ObjStore_S3::get_params()
2435 RGWCORSXMLParser_S3
parser(s
->cct
);
2436 RGWCORSConfiguration_S3
*cors_config
;
2438 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2442 std::tie(r
, data
) = rgw_rest_read_all_input(s
, max_size
, false);
2447 r
= do_aws4_auth_completion();
2452 if (!parser
.init()) {
2456 char* buf
= data
.c_str();
2457 if (!buf
|| !parser
.parse(buf
, data
.length(), 1)) {
2458 return -ERR_MALFORMED_XML
;
2461 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2462 "CORSConfiguration"));
2464 return -ERR_MALFORMED_XML
;
2467 #define CORS_RULES_MAX_NUM 100
2468 int max_num
= s
->cct
->_conf
->rgw_cors_rules_max_num
;
2470 max_num
= CORS_RULES_MAX_NUM
;
2472 int cors_rules_num
= cors_config
->get_rules().size();
2473 if (cors_rules_num
> max_num
) {
2474 ldout(s
->cct
, 4) << "An cors config can have up to "
2476 << " rules, request cors rules num: "
2477 << cors_rules_num
<< dendl
;
2478 op_ret
= -ERR_INVALID_CORS_RULES_ERROR
;
2479 s
->err
.message
= "The number of CORS rules should not exceed allowed limit of "
2480 + std::to_string(max_num
) + " rules.";
2481 return -ERR_INVALID_REQUEST
;
2484 // forward bucket cors requests to meta master zone
2485 if (!store
->svc
.zone
->is_meta_master()) {
2486 /* only need to keep this data around if we're not meta master */
2487 in_data
.append(data
);
2490 if (s
->cct
->_conf
->subsys
.should_gather
<ceph_subsys_rgw
, 15>()) {
2491 ldout(s
->cct
, 15) << "CORSConfiguration";
2492 cors_config
->to_xml(*_dout
);
2496 cors_config
->encode(cors_bl
);
2501 void RGWPutCORS_ObjStore_S3::send_response()
2504 set_req_state_err(s
, op_ret
);
2506 end_header(s
, NULL
, "application/xml");
2510 void RGWDeleteCORS_ObjStore_S3::send_response()
2513 if (!r
|| r
== -ENOENT
)
2514 r
= STATUS_NO_CONTENT
;
2516 set_req_state_err(s
, r
);
2518 end_header(s
, NULL
);
2521 void RGWOptionsCORS_ObjStore_S3::send_response()
2523 string hdrs
, exp_hdrs
;
2524 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2525 /*EACCES means, there is no CORS registered yet for the bucket
2526 *ENOENT means, there is no match of the Origin in the list of CORSRule
2528 if (op_ret
== -ENOENT
)
2531 set_req_state_err(s
, op_ret
);
2533 end_header(s
, NULL
);
2536 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2539 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2541 end_header(s
, NULL
);
2544 void RGWGetRequestPayment_ObjStore_S3::send_response()
2547 end_header(s
, this, "application/xml");
2550 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2551 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2552 s
->formatter
->dump_string("Payer", payer
);
2553 s
->formatter
->close_section();
2554 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2557 class RGWSetRequestPaymentParser
: public RGWXMLParser
2559 XMLObj
*alloc_obj(const char *el
) override
{
2564 RGWSetRequestPaymentParser() {}
2565 ~RGWSetRequestPaymentParser() override
{}
2567 int get_request_payment_payer(bool *requester_pays
) {
2568 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2572 *requester_pays
= false;
2574 XMLObj
*field
= config
->find_first("Payer");
2578 auto& s
= field
->get_data();
2580 if (stringcasecmp(s
, "Requester") == 0) {
2581 *requester_pays
= true;
2582 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2590 int RGWSetRequestPayment_ObjStore_S3::get_params()
2592 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2595 std::tie(r
, in_data
) = rgw_rest_read_all_input(s
, max_size
, false);
2602 RGWSetRequestPaymentParser parser
;
2604 if (!parser
.init()) {
2605 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2609 char* buf
= in_data
.c_str();
2610 if (!parser
.parse(buf
, in_data
.length(), 1)) {
2611 ldout(s
->cct
, 10) << "failed to parse data: " << buf
<< dendl
;
2615 return parser
.get_request_payment_payer(&requester_pays
);
2618 void RGWSetRequestPayment_ObjStore_S3::send_response()
2621 set_req_state_err(s
, op_ret
);
2626 int RGWInitMultipart_ObjStore_S3::get_params()
2628 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2629 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2638 void RGWInitMultipart_ObjStore_S3::send_response()
2641 set_req_state_err(s
, op_ret
);
2643 for (auto &it
: crypt_http_responses
)
2644 dump_header(s
, it
.first
, it
.second
);
2645 end_header(s
, this, "application/xml");
2648 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2649 if (!s
->bucket_tenant
.empty())
2650 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2651 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2652 s
->formatter
->dump_string("Key", s
->object
.name
);
2653 s
->formatter
->dump_string("UploadId", upload_id
);
2654 s
->formatter
->close_section();
2655 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2659 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2662 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2666 int RGWCompleteMultipart_ObjStore_S3::get_params()
2668 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2675 return do_aws4_auth_completion();
2678 void RGWCompleteMultipart_ObjStore_S3::send_response()
2681 set_req_state_err(s
, op_ret
);
2683 dump_header_if_nonempty(s
, "x-amz-version-id", version_id
);
2684 end_header(s
, this, "application/xml");
2687 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2688 std::string base_uri
= compute_domain_uri(s
);
2689 if (!s
->bucket_tenant
.empty()) {
2690 s
->formatter
->dump_format("Location", "%s/%s:%s/%s",
2692 s
->bucket_tenant
.c_str(),
2693 s
->bucket_name
.c_str(),
2694 s
->object
.name
.c_str()
2696 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2698 s
->formatter
->dump_format("Location", "%s/%s/%s",
2700 s
->bucket_name
.c_str(),
2701 s
->object
.name
.c_str()
2704 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2705 s
->formatter
->dump_string("Key", s
->object
.name
);
2706 s
->formatter
->dump_string("ETag", etag
);
2707 s
->formatter
->close_section();
2708 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2712 void RGWAbortMultipart_ObjStore_S3::send_response()
2716 r
= STATUS_NO_CONTENT
;
2718 set_req_state_err(s
, r
);
2720 end_header(s
, this);
2723 void RGWListMultipart_ObjStore_S3::send_response()
2726 set_req_state_err(s
, op_ret
);
2728 // Explicitly use chunked transfer encoding so that we can stream the result
2729 // to the user without having to wait for the full length of it.
2730 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
2734 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2735 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2736 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2739 iter
= parts
.begin();
2740 test_iter
= parts
.rbegin();
2741 if (test_iter
!= parts
.rend()) {
2742 cur_max
= test_iter
->first
;
2744 if (!s
->bucket_tenant
.empty())
2745 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2746 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2747 s
->formatter
->dump_string("Key", s
->object
.name
);
2748 s
->formatter
->dump_string("UploadId", upload_id
);
2749 s
->formatter
->dump_string("StorageClass", "STANDARD");
2750 s
->formatter
->dump_int("PartNumberMarker", marker
);
2751 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2752 s
->formatter
->dump_int("MaxParts", max_parts
);
2753 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2755 ACLOwner
& owner
= policy
.get_owner();
2756 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2758 for (; iter
!= parts
.end(); ++iter
) {
2759 RGWUploadPartInfo
& info
= iter
->second
;
2761 s
->formatter
->open_object_section("Part");
2763 dump_time(s
, "LastModified", &info
.modified
);
2765 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2766 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2767 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2768 s
->formatter
->close_section();
2770 s
->formatter
->close_section();
2771 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2775 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2778 set_req_state_err(s
, op_ret
);
2781 // Explicitly use chunked transfer encoding so that we can stream the result
2782 // to the user without having to wait for the full length of it.
2783 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
2788 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2789 if (!s
->bucket_tenant
.empty())
2790 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2791 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2792 if (!prefix
.empty())
2793 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2794 const string
& key_marker
= marker
.get_key();
2795 if (!key_marker
.empty())
2796 s
->formatter
->dump_string("KeyMarker", key_marker
);
2797 const string
& upload_id_marker
= marker
.get_upload_id();
2798 if (!upload_id_marker
.empty())
2799 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2800 string next_key
= next_marker
.mp
.get_key();
2801 if (!next_key
.empty())
2802 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2803 string next_upload_id
= next_marker
.mp
.get_upload_id();
2804 if (!next_upload_id
.empty())
2805 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2806 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2807 if (!delimiter
.empty())
2808 s
->formatter
->dump_string("Delimiter", delimiter
);
2809 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2812 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2813 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2814 RGWMPObj
& mp
= iter
->mp
;
2815 s
->formatter
->open_array_section("Upload");
2816 s
->formatter
->dump_string("Key", mp
.get_key());
2817 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2818 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2819 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2820 s
->formatter
->dump_string("StorageClass", "STANDARD");
2821 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2822 s
->formatter
->close_section();
2824 if (!common_prefixes
.empty()) {
2825 s
->formatter
->open_array_section("CommonPrefixes");
2826 map
<string
, bool>::iterator pref_iter
;
2827 for (pref_iter
= common_prefixes
.begin();
2828 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2829 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2831 s
->formatter
->close_section();
2834 s
->formatter
->close_section();
2835 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2838 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2840 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2845 return do_aws4_auth_completion();
2848 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2850 if (! status_dumped
) {
2852 set_req_state_err(s
, op_ret
);
2854 status_dumped
= true;
2858 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2861 if (!status_dumped
) {
2866 // Explicitly use chunked transfer encoding so that we can stream the result
2867 // to the user without having to wait for the full length of it.
2868 end_header(s
, this, "application/xml", CHUNKED_TRANSFER_ENCODING
);
2869 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2871 rgw_flush_formatter(s
, s
->formatter
);
2874 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2876 const string
& marker_version_id
, int ret
)
2879 if (op_ret
== 0 && !quiet
) {
2880 s
->formatter
->open_object_section("Deleted");
2881 s
->formatter
->dump_string("Key", key
.name
);
2882 if (!key
.instance
.empty()) {
2883 s
->formatter
->dump_string("VersionId", key
.instance
);
2885 if (delete_marker
) {
2886 s
->formatter
->dump_bool("DeleteMarker", true);
2887 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2889 s
->formatter
->close_section();
2890 } else if (op_ret
< 0) {
2891 struct rgw_http_error r
;
2894 s
->formatter
->open_object_section("Error");
2897 rgw_get_errno_s3(&r
, err_no
);
2899 s
->formatter
->dump_string("Key", key
.name
);
2900 s
->formatter
->dump_string("VersionId", key
.instance
);
2901 s
->formatter
->dump_string("Code", r
.s3_code
);
2902 s
->formatter
->dump_string("Message", r
.s3_code
);
2903 s
->formatter
->close_section();
2906 rgw_flush_formatter(s
, s
->formatter
);
2910 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2913 s
->formatter
->close_section();
2914 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2917 void RGWGetObjLayout_ObjStore_S3::send_response()
2920 set_req_state_err(s
, op_ret
);
2922 end_header(s
, this, "application/json");
2930 f
.open_object_section("result");
2931 ::encode_json("head", head_obj
, &f
);
2932 ::encode_json("manifest", *manifest
, &f
);
2933 f
.open_array_section("data_location");
2934 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2935 f
.open_object_section("obj");
2936 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2937 uint64_t ofs
= miter
.get_ofs();
2938 uint64_t left
= manifest
->get_obj_size() - ofs
;
2939 ::encode_json("ofs", miter
.get_ofs(), &f
);
2940 ::encode_json("loc", raw_loc
, &f
);
2941 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2942 uint64_t loc_size
= miter
.get_stripe_size();
2943 if (loc_size
> left
) {
2946 ::encode_json("loc_size", loc_size
, &f
);
2948 rgw_flush_formatter(s
, &f
);
2952 rgw_flush_formatter(s
, &f
);
2955 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2957 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2958 if (iter
== s
->info
.x_meta_map
.end()) {
2959 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2960 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2964 list
<string
> expressions
;
2965 get_str_list(iter
->second
, ",", expressions
);
2967 for (auto& expression
: expressions
) {
2968 vector
<string
> args
;
2969 get_str_vec(expression
, ";", args
);
2972 s
->err
.message
= "invalid empty expression";
2973 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2976 if (args
.size() > 2) {
2977 s
->err
.message
= string("invalid expression: ") + expression
;
2978 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2982 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2984 if (args
.size() > 1) {
2985 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2988 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2989 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2990 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2994 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2996 ESEntityTypeMap::EntityType entity_type
;
2998 if (val
.empty() || val
== "str" || val
== "string") {
2999 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
3000 } else if (val
== "int" || val
== "integer") {
3001 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
3002 } else if (val
== "date" || val
== "datetime") {
3003 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
3005 s
->err
.message
= string("invalid entity type: ") + val
;
3006 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
3010 mdsearch_config
[key
] = entity_type
;
3016 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
3019 set_req_state_err(s
, op_ret
);
3021 end_header(s
, this);
3024 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
3027 set_req_state_err(s
, op_ret
);
3029 end_header(s
, NULL
, "application/xml");
3031 Formatter
*f
= s
->formatter
;
3032 f
->open_array_section("GetBucketMetaSearchResult");
3033 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
3034 f
->open_object_section("Entry");
3035 string k
= string("x-amz-meta-") + e
.first
;
3036 f
->dump_string("Key", k
.c_str());
3039 case ESEntityTypeMap::ES_ENTITY_INT
:
3042 case ESEntityTypeMap::ES_ENTITY_DATE
:
3048 f
->dump_string("Type", type
);
3052 rgw_flush_formatter(s
, f
);
3055 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
3058 set_req_state_err(s
, op_ret
);
3060 end_header(s
, this);
3064 RGWOp
*RGWHandler_REST_Service_S3::op_get()
3066 if (is_usage_op()) {
3067 return new RGWGetUsage_ObjStore_S3
;
3069 return new RGWListBuckets_ObjStore_S3
;
3073 RGWOp
*RGWHandler_REST_Service_S3::op_head()
3075 return new RGWListBuckets_ObjStore_S3
;
3078 RGWOp
*RGWHandler_REST_Service_S3::op_post()
3080 if (s
->info
.args
.exists("Action")) {
3081 string action
= s
->info
.args
.get("Action");
3082 if (action
.compare("CreateRole") == 0)
3083 return new RGWCreateRole
;
3084 if (action
.compare("DeleteRole") == 0)
3085 return new RGWDeleteRole
;
3086 if (action
.compare("GetRole") == 0)
3087 return new RGWGetRole
;
3088 if (action
.compare("UpdateAssumeRolePolicy") == 0)
3089 return new RGWModifyRole
;
3090 if (action
.compare("ListRoles") == 0)
3091 return new RGWListRoles
;
3092 if (action
.compare("PutRolePolicy") == 0)
3093 return new RGWPutRolePolicy
;
3094 if (action
.compare("GetRolePolicy") == 0)
3095 return new RGWGetRolePolicy
;
3096 if (action
.compare("ListRolePolicies") == 0)
3097 return new RGWListRolePolicies
;
3098 if (action
.compare("DeleteRolePolicy") == 0)
3099 return new RGWDeleteRolePolicy
;
3100 if (action
.compare("PutUserPolicy") == 0)
3101 return new RGWPutUserPolicy
;
3102 if (action
.compare("GetUserPolicy") == 0)
3103 return new RGWGetUserPolicy
;
3104 if (action
.compare("ListUserPolicies") == 0)
3105 return new RGWListUserPolicies
;
3106 if (action
.compare("DeleteUserPolicy") == 0)
3107 return new RGWDeleteUserPolicy
;
3109 if (this->isSTSenabled
) {
3110 RGWHandler_REST_STS
sts_handler(auth_registry
);
3111 sts_handler
.init(store
, s
, s
->cio
);
3112 return sts_handler
.get_op(store
);
3117 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
3121 return new RGWListBucket_ObjStore_S3
;
3123 return new RGWStatBucket_ObjStore_S3
;
3127 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
3129 if (s
->info
.args
.sub_resource_exists("logging"))
3130 return new RGWGetBucketLogging_ObjStore_S3
;
3132 if (s
->info
.args
.sub_resource_exists("location"))
3133 return new RGWGetBucketLocation_ObjStore_S3
;
3135 if (s
->info
.args
.sub_resource_exists("versioning"))
3136 return new RGWGetBucketVersioning_ObjStore_S3
;
3138 if (s
->info
.args
.sub_resource_exists("website")) {
3139 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3142 return new RGWGetBucketWebsite_ObjStore_S3
;
3145 if (s
->info
.args
.exists("mdsearch")) {
3146 return new RGWGetBucketMetaSearch_ObjStore_S3
;
3150 return new RGWGetACLs_ObjStore_S3
;
3151 } else if (is_cors_op()) {
3152 return new RGWGetCORS_ObjStore_S3
;
3153 } else if (is_request_payment_op()) {
3154 return new RGWGetRequestPayment_ObjStore_S3
;
3155 } else if (s
->info
.args
.exists("uploads")) {
3156 return new RGWListBucketMultiparts_ObjStore_S3
;
3157 } else if(is_lc_op()) {
3158 return new RGWGetLC_ObjStore_S3
;
3159 } else if(is_policy_op()) {
3160 return new RGWGetBucketPolicy
;
3162 return get_obj_op(true);
3165 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
3168 return new RGWGetACLs_ObjStore_S3
;
3169 } else if (s
->info
.args
.exists("uploads")) {
3170 return new RGWListBucketMultiparts_ObjStore_S3
;
3172 return get_obj_op(false);
3175 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
3177 if (s
->info
.args
.sub_resource_exists("logging"))
3179 if (s
->info
.args
.sub_resource_exists("versioning"))
3180 return new RGWSetBucketVersioning_ObjStore_S3
;
3181 if (s
->info
.args
.sub_resource_exists("website")) {
3182 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3185 return new RGWSetBucketWebsite_ObjStore_S3
;
3188 return new RGWPutACLs_ObjStore_S3
;
3189 } else if (is_cors_op()) {
3190 return new RGWPutCORS_ObjStore_S3
;
3191 } else if (is_request_payment_op()) {
3192 return new RGWSetRequestPayment_ObjStore_S3
;
3193 } else if(is_lc_op()) {
3194 return new RGWPutLC_ObjStore_S3
;
3195 } else if(is_policy_op()) {
3196 return new RGWPutBucketPolicy
;
3198 return new RGWCreateBucket_ObjStore_S3
;
3201 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
3204 return new RGWDeleteCORS_ObjStore_S3
;
3205 } else if(is_lc_op()) {
3206 return new RGWDeleteLC_ObjStore_S3
;
3207 } else if(is_policy_op()) {
3208 return new RGWDeleteBucketPolicy
;
3211 if (s
->info
.args
.sub_resource_exists("website")) {
3212 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3215 return new RGWDeleteBucketWebsite_ObjStore_S3
;
3218 if (s
->info
.args
.exists("mdsearch")) {
3219 return new RGWDelBucketMetaSearch_ObjStore_S3
;
3222 return new RGWDeleteBucket_ObjStore_S3
;
3225 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
3227 if (s
->info
.args
.exists("delete")) {
3228 return new RGWDeleteMultiObj_ObjStore_S3
;
3231 if (s
->info
.args
.exists("mdsearch")) {
3232 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
3235 return new RGWPostObj_ObjStore_S3
;
3238 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
3240 return new RGWOptionsCORS_ObjStore_S3
;
3243 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
3245 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
3246 get_obj_op
->set_get_data(get_data
);
3250 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
3253 return new RGWGetACLs_ObjStore_S3
;
3254 } else if (s
->info
.args
.exists("uploadId")) {
3255 return new RGWListMultipart_ObjStore_S3
;
3256 } else if (s
->info
.args
.exists("layout")) {
3257 return new RGWGetObjLayout_ObjStore_S3
;
3258 } else if (is_tagging_op()) {
3259 return new RGWGetObjTags_ObjStore_S3
;
3261 return get_obj_op(true);
3264 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
3267 return new RGWGetACLs_ObjStore_S3
;
3268 } else if (s
->info
.args
.exists("uploadId")) {
3269 return new RGWListMultipart_ObjStore_S3
;
3271 return get_obj_op(false);
3274 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
3277 return new RGWPutACLs_ObjStore_S3
;
3278 } else if (is_tagging_op()) {
3279 return new RGWPutObjTags_ObjStore_S3
;
3282 if (s
->init_state
.src_bucket
.empty())
3283 return new RGWPutObj_ObjStore_S3
;
3285 return new RGWCopyObj_ObjStore_S3
;
3288 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
3290 if (is_tagging_op()) {
3291 return new RGWDeleteObjTags_ObjStore_S3
;
3293 string upload_id
= s
->info
.args
.get("uploadId");
3295 if (upload_id
.empty())
3296 return new RGWDeleteObj_ObjStore_S3
;
3298 return new RGWAbortMultipart_ObjStore_S3
;
3301 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
3303 if (s
->info
.args
.exists("uploadId"))
3304 return new RGWCompleteMultipart_ObjStore_S3
;
3306 if (s
->info
.args
.exists("uploads"))
3307 return new RGWInitMultipart_ObjStore_S3
;
3309 return new RGWPostObj_ObjStore_S3
;
3312 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
3314 return new RGWOptionsCORS_ObjStore_S3
;
3317 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
3318 int default_formatter
,
3319 bool configurable_format
)
3324 const char *req_name
= s
->relative_uri
.c_str();
3327 if (*req_name
== '?') {
3330 p
= s
->info
.request_params
.c_str();
3333 s
->info
.args
.set(p
);
3334 s
->info
.args
.parse();
3336 /* must be called after the args parsing */
3337 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
3341 if (*req_name
!= '/')
3350 int pos
= req
.find('/');
3352 first
= req
.substr(0, pos
);
3358 * XXX The intent of the check for empty is apparently to let the bucket
3359 * name from DNS to be set ahead. However, we currently take the DNS
3360 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3361 * So, this check is meaningless.
3363 * Rather than dropping this, the code needs to be changed into putting
3364 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3365 * into req_status.bucket_name directly.
3367 if (s
->init_state
.url_bucket
.empty()) {
3368 // Save bucket to tide us over until token is parsed.
3369 s
->init_state
.url_bucket
= first
;
3371 string encoded_obj_str
= req
.substr(pos
+1);
3372 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
3375 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3380 static int verify_mfa(RGWRados
*store
, RGWUserInfo
*user
, const string
& mfa_str
, bool *verified
)
3382 vector
<string
> params
;
3383 get_str_vec(mfa_str
, " ", params
);
3385 if (params
.size() != 2) {
3386 ldout(store
->ctx(), 5) << "NOTICE: invalid mfa string provided: " << mfa_str
<< dendl
;
3390 string
& serial
= params
[0];
3391 string
& pin
= params
[1];
3393 auto i
= user
->mfa_ids
.find(serial
);
3394 if (i
== user
->mfa_ids
.end()) {
3395 ldout(store
->ctx(), 5) << "NOTICE: user does not have mfa device with serial=" << serial
<< dendl
;
3399 int ret
= store
->check_mfa(user
->user_id
, serial
, pin
);
3401 ldout(store
->ctx(), 20) << "NOTICE: failed to check MFA, serial=" << serial
<< dendl
;
3410 int RGWHandler_REST_S3::postauth_init()
3412 struct req_init_state
*t
= &s
->init_state
;
3413 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3415 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3416 s
->bucket_tenant
, s
->bucket_name
);
3418 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3419 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3422 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3425 if (!s
->bucket_name
.empty()) {
3426 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3429 ret
= validate_object_name(s
->object
.name
);
3434 if (!t
->src_bucket
.empty()) {
3435 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3436 s
->src_tenant_name
, s
->src_bucket_name
);
3437 ret
= rgw_validate_tenant_name(s
->src_tenant_name
);
3440 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3445 const char *mfa
= s
->info
.env
->get("HTTP_X_AMZ_MFA");
3447 ret
= verify_mfa(store
, s
->user
, string(mfa
), &s
->mfa_verified
);
3453 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3454 rgw::io::BasicClient
*cio
)
3460 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3463 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3464 if (!s
->bucket_name
.empty()) {
3465 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3468 ret
= validate_object_name(s
->object
.name
);
3473 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3475 s
->canned_acl
= cacl
;
3477 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3479 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3481 (! s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) &&
3482 (! s
->info
.args
.exists("uploadId"))) {
3484 ret
= RGWCopyObj::parse_copy_location(copy_source
,
3485 s
->init_state
.src_bucket
,
3488 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3489 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3493 const char *sc
= s
->info
.env
->get("HTTP_X_AMZ_STORAGE_CLASS");
3495 s
->info
.storage_class
= sc
;
3498 return RGWHandler_REST::init(store
, s
, cio
);
3501 int RGWHandler_REST_S3::authorize(const DoutPrefixProvider
*dpp
)
3503 if (s
->info
.args
.exists("Action") && s
->info
.args
.get("Action") == "AssumeRoleWithWebIdentity") {
3504 return RGW_Auth_STS::authorize(dpp
, store
, auth_registry
, s
);
3506 return RGW_Auth_S3::authorize(dpp
, store
, auth_registry
, s
);
3509 enum class AwsVersion
{
3515 enum class AwsRoute
{
3521 static inline std::pair
<AwsVersion
, AwsRoute
>
3522 discover_aws_flavour(const req_info
& info
)
3524 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3526 AwsVersion version
= AwsVersion::UNKNOWN
;
3527 AwsRoute route
= AwsRoute::UNKNOWN
;
3529 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3530 if (http_auth
&& http_auth
[0]) {
3531 /* Authorization in Header */
3532 route
= AwsRoute::HEADERS
;
3534 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3535 strlen(AWS4_HMAC_SHA256_STR
))) {
3537 version
= AwsVersion::V4
;
3538 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3540 version
= AwsVersion::V2
;
3543 route
= AwsRoute::QUERY_STRING
;
3545 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3547 version
= AwsVersion::V4
;
3548 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3550 version
= AwsVersion::V2
;
3554 return std::make_pair(version
, route
);
3558 * verify that a signed request comes from the keyholder
3559 * by checking the signature against our locally-computed version
3561 * it tries AWS v4 before AWS v2
3563 int RGW_Auth_S3::authorize(const DoutPrefixProvider
*dpp
,
3564 RGWRados
* const store
,
3565 const rgw::auth::StrategyRegistry
& auth_registry
,
3566 struct req_state
* const s
)
3569 /* neither keystone and rados enabled; warn and exit! */
3570 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3571 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3572 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3573 ldpp_dout(dpp
, 0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3577 const auto ret
= rgw::auth::Strategy::apply(dpp
, auth_registry
.get_s3_main(), s
);
3579 /* Populate the owner info. */
3580 s
->owner
.set_id(s
->user
->user_id
);
3581 s
->owner
.set_name(s
->user
->display_name
);
3586 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3587 rgw::io::BasicClient
*cio
)
3589 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3594 return RGWHandler_REST::init(store
, state
, cio
);
3597 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3598 const rgw::auth::StrategyRegistry
& auth_registry
,
3599 const std::string
& frontend_prefix
)
3601 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3603 RGWHandler_REST_S3::init_from_header(s
,
3604 is_s3website
? RGW_FORMAT_HTML
:
3605 RGW_FORMAT_XML
, true);
3609 RGWHandler_REST
* handler
;
3610 // TODO: Make this more readable
3612 if (s
->init_state
.url_bucket
.empty()) {
3613 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3614 } else if (s
->object
.empty()) {
3615 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3617 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3620 if (s
->init_state
.url_bucket
.empty()) {
3621 handler
= new RGWHandler_REST_Service_S3(auth_registry
, enable_sts
);
3622 } else if (s
->object
.empty()) {
3623 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3625 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3629 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3634 bool RGWHandler_REST_S3Website::web_dir() const {
3635 std::string subdir_name
= url_decode(s
->object
.name
);
3637 if (subdir_name
.empty()) {
3639 } else if (subdir_name
.back() == '/') {
3640 subdir_name
.pop_back();
3643 rgw_obj
obj(s
->bucket
, subdir_name
);
3645 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3646 obj_ctx
.set_atomic(obj
);
3647 obj_ctx
.set_prefetch_data(obj
);
3649 RGWObjState
* state
= nullptr;
3650 if (store
->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false) < 0) {
3653 if (! state
->exists
) {
3656 return state
->exists
;
3659 int RGWHandler_REST_S3Website::init(RGWRados
*store
, req_state
*s
,
3660 rgw::io::BasicClient
* cio
)
3662 // save the original object name before retarget() replaces it with the
3663 // result of get_effective_key(). the error_handler() needs the original
3664 // object name for redirect handling
3665 original_object_name
= s
->object
.name
;
3667 return RGWHandler_REST_S3::init(store
, s
, cio
);
3670 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3672 ldout(s
->cct
, 10) << __func__
<< " Starting retarget" << dendl
;
3674 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3677 int ret
= store
->get_bucket_info(*s
->sysobj_ctx
, s
->bucket_tenant
,
3678 s
->bucket_name
, s
->bucket_info
, NULL
,
3681 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3682 return -ERR_NO_SUCH_BUCKET
;
3684 if (!s
->bucket_info
.has_website
) {
3685 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3686 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3689 rgw_obj_key new_obj
;
3690 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
3691 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3692 << new_obj
<< dendl
;
3694 RGWBWRoutingRule rrule
;
3695 bool should_redirect
=
3696 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3698 if (should_redirect
) {
3699 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3700 const string
& protocol
=
3701 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3702 int redirect_code
= 0;
3703 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3705 // APply a custom HTTP response code
3706 if (redirect_code
> 0)
3707 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3708 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3709 << " proto+host:" << protocol
<< "://" << hostname
3710 << " -> " << s
->redirect
<< dendl
;
3711 return -ERR_WEBSITE_REDIRECT
;
3715 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3716 * operation. Or remove this comment if it's not applicable anymore
3719 s
->object
= new_obj
;
3724 RGWOp
* RGWHandler_REST_S3Website::op_get()
3726 return get_obj_op(true);
3729 RGWOp
* RGWHandler_REST_S3Website::op_head()
3731 return get_obj_op(false);
3734 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3736 s
->formatter
->reset(); /* Try to throw it all away */
3738 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3739 if (getop
.get() == NULL
) {
3740 return -1; // Trigger double error handler
3742 getop
->init(store
, s
, this);
3743 getop
->range_str
= NULL
;
3744 getop
->if_mod
= NULL
;
3745 getop
->if_unmod
= NULL
;
3746 getop
->if_match
= NULL
;
3747 getop
->if_nomatch
= NULL
;
3748 s
->object
= errordoc_key
;
3750 ret
= init_permissions(getop
.get());
3752 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3753 return -1; // Trigger double error handler
3756 ret
= read_permissions(getop
.get());
3758 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3759 return -1; // Trigger double error handler
3763 getop
->set_custom_http_response(http_ret
);
3766 ret
= getop
->init_processing();
3768 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3769 return -1; // Trigger double error handler
3772 ret
= getop
->verify_op_mask();
3774 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3775 return -1; // Trigger double error handler
3778 ret
= getop
->verify_permission();
3780 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3781 return -1; // Trigger double error handler
3784 ret
= getop
->verify_params();
3786 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3787 return -1; // Trigger double error handler
3790 // No going back now
3793 * FIXME Missing headers:
3794 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3795 * x-amz-error-code: NoSuchKey
3796 * x-amz-error-message: The specified key does not exist.
3797 * x-amz-error-detail-Key: foo
3805 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3806 string
* error_content
) {
3807 int new_err_no
= -1;
3808 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3809 int http_error_code
= -1;
3811 if (r
!= rgw_http_s3_errors
.end()) {
3812 http_error_code
= r
->second
.first
;
3814 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3816 RGWBWRoutingRule rrule
;
3817 bool should_redirect
=
3818 s
->bucket_info
.website_conf
.should_redirect(original_object_name
,
3819 http_error_code
, &rrule
);
3821 if (should_redirect
) {
3822 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3823 const string
& protocol
=
3824 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3825 int redirect_code
= 0;
3826 rrule
.apply_rule(protocol
, hostname
, original_object_name
,
3827 &s
->redirect
, &redirect_code
);
3828 // Apply a custom HTTP response code
3829 if (redirect_code
> 0)
3830 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3831 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3832 << " proto+host:" << protocol
<< "://" << hostname
3833 << " -> " << s
->redirect
<< dendl
;
3834 return -ERR_WEBSITE_REDIRECT
;
3835 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3836 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3837 // Do NOT fire the ErrorDoc handler
3838 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3839 /* This serves an entire page!
3840 On success, it will return zero, and no further content should be sent to the socket
3841 On failure, we need the double-error handler
3843 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3844 if (new_err_no
&& new_err_no
!= -1) {
3845 err_no
= new_err_no
;
3848 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3854 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3856 /** If we are in website mode, then it is explicitly impossible to run GET or
3857 * HEAD on the actual directory. We must convert the request to run on the
3858 * suffix object instead!
3860 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3861 op
->set_get_data(get_data
);
3865 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3867 /** If we are in website mode, then it is explicitly impossible to run GET or
3868 * HEAD on the actual directory. We must convert the request to run on the
3869 * suffix object instead!
3871 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3872 op
->set_get_data(get_data
);
3876 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3878 /** If we are in website mode, then it is explicitly impossible to run GET or
3879 * HEAD on the actual directory. We must convert the request to run on the
3880 * suffix object instead!
3882 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3883 op
->set_get_data(get_data
);
3892 static rgw::auth::Completer::cmplptr_t
3893 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3899 AWSEngine::VersionAbstractor::auth_data_t
3900 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3904 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3906 if (version
== AwsVersion::V2
) {
3907 return get_auth_data_v2(s
);
3908 } else if (version
== AwsVersion::V4
) {
3909 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3911 /* FIXME(rzarzynski): handle anon user. */
3916 boost::optional
<std::string
>
3917 AWSGeneralAbstractor::get_v4_canonical_headers(
3918 const req_info
& info
,
3919 const boost::string_view
& signedheaders
,
3920 const bool using_qs
) const
3922 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3926 AWSEngine::VersionAbstractor::auth_data_t
3927 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3928 const bool using_qs
) const
3930 boost::string_view access_key_id
;
3931 boost::string_view signed_hdrs
;
3933 boost::string_view date
;
3934 boost::string_view credential_scope
;
3935 boost::string_view client_signature
;
3936 boost::string_view session_token
;
3938 int ret
= rgw::auth::s3::parse_v4_credentials(s
->info
,
3950 /* craft canonical headers */
3951 boost::optional
<std::string
> canonical_headers
= \
3952 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3953 if (canonical_headers
) {
3954 using sanitize
= rgw::crypt_sanitize::log_content
;
3955 ldout(s
->cct
, 10) << "canonical headers format = "
3956 << sanitize
{*canonical_headers
} << dendl
;
3961 bool is_non_s3_op
= false;
3962 if (s
->op_type
== RGW_STS_GET_SESSION_TOKEN
||
3963 s
->op_type
== RGW_STS_ASSUME_ROLE
||
3964 s
->op_type
== RGW_STS_ASSUME_ROLE_WEB_IDENTITY
) {
3965 is_non_s3_op
= true;
3968 const char* exp_payload_hash
= nullptr;
3969 string payload_hash
;
3971 //For non s3 ops, we need to calculate the payload hash
3972 payload_hash
= s
->info
.args
.get("PayloadHash");
3973 exp_payload_hash
= payload_hash
.c_str();
3975 /* Get the expected hash. */
3976 exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3979 /* Craft canonical URI. Using std::move later so let it be non-const. */
3980 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3982 /* Craft canonical query string. std::moving later so non-const here. */
3983 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3985 /* Craft canonical request. */
3986 auto canonical_req_hash
= \
3987 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3989 std::move(canonical_uri
),
3990 std::move(canonical_qs
),
3991 std::move(*canonical_headers
),
3995 auto string_to_sign
= \
3996 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3997 AWS4_HMAC_SHA256_STR
,
4000 std::move(canonical_req_hash
));
4002 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
4004 std::placeholders::_1
,
4005 std::placeholders::_2
,
4006 std::placeholders::_3
);
4008 /* Requests authenticated with the Query Parameters are treated as unsigned.
4009 * From "Authenticating Requests: Using Query Parameters (AWS Signature
4012 * You don't include a payload hash in the Canonical Request, because
4013 * when you create a presigned URL, you don't know the payload content
4014 * because the URL is used to upload an arbitrary payload. Instead, you
4015 * use a constant string UNSIGNED-PAYLOAD.
4017 * This means we have absolutely no business in spawning completer. Both
4018 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
4019 * by default. We don't need to change that. */
4020 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
) || is_non_s3_op
) {
4025 std::move(string_to_sign
),
4027 null_completer_factory
4030 /* We're going to handle a signed payload. Be aware that even empty HTTP
4031 * body (no payload) requires verification:
4033 * The x-amz-content-sha256 header is required for all AWS Signature
4034 * Version 4 requests. It provides a hash of the request payload. If
4035 * there is no payload, you must provide the hash of an empty string. */
4036 if (!is_v4_payload_streamed(exp_payload_hash
)) {
4037 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
4039 /* payload in a single chunk */
4042 case RGW_OP_CREATE_BUCKET
:
4043 case RGW_OP_PUT_OBJ
:
4044 case RGW_OP_PUT_ACLS
:
4045 case RGW_OP_PUT_CORS
:
4046 case RGW_OP_INIT_MULTIPART
: // in case that Init Multipart uses CHUNK encoding
4047 case RGW_OP_COMPLETE_MULTIPART
:
4048 case RGW_OP_SET_BUCKET_VERSIONING
:
4049 case RGW_OP_DELETE_MULTI_OBJ
:
4050 case RGW_OP_ADMIN_SET_METADATA
:
4051 case RGW_OP_SET_BUCKET_WEBSITE
:
4052 case RGW_OP_PUT_BUCKET_POLICY
:
4053 case RGW_OP_PUT_OBJ_TAGGING
:
4055 case RGW_OP_SET_REQUEST_PAYMENT
:
4058 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
4059 throw -ERR_NOT_IMPLEMENTED
;
4062 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
4064 std::placeholders::_1
);
4069 std::move(string_to_sign
),
4074 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
4075 * it "chunked" but let's be coherent with Amazon's terminology. */
4077 dout(10) << "body content detected in multiple chunks" << dendl
;
4079 /* payload in multiple chunks */
4083 case RGW_OP_PUT_OBJ
:
4086 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
4087 throw -ERR_NOT_IMPLEMENTED
;
4090 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
4092 /* In the case of streamed payload client sets the x-amz-content-sha256
4093 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
4094 * when constructing the Canonical Request. */
4096 /* In the case of single-chunk upload client set the header's value is
4097 * coherent with the one used for Canonical Request crafting. */
4099 /* In the case of query string-based authentication there should be no
4100 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
4102 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
4107 std::placeholders::_1
);
4112 std::move(string_to_sign
),
4121 boost::optional
<std::string
>
4122 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
4123 const req_info
& info
,
4124 const boost::string_view
& signedheaders
,
4125 const bool using_qs
) const
4127 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
4132 AWSEngine::VersionAbstractor::auth_data_t
4133 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
4135 boost::string_view access_key_id
;
4136 boost::string_view signature
;
4137 boost::string_view session_token
;
4140 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
4141 if (! http_auth
|| http_auth
[0] == '\0') {
4142 /* Credentials are provided in query string. We also need to verify
4143 * the "Expires" parameter now. */
4144 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
4145 signature
= s
->info
.args
.get("Signature");
4148 boost::string_view expires
= s
->info
.args
.get("Expires");
4149 if (expires
.empty()) {
4153 /* It looks we have the guarantee that expires is a null-terminated,
4154 * and thus string_view::data() can be safely used. */
4155 const time_t exp
= atoll(expires
.data());
4162 if (s
->info
.args
.exists("X-Amz-Security-Token")) {
4163 session_token
= s
->info
.args
.get("X-Amz-Security-Token");
4164 if (session_token
.size() == 0) {
4170 /* The "Authorization" HTTP header is being used. */
4171 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
4172 const size_t pos
= auth_str
.rfind(':');
4173 if (pos
!= boost::string_view::npos
) {
4174 access_key_id
= auth_str
.substr(0, pos
);
4175 signature
= auth_str
.substr(pos
+ 1);
4178 if (s
->info
.env
->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
4179 session_token
= s
->info
.env
->get("HTTP_X_AMZ_SECURITY_TOKEN");
4180 if (session_token
.size() == 0) {
4186 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
4187 std::string string_to_sign
;
4188 utime_t header_time
;
4189 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
4191 ldout(cct
, 10) << "failed to create the canonized auth header\n"
4192 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
4196 ldout(cct
, 10) << "string_to_sign:\n"
4197 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
4199 if (!qsr
&& !is_time_skew_ok(header_time
)) {
4200 throw -ERR_REQUEST_TIME_SKEWED
;
4204 std::move(access_key_id
),
4205 std::move(signature
),
4206 std::move(session_token
),
4207 std::move(string_to_sign
),
4208 rgw::auth::s3::get_v2_signature
,
4209 null_completer_factory
4214 AWSEngine::VersionAbstractor::auth_data_t
4215 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
4218 s
->auth
.s3_postobj_creds
.access_key
,
4219 s
->auth
.s3_postobj_creds
.signature
,
4220 s
->auth
.s3_postobj_creds
.x_amz_security_token
,
4221 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4222 rgw::auth::s3::get_v2_signature
,
4223 null_completer_factory
4227 AWSEngine::VersionAbstractor::auth_data_t
4228 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
4230 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
4232 /* grab access key id */
4233 const size_t pos
= credential
.find("/");
4234 const boost::string_view access_key_id
= credential
.substr(0, pos
);
4235 dout(10) << "access key id = " << access_key_id
<< dendl
;
4237 /* grab credential scope */
4238 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
4239 dout(10) << "credential scope = " << credential_scope
<< dendl
;
4241 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
4243 std::placeholders::_1
,
4244 std::placeholders::_2
,
4245 std::placeholders::_3
);
4249 s
->auth
.s3_postobj_creds
.signature
,
4250 s
->auth
.s3_postobj_creds
.x_amz_security_token
,
4251 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4253 null_completer_factory
4257 AWSEngine::VersionAbstractor::auth_data_t
4258 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
4260 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
4261 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
4262 << " (AWS4-HMAC-SHA256)" << dendl
;
4263 return get_auth_data_v4(s
);
4265 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
4266 return get_auth_data_v2(s
);
4271 AWSEngine::authenticate(const DoutPrefixProvider
* dpp
, const req_state
* const s
) const
4273 /* Small reminder: an ver_abstractor is allowed to throw! */
4274 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
4276 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
4277 return result_t::deny(-EINVAL
);
4279 return authenticate(dpp
,
4280 auth_data
.access_key_id
,
4281 auth_data
.client_signature
,
4282 auth_data
.session_token
,
4283 auth_data
.string_to_sign
,
4284 auth_data
.signature_factory
,
4285 auth_data
.completer_factory
,
4290 } /* namespace s3 */
4291 } /* namespace auth */
4292 } /* namespace rgw */
4294 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
4295 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
4297 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
4299 if (! cct
->_conf
->rgw_s3_auth_use_ldap
||
4300 cct
->_conf
->rgw_ldap_uri
.empty()) {
4305 std::lock_guard
<std::mutex
> lck(mtx
);
4307 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
4308 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
4309 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
4310 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
4311 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
4312 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
4314 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
4315 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
4323 bool rgw::auth::s3::LDAPEngine::valid() {
4324 std::lock_guard
<std::mutex
> lck(mtx
);
4328 rgw::auth::RemoteApplier::acl_strategy_t
4329 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4331 //This is based on the assumption that the default acl strategy in
4332 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4336 rgw::auth::RemoteApplier::AuthInfo
4337 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
4339 /* The short form of "using" can't be used here -- we're aliasing a class'
4341 using acct_privilege_t
= \
4342 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4344 return rgw::auth::RemoteApplier::AuthInfo
{
4347 RGW_PERM_FULL_CONTROL
,
4348 acct_privilege_t::IS_PLAIN_ACCT
,
4353 rgw::auth::Engine::result_t
4354 rgw::auth::s3::LDAPEngine::authenticate(
4355 const DoutPrefixProvider
* dpp
,
4356 const boost::string_view
& access_key_id
,
4357 const boost::string_view
& signature
,
4358 const boost::string_view
& session_token
,
4359 const string_to_sign_t
& string_to_sign
,
4360 const signature_factory_t
&,
4361 const completer_factory_t
& completer_factory
,
4362 const req_state
* const s
) const
4364 /* boost filters and/or string_ref may throw on invalid input */
4365 rgw::RGWToken base64_token
;
4367 base64_token
= rgw::from_base64(access_key_id
);
4369 base64_token
= std::string("");
4372 if (! base64_token
.valid()) {
4373 return result_t::deny();
4376 //TODO: Uncomment, when we have a migration plan in place.
4377 //Check if a user of type other than 'ldap' is already present, if yes, then
4379 /*RGWUserInfo user_info;
4380 user_info.user_id = base64_token.id;
4381 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4382 if (user_info.type != TYPE_LDAP) {
4383 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4388 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
4389 return result_t::deny();
4392 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4393 get_creds_info(base64_token
));
4394 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4395 } /* rgw::auth::s3::LDAPEngine::authenticate */
4397 void rgw::auth::s3::LDAPEngine::shutdown() {
4405 rgw::auth::Engine::result_t
4406 rgw::auth::s3::LocalEngine::authenticate(
4407 const DoutPrefixProvider
* dpp
,
4408 const boost::string_view
& _access_key_id
,
4409 const boost::string_view
& signature
,
4410 const boost::string_view
& session_token
,
4411 const string_to_sign_t
& string_to_sign
,
4412 const signature_factory_t
& signature_factory
,
4413 const completer_factory_t
& completer_factory
,
4414 const req_state
* const s
) const
4416 /* get the user info */
4417 RGWUserInfo user_info
;
4418 /* TODO(rzarzynski): we need to have string-view taking variant. */
4419 const std::string access_key_id
= _access_key_id
.to_string();
4420 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
4421 ldpp_dout(dpp
, 5) << "error reading user info, uid=" << access_key_id
4422 << " can't authenticate" << dendl
;
4423 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
4425 //TODO: Uncomment, when we have a migration plan in place.
4427 if (s->user->type != TYPE_RGW) {
4428 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << s->user->type
4429 << " is present" << dendl;
4434 const auto iter
= user_info
.access_keys
.find(access_key_id
);
4435 if (iter
== std::end(user_info
.access_keys
)) {
4436 ldpp_dout(dpp
, 0) << "ERROR: access key not encoded in user info" << dendl
;
4437 return result_t::deny(-EPERM
);
4439 const RGWAccessKey
& k
= iter
->second
;
4441 const VersionAbstractor::server_signature_t server_signature
= \
4442 signature_factory(cct
, k
.key
, string_to_sign
);
4443 auto compare
= signature
.compare(server_signature
);
4445 ldpp_dout(dpp
, 15) << "string_to_sign="
4446 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4448 ldpp_dout(dpp
, 15) << "server signature=" << server_signature
<< dendl
;
4449 ldpp_dout(dpp
, 15) << "client signature=" << signature
<< dendl
;
4450 ldpp_dout(dpp
, 15) << "compare=" << compare
<< dendl
;
4453 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4456 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
, boost::none
);
4457 return result_t::grant(std::move(apl
), completer_factory(k
.key
));
4460 rgw::auth::RemoteApplier::AuthInfo
4461 rgw::auth::s3::STSEngine::get_creds_info(const STS::SessionToken
& token
) const noexcept
4463 using acct_privilege_t
= \
4464 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4466 return rgw::auth::RemoteApplier::AuthInfo
{
4470 (token
.is_admin
) ? acct_privilege_t::IS_ADMIN_ACCT
: acct_privilege_t::IS_PLAIN_ACCT
,
4476 rgw::auth::s3::STSEngine::get_session_token(const boost::string_view
& session_token
,
4477 STS::SessionToken
& token
) const
4479 string decodedSessionToken
= rgw::from_base64(session_token
);
4481 auto* cryptohandler
= cct
->get_crypto_handler(CEPH_CRYPTO_AES
);
4482 if (! cryptohandler
) {
4485 string secret_s
= cct
->_conf
->rgw_sts_key
;
4486 buffer::ptr
secret(secret_s
.c_str(), secret_s
.length());
4488 if (ret
= cryptohandler
->validate_secret(secret
); ret
< 0) {
4489 ldout(cct
, 0) << "ERROR: Invalid secret key" << dendl
;
4493 auto* keyhandler
= cryptohandler
->get_key_handler(secret
, error
);
4499 string decrypted_str
;
4500 buffer::list en_input
, dec_output
;
4501 en_input
= buffer::list::static_from_string(decodedSessionToken
);
4503 ret
= keyhandler
->decrypt(en_input
, dec_output
, &error
);
4505 ldout(cct
, 0) << "ERROR: Decryption failed: " << error
<< dendl
;
4509 dec_output
.append('\0');
4510 auto iter
= dec_output
.cbegin();
4511 decode(token
, iter
);
4512 } catch (const buffer::error
& e
) {
4513 ldout(cct
, 0) << "ERROR: decode SessionToken failed: " << error
<< dendl
;
4520 rgw::auth::Engine::result_t
4521 rgw::auth::s3::STSEngine::authenticate(
4522 const DoutPrefixProvider
* dpp
,
4523 const boost::string_view
& _access_key_id
,
4524 const boost::string_view
& signature
,
4525 const boost::string_view
& session_token
,
4526 const string_to_sign_t
& string_to_sign
,
4527 const signature_factory_t
& signature_factory
,
4528 const completer_factory_t
& completer_factory
,
4529 const req_state
* const s
) const
4531 if (! s
->info
.args
.exists("X-Amz-Security-Token") &&
4532 ! s
->info
.env
->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
4533 return result_t::deny();
4536 STS::SessionToken token
;
4537 if (int ret
= get_session_token(session_token
, token
); ret
< 0) {
4538 return result_t::reject(ret
);
4541 //Check if access key is not the same passed in by client
4542 if (token
.access_key_id
!= _access_key_id
) {
4543 ldpp_dout(dpp
, 0) << "Invalid access key" << dendl
;
4544 return result_t::reject(-EPERM
);
4546 //Check if the token has expired
4547 if (! token
.expiration
.empty()) {
4548 std::string expiration
= token
.expiration
;
4549 if (! expiration
.empty()) {
4550 boost::optional
<real_clock::time_point
> exp
= ceph::from_iso_8601(expiration
, false);
4552 real_clock::time_point now
= real_clock::now();
4554 ldpp_dout(dpp
, 0) << "ERROR: Token expired" << dendl
;
4555 return result_t::reject(-EPERM
);
4558 ldpp_dout(dpp
, 0) << "ERROR: Invalid expiration: " << expiration
<< dendl
;
4559 return result_t::reject(-EPERM
);
4563 //Check for signature mismatch
4564 const VersionAbstractor::server_signature_t server_signature
= \
4565 signature_factory(cct
, token
.secret_access_key
, string_to_sign
);
4566 auto compare
= signature
.compare(server_signature
);
4568 ldpp_dout(dpp
, 15) << "string_to_sign="
4569 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4571 ldpp_dout(dpp
, 15) << "server signature=" << server_signature
<< dendl
;
4572 ldpp_dout(dpp
, 15) << "client signature=" << signature
<< dendl
;
4573 ldpp_dout(dpp
, 15) << "compare=" << compare
<< dendl
;
4576 return result_t::reject(-ERR_SIGNATURE_NO_MATCH
);
4579 // Get all the authorization info
4580 RGWUserInfo user_info
;
4582 vector
<string
> role_policies
;
4584 if (! token
.roleId
.empty()) {
4585 RGWRole
role(s
->cct
, store
, token
.roleId
);
4586 if (role
.get_by_id() < 0) {
4587 return result_t::deny(-EPERM
);
4589 vector
<string
> role_policy_names
= role
.get_role_policy_names();
4590 for (auto& policy_name
: role_policy_names
) {
4592 if (int ret
= role
.get_role_policy(policy_name
, perm_policy
); ret
== 0) {
4593 role_policies
.push_back(std::move(perm_policy
));
4596 if (! token
.policy
.empty()) {
4597 role_policies
.push_back(std::move(token
.policy
));
4599 // This is mostly needed to assign the owner of a bucket during its creation
4600 user_id
= token
.user
;
4601 role_name
= role
.get_name();
4604 if (! token
.user
.empty() && token
.acct_type
!= TYPE_ROLE
) {
4606 int ret
= rgw_get_user_info_by_uid(store
, token
.user
, user_info
, NULL
);
4608 ldpp_dout(dpp
, 5) << "ERROR: failed reading user info: uid=" << token
.user
<< dendl
;
4609 return result_t::reject(-EPERM
);
4613 if (token
.acct_type
== TYPE_KEYSTONE
|| token
.acct_type
== TYPE_LDAP
) {
4614 auto apl
= remote_apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4615 get_creds_info(token
));
4616 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4617 } else if (token
.acct_type
== TYPE_ROLE
) {
4618 auto apl
= role_apl_factory
->create_apl_role(cct
, s
, role_name
, user_id
, role_policies
);
4619 return result_t::grant(std::move(apl
), completer_factory(token
.secret_access_key
));
4620 } else { // This is for all local users of type TYPE_RGW or TYPE_NONE
4622 auto apl
= local_apl_factory
->create_apl_local(cct
, s
, user_info
, subuser
, token
.perm_mask
);
4623 return result_t::grant(std::move(apl
), completer_factory(token
.secret_access_key
));
4627 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4630 if (s
->op
== OP_OPTIONS
) {
4636 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
4638 return route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKNOWN
;